-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature request: simple option parsing #6
Comments
With Microlight the really hard thing is knowing what to include ;) Here is the simple command-line parser from Penlight (pl.app module): --- parse command-line arguments into flags and parameters.
-- Understands GNU-style command-line flags; short (`-f`) and long (`--flag`).
-- These may be given a value with either '=' or ':' (`-k:2`,`--alpha=3.2`,`-n2`);
-- note that a number value can be given without a space.
-- Multiple short args can be combined like so: ( `-abcd`).
-- @param args an array of strings (default is the global `arg`)
-- @param flags_with_values any flags that take values, e.g. <code>{out=true}</code>
-- @return a table of flags (flag=value pairs)
-- @return an array of parameters
-- @raise if args is nil, then the global `args` must be available!
function app.parse_args (args,flags_with_values)
if not args then
args = _G.arg
if not args then error "Not in a main program: 'arg' not found" end
end
flags_with_values = flags_with_values or {}
local _args = {}
local flags = {}
local i = 1
while i <= #args do
local a = args[i]
local v = a:match('^-(.+)')
local is_long
if v then -- we have a flag
if v:find '^-' then
is_long = true
v = v:sub(2)
end
if flags_with_values[v] then
if i == #_args or args[i+1]:find '^-' then
return nil, "no value for '"..v.."'"
end
flags[v] = args[i+1]
i = i + 1
else
-- a value can also be indicated with =
local var,val = utils.splitv (v,'=')
var = var or v
val = val or true
if not is_long then
if #var > 1 then
if var:find '.%d+' then -- short flag, number value
val = var:sub(2)
var = var:sub(1,1)
else -- multiple short flags
for i = 1,#var do
flags[var:sub(i,i)] = true
end
val = nil -- prevents use of var as a flag below
end
else -- single short flag (can have value, defaults to true)
val = val or true
end
end
if val then
flags[var] = val
end
end
else
_args[#_args+1] = a
end
i = i + 1
end
return flags,_args
end Easy to adapt for our purposes, maybe write out But I'm not sure if it belongs in such a little library? |
lua microlight is awesome because it does not include lots of useless things, so yes, if it belongs in microlight or not is an important question. I think it might belong there because option parsing is basically needed for every standalone Lua program in one form or other. I miss some form of validation for valid flags too. |
That's exactly the question. It's short - and my collaborators think it should be even shorter! But Penlight is a big beast. To use pl.app, you need its dependencies. Maybe what we need is for this little 'recipe' to be a Lua snippet (http://snippets.luacode.org/). Then people can grab exactly what they need, and no more. It's true that parsing args is something every Lua script needs to do, and it's a pain doing it by hand every time! |
Here is a function that uses the 'options' part of the usage text as validation. It only does shortopt though: local function parse_opts(opthelp, raw_args)
local valid_opts = {}
local opts = {}
local args = {}
local moreopts = true
for optc, separator in opthelp:gmatch("%s+%-(%a)(%s+)") do
valid_opts[optc] = { hasarg = (separator == " ") }
end
local i = 1
while i <= #raw_args do
local a = raw_args[i]
i = i + 1
if a == "--" then
moreopts = false
elseif moreopts and a:sub(1,1) == "-" then
for j = 2, #a do
local opt = a:sub(j,j)
if not valid_opts[opt] then
return nil, "-"..opt, "invalid option"
end
if valid_opts[opt].hasarg then
opts[opt] = raw_args[i]
i = i + 1
else
opts[opt] = true
end
if not opts[opt] then
return nil, "-"..opt, "optarg required"
end
end
else
args[#args + 1] = a
end
end
return opts, args
end Example usage: opthelp = [[
-d DIR set directory
-f some flag
-h show this help and exit
]]
opts, args, errmsg = parse_opts(opthelp, arg)
if opts == nil then
io.stderr:write(("%s: %s\n"):format(errmsg, args))
io.stderr:write(("Usage: %s [OPTIONS]\n"..
"Options:\n%s"):format(arg[0], opthelp))
os.exit(1)
end Not suggesting that you add this as is (support for long opts would be nice), but it would be nice if we could use parts of the help text for validation. |
Other possibility would be to split up Penlight and you pick the parts of it you need. |
I did think of that. But most modules depend on others. I raised the possibility of making them all available as separate LuaRocks packages, for instance, but Hisham wisely felt that it would be going backward - there's already so many little Lua modules doing similar things. But what I can do is make a straightforward version available as a standalone module - still part of the Microlight project. I've just seen your code that uses the actual options text. That's the brilliant thing about Lua, you can easily re-use data which is also meant for the human. Have a look at how pl.lapp does it from Penlight: http://stevedonovan.github.io/Penlight/api/modules/pl.lapp.html (lapp,lua only depends on sip.lua, because I knew someone would want this without the rest of Penlight) |
yes, I was inspired by lapp and what I am looking for is some simplified version of lapp which does not need support for various types (numbers strings) or default values. What I think lapp does right is that the actualy help text is the source of the validation. You don't need list the valid options twice like you do with getopt(3). (one in the optstring and one in the help/usage text) |
It would be nice to have some simple option parsing function included in microlight.
Thanks!
The text was updated successfully, but these errors were encountered: