-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathargparser.lua
More file actions
149 lines (135 loc) · 3.98 KB
/
argparser.lua
File metadata and controls
149 lines (135 loc) · 3.98 KB
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
---@class ConsoleArgParseResult
---@field error string|nil error message or nil if it was successful
---@field args table<string,any>|nil the parses arguments with their name as key and value as value or nil on error
--- Parse console arguments. Similar to how the teeworlds console code does it.
---
--- Example:
---
--- ```lua
--- args = parse_args("i[client id]s[name]?i[code]", "1 steve 255")
--- if args.error then
--- print("arg error: " .. args.error)
--- os.exit(1)
--- end
--- args = args.args
--- print("got name=" .. args.name .. " and client_id=" .. args["client id"])
--- ```
---
---@param params string expected teeworlds rcon arguments string. Example: "s[name]i[id]"
---@param args string arguments we got from the user
---@return ConsoleArgParseResult
function parse_args(params, args)
---@class ParsedParam
---@field name string the name of the param
---@field type string the type of the param, possible values "i", "r", "s"
---@field optional boolean
---@type ParsedParam[]
pparams = {}
---@type string|false
local current_name = false
---@type string|false
local current_type = false
---@type boolean
local current_optional = false
-- TODO: error if optional is followed by non optional
for i = 1, #params do
local c = params:sub(i,i)
local last = params:sub(i+1,i+1) == ""
if current_name then
if c == "]" then
table.insert(pparams, {
name = current_name,
type = current_type,
optional = current_optional
})
current_name = false
current_type = false
current_optional = false
else
current_name = current_name .. c
end
elseif c == "[" then
current_name = ""
elseif c == "?" then
current_optional = true
elseif (c == "i") or (c == "s") then
if current_type then
table.insert(pparams, {
name = current_name,
type = current_type,
optional = current_optional
})
current_name = false
current_type = false
current_optional = false
end
-- only flush prev type and queue next
-- unless its end of string then we flush both
current_type = c
if last then
table.insert(pparams, {
name = current_name,
type = current_type,
optional = current_optional
})
current_name = false
current_type = false
current_optional = false
end
else
return {
error = "unsupported parameter type '" .. c .. "'",
args = nil
}
end
end
if current_optional or current_name or current_type then
return {
error = "unexpected end of parameters",
args = nil
}
end
local result = {
error = nil,
args = {}
}
-- TODO: add multi word arg support with quotes
local words = {}
for word in string.gmatch(args, "%S+") do
words[#words+1] = word
end
for i,param in ipairs(pparams) do
if words[i] == nil then
if param.optional then
break
end
return {
error = "missing arg. usage: " .. params,
args = nil
}
end
---@type string|integer
local key = param.name
if key == false then
key = #result.args+1
end
if param.type == "s" then
result.args[key] = words[i]
elseif param.type == "i" then
local num = tonumber(words[i])
if num == nil then
return {
error = "argument '" .. words[i] .. "' is not a valid integer",
args = nil
}
end
result.args[key] = num
else
return {
error = "unknown parameter type " .. param.type,
args = nil
}
end
end
return result
end