-
Notifications
You must be signed in to change notification settings - Fork 34
/
OptionParser.lua
202 lines (189 loc) · 5.94 KB
/
OptionParser.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
--
-- Lua command line option parser.
-- Interface based on Pythons optparse.
-- http://docs.python.org/lib/module-optparse.html
-- (c) 2008 David Manura, Licensed under the same terms as Lua (MIT license)
--
-- To be used like this:
-- t={usage="<some usage message>", version="<version string>"}
-- op = xlua.OptionParser(t)
-- op:option{"<opt>", action=<action>, dest=<dest>, help="<help message for this option>"}
--
-- with :
-- <opt> the option string to be used (can be anything, if one letter opt, then should be -x val, more letters: -xy=val )
-- <action> one of
-- - store: store in options as key, val
-- - store_true: stores key, true
-- - store_false: stores key, false
-- <dest> is the key under which the option is saved
--
-- options,args = op.parse_args()
--
-- now options is the table of options (key, val) and args is the table with non-option arguments.
-- You can use op.fail(message) for failing and op.help() for printing the usage as you like.
--
-- modifed by Benoit Corda, Clement Farabet
--
local OptionParser = {}
function xlua.OptionParser(usage)
local self = {}
self.usage = usage
self.option_descriptions = {}
self.option_of = {}
for k,v in pairs(OptionParser) do
self[k] = v
end
self:option{"-h", "--help", action="store_true", dest="help",
help="show this help message and exit"}
return self
end
function OptionParser:fail(s) -- extension
io.stderr:write(s .. '\n')
self:help()
os.exit(1)
end
function OptionParser:option(optdesc)
self.option_descriptions[#self.option_descriptions+1] = optdesc
for _,v in ipairs(optdesc) do
self.option_of[v] = optdesc
end
end
function OptionParser:parse(options)
local options = options or {}
local args = {}
-- set the default
for _,v in ipairs(self.option_descriptions) do
if v.default ~= nil and options[v.dest]==nil then
options[v.dest] = v.default
end
end
if not arg then
options.__main__ = false -- python like main
self.options = options
return options, args
end
options.__main__ = true -- python like main
-- expand options (e.g. "--input=file" -> "--input", "file")
local unpack = unpack or table.unpack
local arg = {unpack(arg)}
for i=#arg,1,-1 do local v = arg[i]
local flag, val = v:match('^(%-%-%w+)=(.*)')
if flag then
arg[i] = flag
table.insert(arg, i+1, val)
end
end
local i = 1
while i <= #arg do
local v = arg[i]
local optdesc = self.option_of[v]
if optdesc then
local default = optdesc.default
local action = optdesc.action
local val = default
if action == 'store' or action == nil then
i = i + 1
val = arg[i] or default
if not val then self:fail('option requires an argument ' .. v) end
elseif action == 'store_true' then
val = true
elseif action == 'store_false' then
val = false
end
options[optdesc.dest] = val
else
if v:match('^%-') then self:fail('invalid option ' .. v) end
args[#args+1] = v
end
i = i + 1
end
for k,opt in pairs(self.option_of) do
if opt.req and not options[opt.dest] then
self:fail('option '.. k .. ' requires an argument ')
end
end
if options.help then
self:help()
os.exit()
end
-- set the default if nil
self.options = options
return options, args
end
function OptionParser:flags(optdesc)
local sflags = {}
local action = optdesc and optdesc.action
for _,flag in ipairs(optdesc) do
local sflagend
if action == nil or action == 'store' then
local metavar = optdesc.metavar or optdesc.dest:upper()
sflagend = #flag == 2 and ' ' .. metavar
or '=' .. metavar
else
sflagend = ''
end
sflags[#sflags+1] = flag .. sflagend
end
return table.concat(sflags, ', ')
end
function OptionParser:help()
if arg[-1] then
io.stdout:write("Usage: " .. self.usage:gsub('%%prog', (arg[-1] .. ' ' .. arg[0])) .. "\n")
elseif arg[0] then
io.stdout:write("Usage: " .. self.usage:gsub('%%prog', arg[0]) .. "\n")
else
io.stdout:write("Usage: " .. self.usage:gsub('%%prog', 'THISPROG') .. "\n")
end
io.stdout:write("\n")
io.stdout:write("Options:\n")
pad = 0
for _,optdesc in ipairs(self.option_descriptions) do
pad = math.max(pad, #self:flags(optdesc))
end
for _,optdesc in ipairs(self.option_descriptions) do
local defstr = ''
if optdesc.req then
defstr = ' [REQUIRED]'
elseif optdesc.default then
defstr = ' [default = ' .. tostring(optdesc.default) .. ']'
end
io.stdout:write(" " .. self:flags(optdesc) ..
string.rep(' ', pad - #self:flags(optdesc)) ..
" " .. optdesc.help .. defstr .. "\n")
end
end
function OptionParser:tostring(generatefilename, params)
local str = ''
if not generatefilename then
str = '<'.. ((arg and arg[0]) or 'interpreted.lua'):gsub('.lua','') .. "> configuration:\n"
for k,v in pairs(self.options) do
str = str .. ' + ' .. k .. ' = ' .. tostring(v) .. '\n'
end
else
local first = true
for i,entry in ipairs(self.option_descriptions) do
local key = entry[1]
local match = true
if #params > 0 then
match = false
for i,param in ipairs(params) do
if key == param then match = true; break end
end
end
local val = self.options[entry.dest]
if val and match then
if first then
str = str .. key .. '=' .. tostring(val)
else
str = str .. ',' .. key .. '=' .. tostring(val)
end
first = false
end
end
str = str:gsub('/','|'):gsub(' ','_')
end
return str
end
function OptionParser:summarize(compact)
io.write(self:tostring(compact))
end