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
          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. 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