Giter VIP home page Giter VIP logo

argcheck's Introduction

argcheck

A powerful function argument checker and function overloading system for Lua or LuaJIT.

argcheck generates specific code for checking arguments of a function. This allows complex argument checking (possibly with optional values), with little overhead (with LuaJIT). argcheck computes a tree of all possible variants of arguments, allowing efficient overloading and default argument management.

Installation

The easiest is to use luarocks.

If you use Torch, simply do

luarocks install argcheck

else

luarocks build https://raw.github.com/torch/argcheck/master/rocks/argcheck-scm-1.rockspec

You can also copy the argcheck directory where luajit (or lua) will find it.

Changelog

  • Version 2.0 (git)

    • Rewrote completely the code generation.
    • Now creates a tree of possible argument paths (much more efficient).
    • Thanks to the tree approach, many bugs have been fixed.
    • argcheck will produce an error if there are ambiguous argument rules.
    • The feature chain is still deprecated (but available). Use overload instead.
    • True overloading is happening. Contrary to chain, overload functions must be overwritten.
    • Same functionalities than in 1.0, plus
      • Handles named method calls
      • Can generate a dot graphviz file of the argument paths for debugging purposes.
  • Version 1.0

    • Simplified the way of calling argcheck.
    • Same functionalities than before, except named method call were not handled anymore.
    • Added the call option to call a function if the arguments match given rules.
    • Added the chain option to chain several argcheck function calls (a cheap version of overloading).
    • Simplified the way of adding a type.
  • Version 0.5

    • Handling of default arguments (including defaulting to another argument), optional arguments (which might be nil), named arguments, named only option.
    • Complicated way to handle method and functions.
    • Calling function is mandatory.

Documentation

To use argcheck, you have to first require it:

local argcheck = require 'argcheck'

In the following, we assume this has been done in your script. Note that argcheck does not import anything globally, to avoid cluttering the global namespace. The value returned by the require is a function: for most usages, it will be the only thing you need.

Note that in the following examples we do not use local variables for check functions or example functions. This is bad practice, but helpful if you want to cut-and-paste the code in your interactive lua to see how this is running.

The argcheck() function creates a fast pre-compiled function for checking arguments, according to rules provided by the user. Assume you have a function which requires a unique number argument:

function addfive(x)
  print(string.format('%f + 5 = %f', x, x + 5))
end

You can make sure everything goes fine by creating the rule:

check = argcheck{
   {name="x", type="number"}
}

function addfive(...)
   local x = check(...)
   print(string.format('%f + 5 = %f', x, x + 5))
end

If a user try to pass a wrong argument, too many arguments, or no arguments at all, argcheck will complain:

arguments:
{
  x = number  --
}

stdin:2: invalid arguments

A rule must at least take a name field. The type field is optional (even though it is highly recommended!). If type is not provided, argcheck will make sure the given argument is not nil. If you want also to accept nil arguments, see the opt option.

Default arguments

Arguments can have defaults:

check = argcheck{
   {name="x", type="number", default=0}
}

In which case, if the argument is missing, argcheck will pass the default one to your function:

> addfive()
0.000000 + 5 = 5.000000

Help (or doc)

argcheck encourages you to add help to your function. You can document each argument:

check = argcheck{
   {name="x", type="number", default=0, help="the age of the captain"}
}

Or even document the function:

check = argcheck{
   help=[[
This function is going to do a simple addition.
Give a number, it adds 5. Amazing.
]],
   {name="x", type="number", default=0, help="the age of the captain"}
}

Then, if the user makes a mistake in the arguments, the error message becomes more clear:

> addfive('')
stdin:2: invalid arguments

This function is going to do a simple addition.
Give a number, it adds 5. Amazing.

arguments:
{
   [x = number]  -- the age of the captain [default=0]
}

Note that is (equivalently) possible to use the key doc= instead of help=.

Multiple arguments

Until now, our function had only one argument. Obviously, argcheck can handle as many as you wish:

check = argcheck{
   help=[[
This function is going to do a simple addition.
Give a number, it adds 5. Amazing.
]],
   {name="x", type="number", default=0, help="the age of the captain"},
   {name="msg", type="string", help="a message"}
}

function addfive(...)
  local x, msg = check(...)
  print(string.format('%f + 5 = %f', x, x + 5))
  print(msg)
end

argcheck handles well various cases, including those where some arguments with defaults values might be missing:

> addfive(4, 'hello world')
4.000000 + 5 = 9.000000
hello world
>
> addfive('hello world')
0.000000 + 5 = 5.000000
hello world
>
> addfive(4)

stdin:2: invalid arguments

This function is going to do a simple addition.
Give a number, it adds 5. Amazing.

arguments:
{
  [x   = number]  -- the age of the captain [default=0]
   msg = string   -- a message
}

Default argument defaulting to another argument

Arguments can have a default value coming from another argument, with the defaulta option. In the following

check = argcheck{
  {name="x", type="number"},
  {name="y", type="number", defaulta="x"}
}

function mul(...)
   local x, y = check(...)
   print(string.format('%f x %f = %f', x, y, x * y))
end

argument y will take the value of x if it is not passed during the function call:

> mul(3, 4)
3.000000 x 4.000000 = 12.000000
> mul(3)
3.000000 x 3.000000 = 9.000000

Default arguments function

In some more complex cases, sometimes one needs to run a particular function when the given argument is not provided. The option defaultf is here to help.

idx = 0

check = argcheck{
   {name="x", type="number"},
   {name="y", type="number", defaultf=function() idx = idx + 1 return idx end}
}

function mul(...)
   local x, y = check(...)
   print(string.format('%f x %f = %f', x, y, x * y))
end

This will output the following:

> mul(3)
3.000000 x 1.000000 = 3.000000
> mul(3)
3.000000 x 2.000000 = 6.000000
> mul(3)
3.000000 x 3.000000 = 9.000000
### Optional arguments

Arguments with a default value can be seen as optional. However, as they do have a default value, the underlying function will never receive a nil value. In some situations, one might need to declare an optional argument with no default value. You can do this with the opt option.

check = argcheck{
  {name="x", type="number", default=0, help="the age of the captain"},
  {name="msg", type="string", help="a message", opt=true}
}

function addfive(...)
   local x, msg = check(...)
   print(string.format('%f + 5 = %f', x, x + 5))
   print(msg)
end

In this example, one might call addfive() without the msg argument. Of course, the underlying function must be able to handle nil values:

> addfive('hello world')
0.000000 + 5 = 5.000000
hello world
> addfive()
0.000000 + 5 = 5.000000
nil

argcheck supports Torch Tensors type checks. Specific tensor types like Int, Float, or Double can be checked with torch.<Type>Tensor. Any tensor type can be checked with torch.*Tensor.

check = argcheck{
  {name="anyTensor", type="torch.*Tensor"},
  {name="fTensor", type="torch.FloatTensor"}
}

check(torch.IntTensor(), torch.FloatTensor()) -- Good.
check(torch.FloatTensor(), torch.FloatTensor()) -- Good.
check(torch.FloatTensor(), torch.IntTensor()) -- Invalid.

Specific per-rule check

It is possible to add an extra specific checking function for a given checking rule, with the check option. This function will be called (with the corresponding argument) in addition to the standard type checking. This can be useful for refined argument selection:

check = argcheck{
  {name="x", type="number", help="a number between one and ten",
    check=function(x)
            return x >= 1 and x <= 10
          end}
}

function addfive(...)
   local x = check(...)
   print(string.format('%f + 5 = %f', x, x + 5))
end

> addfive(3)
3.000000 + 5 = 8.000000

> addfive(11)
stdin:2: invalid arguments

arguments:
{
   x = number  -- a number between one and ten
}

Named arguments

argcheck handles named argument calls. Following the previous example, both

addfive(1, "hello world")

and

addfive{x=1, msg="hello world"}

are valid. However, ordered arguments are handled in a much faster way (especially with LuaJIT) than named arguments.

Method named arguments

The common way to define a "method" in Lua is by doing the following:

local object = {}

function object:foobar(x, msg) -- a method foobar
end

The syntax sugar call object:foobar(x, msg) is equivalent to the function call

object.foobar(object, x, msg)

Calling a method with named arguments would be done with

object:foobar{x=..., msg=...}

(where ... is the actual content of x and msg). This translates to foobar(object, {x=..., msg=...}), which is not a regular named function call, given that the object itself should not be treated as a named argument. argcheck will handle such calls, provided the name of the object argument is self, in the rule definition. For e.g.:

local object = {checksum=1234567} -- the object is just a table here
local check = argcheck{
   {name="self", type="table"}, -- check the type of self
   {name="x", type="number"},
   {name="msg", type="string", default="i know what i am doing"},
}

function object.foobar(...) -- note the '.', given we type-check self too
   local self, x, msg = check(...)
   print(string.format('%f + 5 = %f [msg = %s] [self.checksum=%s]', x, x + 5, msg, self.checksum))
end

-- method ordered arguments call
> object:foobar(5, 'hello world')
5.000000 + 5 = 10.000000 [msg = hello world] [self.checksum=1234567]

-- method named arguments call (works too!)
> object:foobar{x=5, msg='hello world'}
5.000000 + 5 = 10.000000 [msg = hello world] [self.checksum=1234567]

-- default argument (and other things) work the same than previously
> object:foobar(7)
7.000000 + 5 = 12.000000 [msg = i know what i am doing] [self.checksum=1234567]

> object:foobar{x=7}
7.000000 + 5 = 12.000000 [msg = i know what i am doing] [self.checksum=1234567]

Note: argcheck assumes the function defined by a set of rules is in fact a method, if the name of the first rule is self.

Options global to all rules

argcheck has several interesting global options, as the help (or doc) we have introduced already. Those global options are simply set in the main argcheck table:

check = argcheck{
   help = "blah blah", -- global help option
...
}

Other global options are described in the following.

Function call

An important feature of argcheck is its ability to call a function if the passed arguments match the defined rules.

Taking back the first example, one could use the call option and rewrite it as:

addfive = argcheck{
   {name="x", type="number"},

   call = function(x)
            print(string.format('%f + 5 = %f', x, x + 5))
          end
}

> addfive(5)
5.000000 + 5 = 10.000000

> addfive()
stdin:1: arguments:
{
   x = number  --
}

As we will see below, argcheck can also handle function overloading, and other complex situations, in which the call feature can simplify the programmer's life. In that respect, it is highly encouraged to use this feature.

Pack arguments into a table

In some cases, it might be interesting to get all arguments into a table. This is not recommended in general, as creating a table slows down the checking process. However, when one uses a lot of arguments, the pack option might be of interest. The function created by argcheck then returns a table containing all arguments with rule names as keys.

check = argcheck{
   pack=true,
   {name="x", type="number", default=0, help="the age of the captain"},
   {name="msg", type="string", help="a message"}
}

function addfive(...)
   local args = check(...) -- now arguments are stored in this table
   print(string.format('%f + 5 = %f', args.x, args.x+5))
   print(args.msg)
end

> addfive(5, 'hello world')
5.000000 + 5 = 10.000000
hello world

Restrict to named-only or ordered-only arguments

In some very special (rare) cases, one might want to disable named calls like addfive{x=1, msg='blah'}, and stick to only ordered arguments like addfive(1, 'blah'), or vice-versa. That might be to handle some ambiguous calls, e.g. when one has to deal with table arguments. The options nonamed and noordered can be used for that purpose:

check = argcheck{
   nonamed=true,
   {name="x", type="number", default=0, help="the age of the captain"},
   {name="msg", type="string", help="a message"}
}

function addfive(...)
   local x, msg = check(...)
   print(string.format('%f + 5 = %f', x, x+5))
   print(msg)
end

> addfive('blah')
0.000000 + 5 = 5.000000
blah

> addfive{msg='blah'}
stdin:2: invalid arguments

arguments:
{
   [x   = number]  -- the age of the captain [default=0]
   msg = string   -- a message
}

Quiet

If you want to handle errors yourself, you might want to make sure the checking function is quiet. The quiet=true option is here for this. If mentioned, the argument checker will return a boolean (true in case of success, false if arguments do not match rules), followed by the arguments (possibly packed). In case of failure false is followed by the help message.

check = argcheck{
   quiet=true,
   {name="x", type="number", default=0, help="the age of the captain"},
   {name="msg", type="string", help="a message"}
}

> print(check(5, 'hello world'))
true             5      hello world

> print(check(5))
false   arguments:
{
  [x   = number]  -- the age of the captain [default=0]
   msg = string   -- a message
}

Overloading

It is possible to overload previous created argchecks manually. E.g., in our example, if we want addfive() to handle the case of a number or string argument, one could leverage the quiet global option and do the following:

checknum = argcheck{
   quiet=true,
   {name="x", type="number"}
}

checkstr = argcheck{
   quiet=true,
   {name="str", type="string"}
}

function addfive(...)

  -- first case
  local status, x = checknum(...)
  if status then
    print(string.format('%f + 5 = %f', x, x + 5))
    return
  end

  -- second case
  local status, str = checkstr(...)
  if status then
    print(string.format('%s .. 5 = %s', str, str .. '5'))
    return
  end

  -- note that in case of failure with quiet, the error is returned after the status
  print('usage:\n\n' .. x .. '\n\nor\n\n' .. str)
  error('invalid arguments')
end

> addfive(123)
123.000000 + 5 = 128.000000

> addfive('hi')
hi .. 5 = hi5

> addfive()
usage:

arguments:
{
   x = number  --
}

or

arguments:
{
   str = string  --
}
stdin:19: invalid arguments

This can however quickly become a burden, if there are many possible argument variations. Instead, one can use the overload option, which is supposed to be used together with call. The value provided to overload must be a function previously created by argcheck.

If the arguments do not match any given variations, then the created argument checker will show a global error message, with usage summarizing all possibilites.

When overloading, argcheck will create a new function (for efficiency reasons) including all possible cases which are being overloaded, as well as the new given case. Beware to overwrite the returned argcheck function each time you overload one!

The previous example is then equivalent to:

addfive = argcheck{
  {name="x", type="number"},
  call = function(x) -- called in case of success
           print(string.format('%f + 5 = %f', x, x + 5))
         end
}

addfive = argcheck{ -- overwrite it
  {name="str", type="string"},
  overload = addfive, -- overload the previous one
  call = function(str) -- called in case of success
           print(string.format('%s .. 5 = %s', str, str .. '5'))
         end
}

th> addfive(5)
5.000000 + 5 = 10.000000

th> addfive('hi')
hi .. 5 = hi5

th> addfive()
stdin:1: arguments:
{
   x = number  --
}

or

arguments:
{
   str = string  --
}

Force

argcheck hates ambiguities, and will spit out an error message if you try to create some rules which are ambiguous. This can in fact happen easily when overloading, or when mixing named/ordered arguments.

For example:

addfive = argcheck{
   {name="x", type="number"},
   call =
      function(x) -- called in case of success
         print(string.format('%f + 5 = %f', x, x + 5))
      end
}

addfive = argcheck{
   {name="x", type="number"},
   {name="msg", type="string", default="i know what i am doing"},
   overload = addfive,
   call =
      function(x, msg) -- called in case of success
         print(string.format('%f + 5 = %f [msg = %s]', x, x + 5, msg))
      end
}

will led to the error message "argcheck rules led to ambiguous situations". One can override this behavior, with the force flag:

addfive = argcheck{
   {name="x", type="number"},
   {name="msg", type="string", default="i know what i am doing"},
   overload = addfive,
   force = true,
   call =
      function(x, msg) -- called in case of success
         print(string.format('%f + 5 = %f [msg = %s]', x, x+5, msg))
      end
}

In this case, consider the subsequent calls:

> addfive(5, 'hello')
5.000000 + 5 = 10.000000 [msg = hello]
> addfive(5)
5.000000 + 5 = 10.000000 [msg = i know what i am doing]

Note that the first function is then never called (you know what you are doing!).

Debug

Adding debug=true as global option will simply dump in stdout the corresponding code for the given checking argument function. It will also return a dot graph, for better understanding of what is going on.

check, dotgraph = argcheck{
   debug=true,
   {name="x", type="number", default=0, help="the age of the captain"},
   {name="msg", type="string", help="a message"}
}

local arg0403e9b0_1d
local istype
local graph
return function(...)
   local narg = select("#", ...)
   if narg >= 1 and istype(select(1, ...), "number") then
      if narg >= 2 and istype(select(2, ...), "string") then
         if narg == 2 then
            local arg1 = select(1, ...)
            local arg2 = select(2, ...)
            return arg1, arg2
         end
      end
   end
   if narg >= 1 and istype(select(1, ...), "string") then
      if narg == 1 then
         local arg2 = select(1, ...)
         local arg1 = arg0403e9b0_1d
         return arg1, arg2
      end
   end
   if narg == 1 and istype(select(1, ...), "table") then
      local args = select(1, ...)
      local narg = 0
      for k,v in pairs(args) do
         narg = narg + 1
      end
      if narg >= 1 and istype(args.x, "number") then
         if narg >= 2 and istype(args.msg, "string") then
            if narg == 2 then
               local arg1 = args.x
               local arg2 = args.msg
               return arg1, arg2
            end
         end
      end
      if narg >= 1 and istype(args.msg, "string") then
         if narg == 1 then
            local arg2 = args.msg
            local arg1 = arg0403e9b0_1d
            return arg1, arg2
         end
      end
   end
   assert(graph)
   error(string.format("%s\ninvalid arguments!", graph:usage()))
end

> print(dotgraph)
digraph ACN {
edge [penwidth=.3 arrowsize=0.8];
id0403efc0 [label="@" penwidth=.1 fontsize=10 style=filled fillcolor="#eeeeee"];
edge [penwidth=.3 arrowsize=0.8];
id0403f460 [label="number" penwidth=.1 fontsize=10 style=filled fillcolor="#eeeeee"];
edge [penwidth=.3 arrowsize=0.8];
id0403f530 [label="string" penwidth=.1 fontsize=10 style=filled fillcolor="#aaaaaa"];
id0403f460 -> id0403f530;
id0403efc0 -> id0403f460;
edge [penwidth=.3 arrowsize=0.8];
id0403f7b8 [label="table" penwidth=.1 fontsize=10 style=filled fillcolor="#eeeeee"];
edge [penwidth=.3 arrowsize=0.8];
id0403f618 [label="number (x)" penwidth=.1 fontsize=10 style=filled fillcolor="#eeeeee"];
edge [penwidth=.3 arrowsize=0.8];
id04040068 [label="string (msg)" penwidth=.1 fontsize=10 style=filled fillcolor="#aaaaaa"];
id0403f618 -> id04040068;
id0403f7b8 -> id0403f618;
edge [penwidth=.3 arrowsize=0.8];
id040408b0 [label="string (msg)" penwidth=.1 fontsize=10 style=filled fillcolor="#aaaaaa"];
id0403f7b8 -> id040408b0;
id0403efc0 -> id0403f7b8;
edge [penwidth=.3 arrowsize=0.8];
id04040390 [label="string" penwidth=.1 fontsize=10 style=filled fillcolor="#aaaaaa"];
id0403efc0 -> id04040390;
}

As you can see, for a simple example like this one, the code is already not that trivial, but handles both named and ordered arguments. Generating an image out of the graph with dot (e.g. with dot -Tpng), leads to the following:

Nodes with (...) are nodes corresponding to named arguments. Dark gray nodes represent valid paths in the graph. Node with a * suffix (after the type name) are nodes which might be self first argument of a method (not present in the shown example).

Advanced usage

By default, argcheck uses the standard type() Lua function to determine the type of your arguments. In some cases, like if you are handling your own class system, you might want to specify how to check types. This can be simply done by overriding the istype() function available in the argcheck environment.

env = require 'argcheck.env' -- retrieve argcheck environement

-- this is the default type function
-- which can be overrided by the user
function env.istype(obj, typename)
   return type(obj) == typename
end

Note that if you change the istype() function, it will not affect previously defined argument checking functions: istype() is passed as an upvalue for each created argument function.

Real-life example

See our cairo FFI interface, which leverages argcheck.

Benchmark

See the argcheck benchmark page for detailed performance report.

argcheck's People

Contributors

andresy avatar atcold avatar jbarbero avatar jgehring avatar soumith avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

argcheck's Issues

Missing argument description when help main field is provided

In the documentation we have

> addfive('')
stdin:2: invalid arguments

This function is going to do a simple addition.
Give a number, it adds 5. Amazing.

arguments:
{
   [x = number]  -- the age of the captain [default=0]
}

in my terminal I get

th> addfive('')
[string "argcheck"]:49: 
This function is going to do a simple addition. Give a number, it adds 5.
Amazing.

Got: string
invalid arguments!
stack traceback:
        [C]: in function 'error'
        [string "argcheck"]:49: in function 'check'
        [string "function addfive(...)..."]:2: in function 'addfive'
        [string "_RESULT={addfive('')}"]:1: in main chunk
        [C]: in function 'xpcall'
        /Users/atcold/torch/install/share/lua/5.1/trepl/init.lua:651: in function 'repl'
        ...cold/torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th:199: in main chunk
        [C]: at 0x010ca74630
                                                                      [0.0009s]
  1. So, the last part, with the argument explanation, is missing.
  2. Moreover, it looks like the \n is ignored.

Also ? addfive does not show the documentation. How do I get to see it?

In the same way, for the multi argument case, on the documentation it's shown that one may expect an explanation like the following

arguments:
{
  [x   = number]  -- the age of the captain [default=0]
   msg = string   -- a message
}

where one knows that msg does not have any default value, but what I get instead is

Got: number
invalid arguments!
stack traceback:
        [C]: in function 'error'
        [string "argcheck"]:74: in function 'check'
        [string "function addfive(...)..."]:2: in function 'addfive'
        [string "_RESULT={addfive(4)}"]:1: in main chunk
        [C]: in function 'xpcall'
        /Users/atcold/torch/install/share/lua/5.1/trepl/init.lua:651: in function 'repl'
        ...cold/torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th:199: in main chunk
        [C]: at 0x010ca74630

Tables as first argument introduces ambiguity - proposal: table wrappers classes

I'm struggling to grasp how to approach argument that can either be a string or a table for the __init function. I figured that if I go with ordered arguments the argcheck should just use the env.istype and let me worry about the implementation details. Unfortunately nothing seems to happen when using the nonamed argument, here's an example:

local argcheck = require 'argcheck'

test = torch.class('test')
env = require 'argcheck.env' -- retrieve argcheck environement
env.istype = function(obj, typename)
  if (typename == "table|string") then
    return torch.type(obj) == "table" or
      torch.type(obj) == "string"
  end
  if (typename == "torch.*Tensor") then
    -- regular expressions don't work therefore this
    return torch.type(obj) == "torch.IntTensor" or
      torch.type(obj) == "torch.FloatTensor" or
      torch.type(obj) == "torch.DoubleTensor"
  end
  return torch.type(obj) == typename
end

test.__init = argcheck{
  nonamed=true,
  {name="self", type = "test"},
  {name="tbl", type = "table|string", default=false},
  call = function(self, tbl) -- called in case of success
    print(tbl)
   end
}

print("-- No arguments")
a = test.new()

print("\n-- String")
a = test.new("test")

print("\n-- Table")
a = test.new({1,2,3})

print("\n-- Should throw error")
a = test.new{tbl={1,2,3}}

Outputs:

-- No arguments 
false   

-- String   
test    

-- Table    
{
  1 : 1
  2 : 2
  3 : 3
}

-- Should throw error   
{
  tbl : 
    {
      1 : 1
      2 : 2
      3 : 3
    }
}

The documentation is a little thin on classes, if I'm missing something please consider extending the docs a little so that this awesome package gets easier to implement.

Bug with tables?

This fails, with a weird nested table:

local opt = require('argcheck') {
   pack = true,
   {name = 'a', type = 'number', default = 10},
   {name = 'b', type = 'table', default = {100,2}},
   {name = 'c', type = 'boolean', default = false},
}

print( opt({a= 10, b= {1,2}}) )

Normal?

Fails when default and no type

This fails with an assertion error:

local testCheck = require('argcheck'){
   {name="formats", default=10, help="authorized formats"},
}

Am I missing something?

bugs wrt order of arguments and defaults

I found two bugs. Both can be reproduced with this gist:
https://gist.github.com/c10bc2ad64e5facccf7d

Bug 1:
If you run the gist as is, you will see that "split" is given the value of 90 (which is default), even though it is sent split=15 in the constructor.
I noticed that if you interchange lines 12 <-> 13, this bug goes away

Bug 2:
The gist as is will give the output:
{
split : 90
paths :
{
1 : ../../toyset
}
sampleSize :
{
1 : 3
2 : 100
3 : 100
}
}

However, if you remove line 10, you will get the output:
{
sampleSize :
{
split : 15
paths :
{
1 : ../../toyset
}
sampleSize :
{
1 : 3
2 : 100
3 : 100
}
}
split : 90
}

Notice that inside of sampleSize, there is now a member variable split: 15, apart from the last line having a split: 90

torch/rational broken

argcheck cause error :

th> rat = require 'rational'
                                                                      [0.0622s]
th> print(rat(5, 4) * rat(3, 6))
[string "argcheck"]:42: 
Arguments:

({
   a = rational.number  -- 
   b = rational.number  -- 
})


Got: table, table
invalid arguments!
stack traceback:
        [C]: in function 'error'
        [string "argcheck"]:42: in function '__mul'
        [string "print(rat(5, 4) * rat(3, 6))"]:1: in main chunk
        [C]: in function 'xpcall'
        ...lya/devel/lua/torch/install/share/lua/5.1/trepl/init.lua:679: in function 'repl'
        .../lua/torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th:204: in main chunk
        [C]: at 0x562db81c3e00
                                                                      [0.0005s]

Beggining a doc line with an hyphen fails

Just found that sundown is bugging when a line in the argcheck documentation bloc begins with an hyphen. Check the following code :

Dataframe.upgrade_frame = argcheck{doc =  [[
<a name="Dataframe.upgrade_frame">
### Dataframe.upgrade_frame(@ARGP)

Some text :
- First point
- Second point

@ARGT

_Return value_: void
]],
    {name = "self", type = "Dataframe"},
    call = function(self)
end}

Here is the error I have with Lua 5.1, 5.2 and 5.3 :

ascii.lua:227: attempt to index global 'bit' (a nil value)

Everything works perfectly with Luajit. Replacing the hyphen by an underscore or preceding it by a random letter do the trick.

Here is the full stacktrace :

/opt/torch/install/share/lua/5.1/sundown/ascii.lua:227: attempt to index global 'bit' (a nil value)
stack traceback:
    /opt/torch/install/share/lua/5.1/trepl/init.lua:501: in function </opt/torch/install/share/lua/5.1/trepl/init.lua:494>
    /opt/torch/install/share/lua/5.1/sundown/ascii.lua:227: in function </opt/torch/install/share/lua/5.1/sundown/ascii.lua:223>
    [C]: in function 'sd_markdown_render'
    /opt/torch/install/share/lua/5.1/sundown/ascii.lua:391: in function 'preprocess'
    /opt/torch/install/share/lua/5.1/sundown/ascii.lua:583: in function 'render'
    /opt/torch/install/share/lua/5.1/argcheck/usage.lua:84: in function 'render'
    /opt/torch/install/share/lua/5.1/argcheck/init.lua:102: in function 'argcheck'
    ...dataframe/sub_classes/subset_extensions/samplers.lua:23: in main chunk
    /opt/torch-dataframe/sub_classes/subset.lua:220: in main chunk
    /opt/torch-dataframe/init.lua:39: in main chunk
    [C]: in function 'dofile'
    /opt/torch/install/share/lua/5.1/trepl/init.lua:361: in function 'require'
    [string "_RESULT={require './init.lua'}"]:1: in main chunk
    [C]: in function 'xpcall'
    /opt/torch/install/share/lua/5.1/trepl/init.lua:651: in function 'repl'
    .../torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th:199: in main chunk
    [C]: ?

Problem parsing more than 9 input arguments

The following code snippet reproduces the problem:

argcheck = require 'argcheck'
initcheck = argcheck{
  {name="x1",type="string",help="x1",default="a"},
  {name="x2",type="string",help="x2",default="a"},
  {name="x3",type="string",help="x3",default="a"},
  {name="x4",type="string",help="x4",default="a"},
  {name="x5",type="string",help="x5",default="a"},
  {name="x6",type="string",help="x6",default="a"},
  {name="x7",type="string",help="x7",default="a"},
  {name="x8",type="string",help="x8",default="a"},
  {name="x9",type="string",help="x9",default="a"},
  {name="x10",type="string",help="x10",default="a"}
}

Here is the error I get:

/usr/local/share/lua/5.1/argcheck/init.lua:105: could not generate argument checker: [string "argcheck"]:35093: control structure too long near 'end'
stack traceback:
    [C]: in function 'error'
    /usr/local/share/lua/5.1/argcheck/init.lua:105: in function 'argcheck'
    [string "initcheck = argcheck{..."]:1: in main chunk
    [C]: in function 'xpcall'
    /usr/local/share/lua/5.1/trepl/init.lua:567: in function </usr/local/share/lua/5.1/trepl/init.lua:468>

default sets given argument to last slot

exfunc = argcheck{
{name='dim1', type='number'},
{name='dim2', type='number', default=0},
{name='dim3', type='number', default=0},
{name='dim4', type='number', default=0},
nonamed=true,
call =
function(dim1, dim2, dim3, dim4)
print(dim1, dim2, dim3, dim4)
end
}

exfunc(1,2)

Output:
1 0 0 2

Expected:
1 2 0 0

Preparing the argcheck.doc for large projects

As projects grow bigger the API-documentation can be difficult to manage. I would like to see (1) support for multiple files and (2) automated generation of table of contents (TOC).

Allowing direct markdown output to multiple files.

In our Dataframe package we use th doc.lua > Doc/README.md in order to create the docs. As the Dataframe has grown in functionality we have split the package into main functions and extensions. The latter are in a subdirectory and therefore easy to navigate and find. Unfortunately argcheck.doc relies on detecting the output being a tty and we aren’t sure how to separate each extension file in the th doc.lua call. The only solution it seems is to add a bash-script outside Lua that does this. Could you consider amending the usage.render/doc.stop?

Adding a table of contents

I saw in your Cairo-ffi that all function definitions are accommodated with a an anchor, i.e. <a name="ImageSurface.new">. We have copied that structure, unfortunately I can’t find any way to autogenerate a table of contents using that information. Atom has a neat package that allows this, perhaps it could work as a template for the Lua implementation?

Thanks for an awesome package!

Installation fails; rock not found

What am I missing?

$ luarocks install argcheck
Warning: falling back to curl - install luasec to get native HTTPS support

Error: No results matching query were found.

I'm on a Mac. Other rocks install fine.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.