chk.txt  Utility used to check arguments of commands and functions

==============================================================================

CONTENTS                                                      chk-contents

    1. Intro                                      chk-intro

    2. Functionality provided                     chk-functionality

        2.1. Functions                            chk-functions

        2.2. Checks                               chk-checks

        2.3. Models                               chk-model

        2.4. Transformations                      chk-trans

    3. Example usage                              chk-usage

==============================================================================

1. Intro                                                         chk-intro

This plugin provides the ability to check whether function or command 

arguments match specific model or whether some value matches specific model.

Features:

    ∙ Checks a list of arguments against specified model

    ∙ Checks a single value against specified model

    ∙ Provides a way to specify default values for optional arguments

    ∙ Provides a way to check a single value, convert it, and check again

Plugin requires load plugin to work.

==============================================================================

2. Functionality provided                                chk-functionality

This plugin provides two functions. Functions are accessed via dictionary 

returned by load-func-getfunctions function.

------------------------------------------------------------------------------

2.1. Functions                                               chk-functions

                                                    chk-func-checkargument

checkargument({check}{argument}) :: Check -> a -> Bool

        Checks data {argument} using check {check}. For possible {check} 

        arguments see chk-checks

                                                   chk-func-checkarguments

checkarguments({model}{arguments}) :: Model -> [a] -> Bool

        Checks argument list {arguments} (it must be a single list!) using 

        model {model}. For possible {model} arguments see chk-model.

------------------------------------------------------------------------------

2.2. Checks                                                     chk-checks

All checks are lists of three or two elements. First element is the name of 

a check. Second is an argument of the check. Third is optional and it contains 

the error message displayed when the check fails.

Check name  Check argument and description

                                                              chk-check-in

in          [a] (any list)

            Checks whether {argument} is in list {checkargument}.

                                                           chk-check-regex

regex       Regex (correct regular expression)

            Checks whether {argument} is a string and matches regular 

            expression {checkargument}.

                                                            chk-check-func

func        (a -> Bool) (any function that takes {argument} and

                         returns 1 or 0)

            Checks, whether function being supplied by {argument} as an 

            {argument} returns true. Note that this function must not echo any 

            warnings using :echoerr: they will be captured by :try and 

            check will fail.

                                                            chk-check-type

type        Int (any integer from 0 to 5)

            Checks, whether type of {argument} is {checkargument}.

                                                          chk-check-isfunc

isfunc      Bool (0 or 1)

            Checks, whether {argument} is function reference and it is 

            callable. It is similar to chk-check-type when {checkargument} 

            is 2, but prevents from supplying function references that are not 

            callable (for example, if this reference points to script-local 

            function). If {checkargument} is 1, then check also accepts 

            strings with function names.

                                                            chk-check-bool

bool        _ (argument is ignored)

            Checks, whether {argument} is either 0 or 1.

                                                            chk-check-eval

eval        String (any correct expression)

            Checks, whether eval({checkargument}) is true. Inside this check 

            a:Arg is set to {argument}Note that evaluated expression must 

            not echo any warnings using :echoerr: they will be captured by 

            :try and check will fail.

                                                           chk-check-keyof

keyof       Dictionary

            Checks, whether {argument} is a key of {checkargument}{argument} 

            must be of a type String.

                                                            chk-check-hkey

hkey        String

            Checks, whether {argument} is a Dictionary and has key 

            {checkargument}.

                                                           chk-check-equal

equal       a (everything)

            Checks, whether {argument} is identical to {checkargument}.

                                                             chk-check-var

var         String (either empty or contains comma-delimited variable type

                    names: buffer, window, tabpage, global, vim, option, any)

            Checks, whether {argument} is a variable name, this 

            variable exists and has specified type. (`option' variable names 

            must start with `&', others with `b:', `w:', `t:', `g:' or `v:' 

            for buffer, window, tabpage, global or vim responsively. See 

            internal-variables for details.)

                                                             chk-check-any

any         _ (argument is ignored)

            Always true.

                                                             chk-check-num

num         (Fractional a) => Either (Either a String, a) (a)

                              (list with one or two elements)

            Checks whether {argument} is a number (depending on a type of 

            first number in {checkargument} it must have a type either Integer 

            or any of Integer and Fload) from first element of {checkargument} 

            (if it is not equal to empty string) to the second element of 

            {checkargument} (if it exists).

            Examples: >

                Check               Argument   Result

                ["num", [0]]        1          True

                ["num", [0]]        -1         False

                ["num", [0]]        1.0        False

                ["num", [0.0]]      1.0        True

                ["num", [0.0]]      1          True

                ["num", ["", 0]]    1          False

                ["num", ["", 0]]    -1         True

                ["num", ["", 0]]    -1.0       False

                ["num", [0, 2]]     0          True

                ["num", [0, 2]]     3          False

                ["num", [0, 2]]     -1         False

                ["num", [0, 2.0]]   1.0        False

                ["num", [0.0, 2.0]] 1.0        True

                ["num", [0.0, 2]]   1.0        True

nums         (see above)                                    chk-check-nums

            Just like chk-check-num, but before doing a check it runs 

            eval({argument}).

                                                           chk-check-isreg

isreg       _ (argument is ignored)

            Check whether {argument} is a correct regular expression.

                                                         chk-check-hlgroup

hlgroup     _ (argument is ignored)

            Check whether {argument} is a name of existing highlight group.

                                                            chk-check-file

file        String

            Test, whether {argument} is a filename, which

            {checkargument}  Meaning

                   r         exists and is readable;

                   rw        exists and is writeable;

                   d         exists and is a directory;

                   x         exists and is executable;

                   dw        exists, is a directory and is writeable;

                   w         either exists and is writeable or does not

                             exist, but is in directory where we could 

                             write.

                                                             chk-check-len

len         Either (Integer, Integer) (Integer) (list with one or two

                                                 integers)

            Checks whether {argument} is a list with length from 

            {checkargument}[0] to {checkargument}[1] (or infinity if it is not 

            present). Use regular expressions to test for string length.

                                                          chk-check-chklst

chklst      [ Check ] (list of checks)

            Checks whether {argument} is a list with length equal to length of 

            {checkargument} and every element in {argument} matches Check in 

            the identical position.

                                                          chk-check-optlst

optlst      ([ Check ], [ Check ]) (two lists of checks)

            Checks whether {argument} is a list with length not less then 

            length of {checkargument}[0] and not greater then sum of 

            lengths of {checkargument}[0] and {checkargument}[1], first 

            len({checkargument}[0]) elements of {argument} match checks in 

            {checkargument}[0] and other elements of {argument} match checks 

            in {checkargument}[1].

                                                          chk-check-alllst

alllst      Check

            Checks whether {argument} is a list and every element in 

            {argument} matches {checkargument}.

                                                            chk-check-dict

dict        [(Check, Check)] (list of lists of two checks)

            If {argument} is a dictionary then for every key of {argument} if 

            it matches left Check and value does not match right check return 

            False. Also return False if some key matches none of right checks.

                                                             chk-check-map

map         ({CheckName}, [ {CheckArgument} ])

            For every {CheckArgument} in list {checkargument}[1] check whether 

            {argument} matches check [{CheckName}{CheckArgument}].

                                                         chk-check-allorno

allorno     [ Check ] (list of checks)

            Check succeeds either if {argument} matches all Checks in 

            {checkargument} or none of Checks in {checkargument}.

                                                             chk-check-not

not         Check

            Check, whether {argument} does not match {checkargument}.

                                                              chk-check-or

or          [ Check ] (list of checks)

            Check, whether {argument} matches any of checks in 

            {checkargument}.

                                                             chk-check-and

and         [ Check ] (list of checks)

            Checks, whether {argument} matches all of checks in 

            {checkargument}.

-----------------------------------------------------------------------------

2.3. Models                                                      chk-model

{model} is a dictionary with required key "model" and some other keys, which 

depend on "model".

Model     Description

                                                          chk-model-simple

simple    Required keys: "required" :: [ ArgTrans ] (list of transformations)

          Meaning: there must be exactly len("required") arguments

                   in {arguments}, all must be successfully 

                   transformed by appropriate ArgTrans.

          Examples:

              If we need to check arguments to the pow() function: >

              pow(Float, Float) -> Float

                  {    "model": "simple",

                    "required": [["or", ["type", type(0.0)],

                                        ["type", type(0)]],

                                 ["or", ["type", type(0.0)],

                                        ["type", type(0)]]] }

<

                                                        chk-model-optional

optional  Required keys: no

          Optional keys: "required" :: [ ArgTrans ]

                         "optional" :: [(ArgTrans, ArgTrans, a)]

                              (list of lists of three elements: transformation 

                              for present argument, transformation for default 

                              value and default value)

                         "next" :: ArgTrans

          Meaning: there must be at least len("required") arguments (or 0 if

                   it is not present) and at most len("optional") optional 

                   arguments (unless "next" key is present). If some optional 

                   argument is present it is transformed by first ArgTrans. If 

                   it is not present, then third value in list is transformed 

                   using the second ArgTrans. After all optional arguments 

                   were processed, all other arguments are transformed using 

                   ArgTrans from "next" key. If there is no "next" key and 

                   number of arguments is greater then number of required plus 

                   number of optional arguments, then 0 is returned, 

                   indicating that an error occured.

          Example:

              If we need to check arguments to the matchstr() function: >

              matchstr(String, Regex[, UInteger[, UInteger]])

                  {   "model": "optional",

                   "required": [["type", type("")],

                                ["isreg", ""]],

                   "optional": [[["num", [0]], {}, 0],

                                [["num", [0]], {}, 0]] }

<

                                                        chk-model-prefixed

prefixed  Required keys: no

          Optional keys: "required" :: [ ArgTrans ]

                         "optional" :: [(ArgTrans, ArgTrans, a)]

                         "prefrequired" :: {prefix: ArgTrans}

                              (dictionary with values identical to 

                              "required" values)

                         "prefoptional" :: {prefix: (ArgTrans,

                                                     ArgTrans, a)}

                              (dictionary with values identical to 

                              "optional" values)

                         "preflist" :: [ String ] (list of

                                                   strings)

                         "allowtrun" :: Bool

                         "altpref" :: [ prefix ] (list of strings)

          Meaning:

              Command must look like that: >

                   Command [required_arguments]

                         \ [optional_arguments]

                         \ [{prefix} {argument}]

                         \ [{prefixFromPreflist} [arguments]]

<

              Here [required_arguments] are described in "required" key and 

              are handled just like in chk-model-simple

              [optional_arguments] are described in "optional" key and are 

              handled just like in chk-model-optional, but with one 

              difference: [optional_arguments] must contain none of the keys 

              of "prefrequired" and "prefoptional" dictionaries. Then for all 

              keys from "prefrequired" and "prefoptional" not listed in 

              "preflist" if in the arguments list there is a sequence [{key}

              {value}], then extend the last entry in arguments list (it will 

              always be a dictionary) with { {key}{value} } pair. Prefixes 

              listed in "altpref" does not require any values, though they 

              make take one if they are present in "prefrequired" or 

              "prefoptional" as well, so if `prefix' is in "altpref" and in 

              "prefrequired", then either of `prefix' (=`prefix 1'), 

              `noprefix' (=`prefix 0', default) and `prefix {value}' are 

              possible. If some key from "prefrequired" or "prefoptional" is 

              listed in "preflist" then all arguments starting with one equal 

              to this key and ending with one of the keys from "prefrequired" 

              or "prefoptional" are added to the list. Last entry in arguments 

              list will be extended with { {key}{list} } pair then. Key 

              "allowtrun" denies or allows (default: allow) reducing prefixes. 

              For example, if there are prefixes “columns”, “count” and 

              “print” then if reducing is allowed “columns” may be reduced to 

              “col”, “count” to “cou” and “print” to “p”. Presence of either 

              "optional" or "preflist" keys denies reducing.

              Examples: >

                  {"model": "prefixed",

                   "required": [{}],

                   "optional": [[{}, {}, "def"]],

                   "prefrequired": {"for": {}, "in": {}},

                   "prefoptional": {"using": [{}, {}, "defU",

                                    "list": [{}, {}, ["defL"]]},

                   "preflist": ["in", "list"]}

                  Cmdline => Result:

                  required optional for F in I using U list L =>

                            ["required", "optional", {"for": "F",

                                                      "in": ["I"],

                                                      "using": "U",

                                                      "list": ["L"]}]

                  required for F in I using U list L =>

                            ["required", "def", {"for": "F",

                                                 "in": ["I"],

                                                 "using": "U",

                                                 "list": ["L"]}]

                  required four for F in I using U list L =>

                            ["required", "four", {"for": "F",

                                                  "in": ["I"],

                                                  "using": "U",

                                                  "list": ["L"]}]

                  required for F in I1 I2  =>

                            ["required", "def", {"for": "F",

                                                 "in": ["I1", "I2"],

                                                 "using": "defU",

                                                 "list": ["defL"]}]

                  required for F in I1 I2 list L1 L2 =>

                            ["required", "def", {"for": "F",

                                                 "in": ["I1", "I2"],

                                                 "using": "U",

                                                 "list": ["L1", "L2"]}]

<

                                                         chk-model-actions

actions   Required keys: "actions" :: {action: Model}

          Optional keys: "allowtrun" :: Bool

          Meaning: first argument must be one of keys from "actions"

                   dictionary. Other arguments must be valid models. Key 

                   "allowtrun" denies or allows (default: allow) reducing 

                   action names. For example, if there are actions “start”, 

                   “stop” and “restart” then if reducing is allowed “start” 

                   may be reduced to “sta”, “stop” to “sto” and “restart” to 

                   “r”.

                                                          chk-model-aslist

aslist    Required keys: "check" :: ArgTrans

          Meaning: check argument list as a single argument

------------------------------------------------------------------------------

2.4. Transformations                                             chk-trans

Every ArgTrans is either a dictionary or a Check. If it is a Check, then 

argument is not transformed, only checked. If it is a dictionary it may 

contain the following keys:

Key     Value and description

check   Check

        Check the argument.

trans   Transformation

        Transform the argument using transformation. Every transformation is 

        a list of two elemnts: name of the transformation and argument. 

        Possible transformations:

        Name    {transargument}

        eq      a (any value)

                Return 1 if {argument} is identical to {transargument} and 

                0 otherwise.

        func    Function (a -> b) (function that takes one argument)

                Pass the argument to the function and use the result.

        eval    String (expression)

                Eval the {transargument} and take the result. Inside the 

                expression a:Arg is {argument} and a:Trans is 

                {transargument}.

        earg    _ (argument is ignored)

                Eval the being transformed argument and take the result. Note 

                that {transargument} is still available via a:Trans 

                variable.

        call    [{functionarguments}] (list of function arguments)

                Take the result of call({argument}{transargument}, {}).

        pipe    [ Transformation ] (list of transformations)

                Take the result of n'th transformation and transform it using 

                the (n+1)'th transformation.

transchk  Check

        Check the result of transformation.

skip    _ (value is ignored)

        Do not add {argument} to the arguments list.

==============================================================================

3. Example usage                                                 chk-usage

Pretend that you want to create a function that will echo message with changed 

highlighting and want to throw an exception if given higlight group does not 

exist: >

    " Tests, whether given highlight group exists

    function HighlightExists(hlname)

        try

            silent execute "highlight ".a:name

            return 1

        catch

            return 0

        endtry

    endfunction

    " Get a dictionary with this plugins' functions

    let s:chkdict=load#LoadFuncdict().getfunctions("chk")

    function EchoHighlighted(hlname, text)

        " Test, whether given ``hlname'' is a valid name for a highlight group 

        " and this highlight group exists.

        if !s:chkdict.checkargument(["and", [["regex", '^[a-zA-Z0-9_]\+$', "Invalid name for highlight group"],

                                            \["func", function("HighlightExists"), "Highlight group does not exist"]]],

                           \a:hlname)

            throw "Invalid hlname."

        endif

        execute "echohl ".a:hlname

        echo a:text

        echohl None

    endfunction

Now, if you do >

    call EchoHighlighted("Comment", "This message will be highlighted like a comment")

you will get highlighted message, but these calls will throw an error: >

    call EchoHighlighted("Invalid Name", "This will throw an error")

    " output:

    " chk/achk.regex:InvalidValue(Value does not match regular expression /'^[a-zA-Z0-9_]\+$'/: 'Invalid Name')

    " chk/achk._main:InvalidValue(Invalid name for highlight group)

    " chk/achk._main:InvalidValue(Invalid value)

    " Error detected while processing function EchoHighlighted:

    " line    6:

    " E605: Exception not caught: Invalid hlname.

    call EchoHighlighted("NonExistantGroup", "This will throw an error too")

    " output:

    " chk/achk.func:InvalidValue(Function function('HighlightExists') returned a error)

    " chk/achk._main:InvalidValue(Highlight group does not exist)

    " chk/achk._main:InvalidValue(Invalid value)

    " Error detected while processing function EchoHighlighted:

    " line    6:

    " E605: Exception not caught: Invalid hlname.

Here is the same example, rewritten to use checkarguments function and new 

`hlgroup' check: >

    " Get a dictionary with this plugins' functions

    let s:chkdict=load#LoadFuncdict().getfunctions("chk")

    function EchoHighlighted(...)

        " Test, whether given ``hlname'' is a valid name for a highlight group 

        " and this highlight group exists.

        let args=s:chkdict.checkarguments({"model": "simple",

                \"required": [["hlgroup", ""],

                             \["any", ""]],}

                           \a:000)

        if type(args)!=type([])

            throw "Invalid arguments."

        endif

        execute "echohl ".args[0]

        echo args[1]

        echohl None

    endfunction

vim: ft=help:tw=78:nowrap