230 lines
5.6 KiB
Lua
230 lines
5.6 KiB
Lua
ChatCmdBuilder = {}
|
|
|
|
function ChatCmdBuilder.new(name, func, def)
|
|
def = def or {}
|
|
local cmd = ChatCmdBuilder.build(func)
|
|
cmd.def = def
|
|
def.func = cmd.run
|
|
minetest.register_chatcommand(name, def)
|
|
return cmd
|
|
end
|
|
|
|
local STATE_READY = 1
|
|
local STATE_PARAM = 2
|
|
local STATE_PARAM_TYPE = 3
|
|
local bad_chars = {}
|
|
bad_chars["("] = true
|
|
bad_chars[")"] = true
|
|
bad_chars["."] = true
|
|
bad_chars["%"] = true
|
|
bad_chars["+"] = true
|
|
bad_chars["-"] = true
|
|
bad_chars["*"] = true
|
|
bad_chars["?"] = true
|
|
bad_chars["["] = true
|
|
bad_chars["^"] = true
|
|
bad_chars["$"] = true
|
|
local function escape(char)
|
|
if bad_chars[char] then
|
|
return "%" .. char
|
|
else
|
|
return char
|
|
end
|
|
end
|
|
|
|
function ChatCmdBuilder.build(func)
|
|
local cmd = {
|
|
_subs = {}
|
|
}
|
|
function cmd:sub(route, func, def)
|
|
print("Parsing " .. route)
|
|
|
|
def = def or {}
|
|
if string.trim then
|
|
route = string.trim(route)
|
|
end
|
|
|
|
local sub = {
|
|
pattern = "^",
|
|
params = {},
|
|
func = func
|
|
}
|
|
|
|
-- End of param reached: add it to the pattern
|
|
local param = ""
|
|
local param_type = ""
|
|
local should_be_eos = false
|
|
local function finishParam()
|
|
if param ~= "" and param_type ~= "" then
|
|
print(" - Found param " .. param .. " type " .. param_type)
|
|
|
|
if param_type == "pos" then
|
|
sub.pattern = sub.pattern .. "%(? *(%-?[%d.]+) *, *(%-?[%d.]+) *, *(%-?[%d.]+) *%)?"
|
|
elseif param_type == "text" then
|
|
sub.pattern = sub.pattern .. "(*+)"
|
|
should_be_eos = true
|
|
elseif param_type == "number" then
|
|
sub.pattern = sub.pattern .. "([%d.]+)"
|
|
elseif param_type == "int" then
|
|
sub.pattern = sub.pattern .. "([%d]+)"
|
|
else
|
|
if param_type ~= "word" then
|
|
print("Unrecognised param_type=" .. param_type .. ", using 'word' type instead")
|
|
param_type = "word"
|
|
end
|
|
sub.pattern = sub.pattern .. "([^ ]+)"
|
|
end
|
|
|
|
table.insert(sub.params, param_type)
|
|
|
|
param = ""
|
|
param_type = ""
|
|
end
|
|
end
|
|
|
|
-- Iterate through the route to find params
|
|
local state = STATE_READY
|
|
for i = 1, #route do
|
|
local c = route:sub(i, i)
|
|
if should_be_eos then
|
|
error("Should be end of string. Nothing is allowed after a param of type text.")
|
|
end
|
|
|
|
if state == STATE_READY then
|
|
if c == ":" then
|
|
print(" - Found :, entering param")
|
|
state = STATE_PARAM
|
|
param_type = "word"
|
|
else
|
|
sub.pattern = sub.pattern .. escape(c)
|
|
end
|
|
elseif state == STATE_PARAM then
|
|
if c == ":" then
|
|
print(" - Found :, entering param type")
|
|
state = STATE_PARAM_TYPE
|
|
param_type = ""
|
|
elseif c:match("%W") then
|
|
print(" - Found nonalphanum, leaving param")
|
|
state = STATE_READY
|
|
finishParam()
|
|
sub.pattern = sub.pattern .. escape(c)
|
|
else
|
|
param = param .. c
|
|
end
|
|
elseif state == STATE_PARAM_TYPE then
|
|
if c:match("%W") then
|
|
print(" - Found nonalphanum, leaving param type")
|
|
state = STATE_READY
|
|
finishParam()
|
|
sub.pattern = sub.pattern .. escape(c)
|
|
else
|
|
param_type = param_type .. c
|
|
end
|
|
end
|
|
end
|
|
print(" - End of route")
|
|
finishParam()
|
|
sub.pattern = sub.pattern .. "$"
|
|
print("Pattern: " .. sub.pattern)
|
|
|
|
table.insert(self._subs, sub)
|
|
end
|
|
|
|
if func then
|
|
func(cmd)
|
|
end
|
|
|
|
cmd.run = function(name, param)
|
|
for i = 1, #cmd._subs do
|
|
local sub = cmd._subs[i]
|
|
local res = { string.match(param, sub.pattern) }
|
|
if #res > 0 then
|
|
local pointer = 1
|
|
local params = { name }
|
|
for j = 1, #sub.params do
|
|
local param = sub.params[j]
|
|
if param == "pos" then
|
|
local pos = {
|
|
x = tonumber(res[pointer]),
|
|
y = tonumber(res[pointer + 1]),
|
|
z = tonumber(res[pointer + 2])
|
|
}
|
|
table.insert(params, pos)
|
|
pointer = pointer + 3
|
|
elseif param == "number" or param == "int" then
|
|
table.insert(params, tonumber(res[pointer]))
|
|
pointer = pointer + 1
|
|
else
|
|
table.insert(params, res[pointer])
|
|
pointer = pointer + 1
|
|
end
|
|
end
|
|
return sub.func(unpack(params))
|
|
end
|
|
end
|
|
print("No matches")
|
|
end
|
|
|
|
return cmd
|
|
end
|
|
|
|
local function run_tests()
|
|
if not (ChatCmdBuilder.build(function(cmd)
|
|
cmd:sub("bar :one and :two:word", function(name, one, two)
|
|
if name == "singleplayer" and one == "abc" and two == "def" then
|
|
return true
|
|
end
|
|
end)
|
|
end))("singleplayer", "bar abc and def") then
|
|
error("Test 1 failed")
|
|
end
|
|
|
|
local move = ChatCmdBuilder.build(function(cmd)
|
|
cmd:sub("move :target to :pos:pos", function(name, target, pos)
|
|
if name == "singleplayer" and target == "player1" and
|
|
pos.x == 0 and pos.y == 1 and pos.z == 2 then
|
|
return true
|
|
end
|
|
end)
|
|
end)
|
|
if not move("singleplayer", "move player1 to 0,1,2") then
|
|
error("Test 2 failed")
|
|
end
|
|
if not move("singleplayer", "move player1 to (0,1,2)") then
|
|
error("Test 3 failed")
|
|
end
|
|
if not move("singleplayer", "move player1 to 0, 1,2") then
|
|
error("Test 4 failed")
|
|
end
|
|
if not move("singleplayer", "move player1 to 0 ,1, 2") then
|
|
error("Test 5 failed")
|
|
end
|
|
if not move("singleplayer", "move player1 to 0, 1, 2") then
|
|
error("Test 6 failed")
|
|
end
|
|
if not move("singleplayer", "move player1 to 0 ,1 ,2") then
|
|
error("Test 7 failed")
|
|
end
|
|
if not move("singleplayer", "move player1 to ( 0 ,1 ,2)") then
|
|
error("Test 7 failed")
|
|
end
|
|
if move("singleplayer", "move player1 to abc,def,sdosd") then
|
|
error("Test 8 failed")
|
|
end
|
|
if move("singleplayer", "move player1 to abc def sdosd") then
|
|
error("Test 8 failed")
|
|
end
|
|
|
|
if not (ChatCmdBuilder.build(function(cmd)
|
|
cmd:sub("does :one:int plus :two:int equal :three:int", function(name, one, two, three)
|
|
if name == "singleplayer" and one + two == three then
|
|
return true
|
|
end
|
|
end)
|
|
end))("singleplayer", "does 1 plus 2 equal 3") then
|
|
error("Test 9 failed")
|
|
end
|
|
end
|
|
if not minetest then
|
|
run_tests()
|
|
end
|