---
advtrains/aj_unittest.lua | 155 +++++++++++++++++++++++++++++
advtrains/atc.lua | 192 ++----------------------------------
advtrains/atcjit.lua | 201 ++++++++++++++++++++++++++++++++++++++
advtrains/init.lua | 2 +
4 files changed, 367 insertions(+), 183 deletions(-)
create mode 100644 advtrains/aj_unittest.lua
create mode 100644 advtrains/atcjit.lua
diff --git a/advtrains/aj_unittest.lua b/advtrains/aj_unittest.lua
new file mode 100644
index 0000000..9dacbc3
--- /dev/null
+++ b/advtrains/aj_unittest.lua
@@ -0,0 +1,155 @@
+advtrains = {}
+_G.advtrains = advtrains
+function _G.attrans(...) return ... end
+function advtrains.invert_train() end
+function advtrains.train_ensure_init() end
+
+local atcjit = require("atcjit")
+
+local function assert_atc(train, warn, err, res)
+ local w, e = atcjit.execute(train.id,train)
+ assert.same(err, e)
+ if w then assert.same(warn, w) end
+ assert.same(res, train)
+end
+
+local function thisatc(desc, train, warn, err, res)
+ it(desc, function() assert_atc(train, warn, err, res) end)
+end
+
+describe("simple ATC track", function()
+ local t = {
+ atc_arrow = true,
+ atc_command = " B12WB8WBBWOLD15ORD15OCD1RS10WSM",
+ door_open = 0,
+ max_speed = 20,
+ tarvelocity = 10,
+ velocity = 0,
+ }
+ thisatc("should make the train slow down to 12", t, {}, nil,{
+ atc_arrow = true,
+ atc_brake_target = 12,
+ atc_command = "B8WBBWOLD15ORD15OCD1RS10WSM",
+ atc_wait_finish = true,
+ door_open = 0,
+ max_speed = 20,
+ tarvelocity = 10,
+ velocity = 0,
+ })
+ thisatc("should make the train brake to 8", t, {}, nil, {
+ atc_arrow = true,
+ atc_brake_target = 8,
+ atc_command = "BBWOLD15ORD15OCD1RS10WSM",
+ atc_wait_finish = true,
+ door_open = 0,
+ max_speed = 20,
+ tarvelocity = 8,
+ velocity = 0,
+ })
+ thisatc("should make the train stop", t, {}, nil, {
+ atc_arrow = true,
+ atc_brake_target = -1,
+ atc_command = "OLD15ORD15OCD1RS10WSM",
+ atc_wait_finish = true,
+ door_open = 0,
+ max_speed = 20,
+ tarvelocity = 0,
+ velocity = 0,
+ })
+ thisatc("should make the train open its left doors", t, {}, nil, {
+ atc_arrow = true,
+ atc_brake_target = -1,
+ atc_command = "ORD15OCD1RS10WSM",
+ atc_delay = 15,
+ atc_wait_finish = true,
+ door_open = -1,
+ max_speed = 20,
+ tarvelocity = 0,
+ velocity = 0,
+ })
+ thisatc("should make the train open its right doors", t, {}, nil,{
+ atc_arrow = true,
+ atc_brake_target = -1,
+ atc_command = "OCD1RS10WSM",
+ atc_delay = 15,
+ atc_wait_finish = true,
+ door_open = 1,
+ max_speed = 20,
+ tarvelocity = 0,
+ velocity = 0,
+ })
+ thisatc("should make the train close its doors", t, {}, nil, {
+ atc_arrow = true,
+ atc_brake_target = -1,
+ atc_command = "RS10WSM",
+ atc_delay = 1,
+ atc_wait_finish = true,
+ door_open = 0,
+ max_speed = 20,
+ tarvelocity = 0,
+ velocity = 0,
+ })
+ thisatc("should make the train depart and accelerate to 10", t, {}, nil, {
+ atc_arrow = true,
+ atc_brake_target = -1,
+ atc_command = "SM",
+ atc_delay = 1,
+ atc_wait_finish = true,
+ door_open = 0,
+ max_speed = 20,
+ tarvelocity = 10,
+ velocity = 0,
+ })
+ thisatc("should make the train accelerate to 20", t, {}, nil, {
+ atc_arrow = true,
+ atc_brake_target = -1,
+ atc_delay = 1,
+ atc_wait_finish = true,
+ door_open = 0,
+ max_speed = 20,
+ tarvelocity = 20,
+ velocity = 0,
+ })
+end)
+
+describe("ATC track with whitespaces", function()
+ local t = {
+ atc_command = " \t\n OC \n S20 \r "
+ }
+ thisatc("should not cause errors", t, {}, nil, {
+ door_open = 0,
+ tarvelocity = 20,
+ })
+end)
+
+describe("empty ATC track", function()
+ local t = {atc_command = ""}
+ thisatc("should not do anything", t, {}, nil, {})
+end)
+
+describe("ATC track with nested I statements", function()
+ local t = {
+ atc_arrow = false,
+ atc_command = "I+OREI>5I<=10S16WORES12;D15;;OC",
+ velocity = 10,
+ door_open = 0,
+ }
+ thisatc("should make the train accelerate to 16", t, {}, nil,{
+ atc_arrow = false,
+ atc_command = "ORD15OC",
+ atc_wait_finish = true,
+ velocity = 10,
+ door_open = 0,
+ tarvelocity = 16,
+ })
+end)
+
+describe("ATC track with invalid statement", function()
+ local t = { atc_command = "Ifoo" }
+ thisatc("should report an error", t, {}, "Invalid command or malformed I statement: Ifoo", t)
+end)
+
+describe("ATC track with invalid I condition", function()
+ local t = { atc_command = "I?;" }
+ thisatc("should report an error", t, {}, "Invalid I statement", t)
+end)
diff --git a/advtrains/atc.lua b/advtrains/atc.lua
index 2fa3929..eff6420 100644
--- a/advtrains/atc.lua
+++ b/advtrains/atc.lua
@@ -53,15 +53,7 @@ function atc.send_command(pos, par_tid)
atwarn("ATC rail at", pos, ": Rail not on train's path! Can't determine arrow direction. Assuming +!")
end
- local command = atc.controllers[pts].command
- command = eval_conditional(command, iconnid==1, train.velocity)
- if not command then command="" end
- command=string.match(command, "^%s*(.*)$")
-
- if command == "" then
- atprint("Sending ATC Command to", train_id, ": Not modifying, conditional evaluated empty.")
- return true
- end
+ local command = atc.controllers[pts].command
atc.train_set_command(train, command, iconnid==1)
atprint("Sending ATC Command to", train_id, ":", command, "iconnid=",iconnid)
@@ -182,184 +174,18 @@ function atc.get_atc_controller_formspec(pos, meta)
return formspec.."button_exit[0.5,4.5;7,1;save;"..attrans("Save").."]"
end
---from trainlogic.lua train step
-local matchptn={
- ["SM"]=function(id, train)
- train.tarvelocity=train.max_speed
- return 2
- end,
- ["S([0-9]+)"]=function(id, train, match)
- train.tarvelocity=tonumber(match)
- return #match+1
- end,
- ["B([0-9]+)"]=function(id, train, match)
- if train.velocity>tonumber(match) then
- train.atc_brake_target=tonumber(match)
- if not train.tarvelocity or train.tarvelocity>train.atc_brake_target then
- train.tarvelocity=train.atc_brake_target
- end
- end
- return #match+1
- end,
- ["BB"]=function(id, train)
- train.atc_brake_target = -1
- train.tarvelocity = 0
- return 2
- end,
- ["W"]=function(id, train)
- train.atc_wait_finish=true
- return 1
- end,
- ["D([0-9]+)"]=function(id, train, match)
- train.atc_delay=tonumber(match)
- return #match+1
- end,
- ["R"]=function(id, train)
- if train.velocity<=0 then
- advtrains.invert_train(id)
- advtrains.train_ensure_init(id, train)
- -- no one minds if this failed... this shouldn't even be called without train being initialized...
- else
- atwarn(sid(id), attrans("ATC Reverse command warning: didn't reverse train, train moving!"))
- end
- return 1
- end,
- ["O([LRC])"]=function(id, train, match)
- local tt={L=-1, R=1, C=0}
- local arr=train.atc_arrow and 1 or -1
- train.door_open = tt[match]*arr
- return 2
- end,
- ["K"] = function(id, train)
- if train.door_open == 0 then
- atwarn(sid(id), attrans("ATC Kick command warning: Doors closed"))
- return 1
- end
- if train.velocity > 0 then
- atwarn(sid(id), attrans("ATC Kick command warning: Train moving"))
- return 1
- end
- local tp = train.trainparts
- for i=1,#tp do
- local data = advtrains.wagons[tp[i]]
- local obj = advtrains.wagon_objects[tp[i]]
- if data and obj then
- local ent = obj:get_luaentity()
- if ent then
- for seatno,seat in pairs(ent.seats) do
- if data.seatp[seatno] and not ent:is_driver_stand(seat) then
- ent:get_off(seatno)
- end
- end
- end
- end
- end
- return 1
- end,
-}
-
-eval_conditional = function(command, arrow, speed)
- --conditional statement?
- local is_cond, cond_applies, compare
- local cond, rest=string.match(command, "^I([%+%-])(.+)$")
- if cond then
- is_cond=true
- if cond=="+" then
- cond_applies=arrow
- end
- if cond=="-" then
- cond_applies=not arrow
- end
- else
- cond, compare, rest=string.match(command, "^I([<>]=?)([0-9]+)(.+)$")
- if cond and compare then
- is_cond=true
- if cond=="<" then
- cond_applies=speed<tonumber(compare)
- end
- if cond==">" then
- cond_applies=speed>tonumber(compare)
- end
- if cond=="<=" then
- cond_applies=speed<=tonumber(compare)
- end
- if cond==">=" then
- cond_applies=speed>=tonumber(compare)
- end
- end
- end
- if is_cond then
- atprint("Evaluating if statement: "..command)
- atprint("Cond: "..(cond or "nil"))
- atprint("Applies: "..(cond_applies and "true" or "false"))
- atprint("Rest: "..rest)
- --find end of conditional statement
- local nest, pos, elsepos=0, 1
- while nest>=0 do
- if pos>#rest then
- atwarn(sid(id), attrans("ATC command syntax error: I statement not closed: @1",command))
- return ""
- end
- local char=string.sub(rest, pos, pos)
- if char=="I" then
- nest=nest+1
- end
- if char==";" then
- nest=nest-1
- end
- if nest==0 and char=="E" then
- elsepos=pos+0
- end
- pos=pos+1
- end
- if not elsepos then elsepos=pos-1 end
- if cond_applies then
- command=string.sub(rest, 1, elsepos-1)..string.sub(rest, pos)
- else
- command=string.sub(rest, elsepos+1, pos-2)..string.sub(rest, pos)
- end
- atprint("Result: "..command)
- end
- return command
-end
function atc.execute_atc_command(id, train)
- --strip whitespaces
- local command=string.match(train.atc_command, "^%s*(.*)$")
-
-
- if string.match(command, "^%s*$") then
- train.atc_command=nil
- return
- end
-
- train.atc_command = eval_conditional(command, train.atc_arrow, train.velocity)
-
- if not train.atc_command then return end
- command=string.match(train.atc_command, "^%s*(.*)$")
-
- if string.match(command, "^%s*$") then
- train.atc_command=nil
- return
- end
-
- for pattern, func in pairs(matchptn) do
- local match=string.match(command, "^"..pattern)
- if match then
- local patlen=func(id, train, match)
-
- atprint("Executing: "..string.sub(command, 1, patlen))
-
- train.atc_command=string.sub(command, patlen+1)
- if train.atc_delay<=0 and not train.atc_wait_finish then
- --continue (recursive, cmds shouldn't get too long, and it's a end-recursion.)
- atc.execute_atc_command(id, train)
- end
- return
+ local w, e = advtrains.atcjit.execute(id, train)
+ if w then
+ for i = 1, #w, 1 do
+ atwarn(sid(id),w[i])
end
end
- atwarn(sid(id), attrans("ATC command parse error: Unknown command: @1", command))
- atc.train_reset_command(train, true)
+ if e then
+ atwarn(sid(id),e)
+ atc.train_reset_command(train, true)
+ end
end
diff --git a/advtrains/atcjit.lua b/advtrains/atcjit.lua
new file mode 100644
index 0000000..8e01239
--- /dev/null
+++ b/advtrains/atcjit.lua
@@ -0,0 +1,201 @@
+local aj_cache = {}
+
+local aj_tostring
+
+local matchptn = {
+ ["BB"] = function()
+ return [[do
+ train.atc_brake_target = -1
+ train.tarvelocity = 0
+ end]], 2
+ end,
+ ["B([0-9]+)"] = function(match)
+ return string.format([[do
+ train.atc_brake_target = %s
+ if not train.tarvelocity or train.tarvelocity > train.atc_brake_target then
+ train.tarvelocity = train.atc_brake_target
+ end
+ end]], match),#match+1
+ end,
+ ["D([0-9]+)"] = function(match)
+ return string.format("train.atc_delay = %s", match), #match+1, true
+ end,
+ ["(%bI;)"] = function(match)
+ local i = 2
+ local l = #match
+ local epos
+ while (i<l) do
+ local b, e, c = string.find(match,"([IE])",i)
+ if c == "I" then
+ b, e = string.find(match,"%bI;", b)
+ -- This is unlikely to happen since the %b pattern is balanced
+ if not (b and e) then return nil, "Malformed nested I statement" end
+ i = e+1
+ else
+ epos = b
+ break
+ end
+ end
+ local endp = string.find(match,"[WDI]") and true
+ local cond = string.match(match,"^I([+-])")
+ if cond then
+ local vtrue = string.sub(match, 3, epos and epos-1 or -2)
+ local vfalse = epos and string.sub(match, epos+1, -2)
+ local cstr = (cond == "-") and "not" or ""
+ local tstr, err = aj_tostring(vtrue, 1, true)
+ if not tstr then return nil, err end
+ if vfalse then
+ local fstr, err = aj_tostring(vfalse, 1, true)
+ if not fstr then return nil, err end
+ return string.format("if %s train.atc_arrow then %s else %s end",
+ cstr, tstr, fstr), l, endp
+ else
+ return string.format("if %s train.atc_arrow then %s end", cstr, tstr), l, endp
+ end
+ else
+ local op, ref = string.match(match,"^I([<>]=?)([0-9]+)")
+ if not op then
+ return _, "Invalid I statement"
+ end
+ local spos = 2+#op+#ref
+ local vtrue = string.sub(match, spos, epos and epos-1 or -2)
+ local vfalse = epos and string.sub(match, epos+1, -2)
+ local cstr = string.format("train.velocity %s %s", op, ref)
+ local tstr = aj_tostring(vtrue, 1, true)
+ if vfalse then
+ local fstr, err = aj_tostring(vfalse, 1, true)
+ if not fstr then return nil, err end
+ return string.format("if %s then %s else %s end", cstr, tstr, fstr), l, endp
+ else
+ return string.format("if %s then %s end", cstr, tstr), l, endp
+ end
+ end
+ end,
+ ["K"] = function()
+ return [=[do
+ if train.door_open == 0 then
+ _w[#_w+1] = attrans("ATC Kick command warning: Doors are closed")
+ elseif train.velocity>0 then
+ _w[#_w+1] = attrans("ATC Kick command warning: Train moving")
+ else
+ local tp = train.trainparts
+ for i = 1, #tp do
+ local data = advtrains.wagons[tp[i]]
+ local obj = advtrains.wagon_objects[tp[i]]
+ if data and obj then
+ local ent = obj:get_luaentity()
+ if ent then
+ for seatno, seat in pairs(ent.seats) do
+ if data.seatp[seatno] and not ent:is_driver_stand(seat) then
+ ent:get_off(seatno)
+ end
+ end
+ end
+ end
+ end
+ end
+ end]=], 1
+ end,
+ ["O([LR])"] = function(match)
+ local tt = {L = -1, R = 1}
+ return string.format("train.door_open = %d*(train.atc_arrow and 1 or -1)",tt[match]), 2
+ end,
+ ["OC"] = function(match)
+ return "train.door_open = 0", 2
+ end,
+ ["R"] = function()
+ return [[
+ if train.velocity<=0 then
+ advtrains.invert_train(id)
+ advtrains.train_ensure_init(id, train)
+ else
+ _w[#_w+1] = attrans("ATC Reverse command warning: didn't reverse train, train moving!")
+ end]], 1
+ end,
+ ["SM"] = function()
+ return "train.tarvelocity=train.max_speed", 2
+ end,
+ ["S([0-9]+)"] = function(match)
+ return string.format("train.tarvelocity=%s",match), #match+1
+ end,
+ ["W"] = function()
+ return "train.atc_wait_finish=true", 1, true
+ end,
+}
+
+local function aj_tostring_single(cmd, pos)
+ if not pos then pos = 1 end
+ for pattern, func in pairs(matchptn) do
+ local match = string.match(cmd, "^"..pattern, pos)
+ if match then
+ return func(match)
+ end
+ end
+ return nil
+end
+
+aj_tostring = function(cmd, pos, noreset)
+ if not pos then pos = 1 end
+ local t = {}
+ local endp = false
+ while pos <= #cmd do
+ if string.match(cmd,"^%s+$", pos) then break end
+ local _, e = string.find(cmd, "^%s+", pos)
+ if e then pos = e+1 end
+ local str, len
+ str, len, endp = aj_tostring_single(cmd, pos)
+ if not str then
+ return nil, (len or "Invalid command or malformed I statement: "..string.sub(cmd,pos))
+ end
+ t[#t+1] = str
+ pos = pos+len
+ if endp then
+ if endp then
+ t[#t+1] = string.format("_c[#_c+1]=%q",string.sub(cmd, pos))
+ end
+ break
+ end
+ end
+ return table.concat(t,"\n"), pos
+end
+
+local function aj_compile(cmd)
+ if aj_cache[cmd] then
+ local x = aj_cache[cmd]
+ if type(x) == "function" then
+ return x
+ else
+ return nil, x
+ end
+ end
+ local str, err = aj_tostring(cmd)
+ if not str then
+ aj_cache[cmd] = err
+ return nil, err
+ end
+ local str = string.format([[return function(id,train)
+ local _c = {}
+ local _w = {}
+ %s
+ if _c[1] then train.atc_command=table.concat(_c)
+ else train.atc_command = nil end
+ return _w, nil
+ end]], str)
+ local f, e = loadstring(str)
+ if not f then return nil, e end
+ f = f()
+ aj_cache[cmd] = f
+ return f
+end
+
+local function aj_execute(id,train)
+ if not train.atc_command then return end
+ local func, err = aj_compile(train.atc_command)
+ if func then return func(id,train) end
+ return nil, err
+end
+
+return {
+ compile = aj_compile,
+ execute = aj_execute
+}
diff --git a/advtrains/init.lua b/advtrains/init.lua
index 6e622e5..2c3c7fc 100644
--- a/advtrains/init.lua
+++ b/advtrains/init.lua
@@ -195,6 +195,8 @@ advtrains.meseconrules =
advtrains.fpath=minetest.get_worldpath().."/advtrains"
+advtrains.atcjit=dofile(advtrains.modpath.."/atcjit.lua")
+
dofile(advtrains.modpath.."/path.lua")
dofile(advtrains.modpath.."/trainlogic.lua")
dofile(advtrains.modpath.."/trainhud.lua")
--
2.30.0
---
advtrains/aj_unittest.lua | 15 +++++++++++
advtrains/atc.lua | 15 +++++++++--
advtrains/atcjit.lua | 57 ++++++++++++++++++++++-----------------
3 files changed, 60 insertions(+), 27 deletions(-)
diff --git a/advtrains/aj_unittest.lua b/advtrains/aj_unittest.lua
index 9dacbc3..a660131 100644
--- a/advtrains/aj_unittest.lua
+++ b/advtrains/aj_unittest.lua
@@ -153,3 +153,18 @@ describe("ATC track with invalid I condition", function()
local t = { atc_command = "I?;" }
thisatc("should report an error", t, {}, "Invalid I statement", t)
end)
+
+describe("ATC track reusing existing code", function()
+ local t = { atc_command = " B12WB8WBBWOLD15ORD15OCD1RS10WSM", tarvelocity = 15 }
+ thisatc("should do the same thing as in the first test", t, {}, nil, {
+ atc_brake_target = 12,
+ atc_command = "B8WBBWOLD15ORD15OCD1RS10WSM",
+ atc_wait_finish = true,
+ tarvelocity = 12
+ })
+end)
+
+describe("ATC track reusing malformed code", function()
+ local t = {atc_command = "I?;"}
+ thisatc("Should report the invalid I statement", t, {}, "Invalid I statement", t)
+end)
diff --git a/advtrains/atc.lua b/advtrains/atc.lua
index eff6420..186c506 100644
--- a/advtrains/atc.lua
+++ b/advtrains/atc.lua
@@ -174,7 +174,6 @@ function atc.get_atc_controller_formspec(pos, meta)
return formspec.."button_exit[0.5,4.5;7,1;save;"..attrans("Save").."]"
end
-
function atc.execute_atc_command(id, train)
local w, e = advtrains.atcjit.execute(id, train)
if w then
@@ -188,7 +187,19 @@ function atc.execute_atc_command(id, train)
end
end
-
+minetest.register_chatcommand("at_get_lua",{
+ params = "<command>",
+ description = "Compile the given ATC command into Lua code and show the given code",
+ privs = {train_admin = true},
+ func = function(name, params)
+ local f, s = advtrains.atcjit.compile(params)
+ if not f then
+ return false, s or ("Unknown compilation error")
+ else
+ return true, s
+ end
+ end,
+})
--move table to desired place
advtrains.atc=atc
diff --git a/advtrains/atcjit.lua b/advtrains/atcjit.lua
index 7d4af04..52a03c2 100644
--- a/advtrains/atcjit.lua
+++ b/advtrains/atcjit.lua
@@ -1,4 +1,5 @@
local aj_cache = {}
+local aj_strout = {}
local aj_tostring
@@ -150,8 +151,9 @@ aj_tostring = function(cmd, pos, noreset)
t[#t+1] = str
pos = pos+len
if endp then
- if endp then
- t[#t+1] = string.format("_c[#_c+1]=%q",string.sub(cmd, pos))
+ local cont = string.sub(cmd, pos)
+ if not string.match(cont, "^%s*$") then
+ t[#t+1] = string.format("_c[#_c+1]=%q",cont)
end
break
end
@@ -160,32 +162,37 @@ aj_tostring = function(cmd, pos, noreset)
end
local function aj_compile(cmd)
- if aj_cache[cmd] then
- local x = aj_cache[cmd]
- if type(x) == "function" then
- return x
+ local c = aj_cache[cmd]
+ if c then
+ if type(c) == "function" then
+ return c, aj_strout[cmd]
else
- return nil, x
+ return nil, c
end
+ else
+ local str, err = aj_tostring(cmd)
+ if not str then
+ aj_cache[cmd] = err
+ return nil, err
+ end
+ str = string.format([[return function(id, train)
+ local _c={}
+ local _w={}
+ %s
+ if _c[1] then train.atc_command=table.concat(_c)
+ else train.atc_command=nil end
+ return _w, nil
+ end]], str)
+ local f, e = loadstring(str)
+ if not f then
+ aj_cache[cmd] = e
+ return nil, e
+ end
+ f = f()
+ aj_cache[cmd] = f
+ aj_strout[cmd] = str
+ return f, str
end
- local str, err = aj_tostring(cmd)
- if not str then
- aj_cache[cmd] = err
- return nil, err
- end
- local str = string.format([[return function(id,train)
- local _c = {}
- local _w = {}
- %s
- if _c[1] then train.atc_command=table.concat(_c)
- else train.atc_command = nil end
- return _w, nil
- end]], str)
- local f, e = loadstring(str)
- if not f then return nil, e end
- f = f()
- aj_cache[cmd] = f
- return f
end
local function aj_execute(id,train)
--
2.30.0
---
advtrains/atc.lua | 7 +--
advtrains/atcjit.lua | 102 ++++++++++++++++++++++++-------------------
2 files changed, 58 insertions(+), 51 deletions(-)
diff --git a/advtrains/atc.lua b/advtrains/atc.lua
index 186c506..7b1ec2f 100644
--- a/advtrains/atc.lua
+++ b/advtrains/atc.lua
@@ -175,12 +175,7 @@ function atc.get_atc_controller_formspec(pos, meta)
end
function atc.execute_atc_command(id, train)
- local w, e = advtrains.atcjit.execute(id, train)
- if w then
- for i = 1, #w, 1 do
- atwarn(sid(id),w[i])
- end
- end
+ local _, e = advtrains.atcjit.execute(id, train)
if e then
atwarn(sid(id),e)
atc.train_reset_command(train, true)
diff --git a/advtrains/atcjit.lua b/advtrains/atcjit.lua
index 52a03c2..e3df1d1 100644
--- a/advtrains/atcjit.lua
+++ b/advtrains/atcjit.lua
@@ -3,25 +3,39 @@ local aj_strout = {}
local aj_tostring
+--[[ Notes on the pattern matching functions:
+- Patterns can have multiple captures (e.g. coordinates). These captures
+are passed starting from the second argument.
+- The commands after the match are passed as the first argument. If the
+command involves waiting, it should:
+ - Set train.atc_command to the first argument passed to the function,
+ - End with "do return end", and
+ - Return true as the second argument
+- The function should work on
+]]
local matchptn = {
["BB"] = function()
return [[do
train.atc_brake_target = -1
train.tarvelocity = 0
- end]], 2
+ end]]
end,
- ["B([0-9]+)"] = function(match)
+ ["B([0-9]+)"] = function(_, match)
return string.format([[do
train.atc_brake_target = %s
if not train.tarvelocity or train.tarvelocity > train.atc_brake_target then
train.tarvelocity = train.atc_brake_target
end
- end]], match),#match+1
+ end]], match)
end,
- ["D([0-9]+)"] = function(match)
- return string.format("train.atc_delay = %s", match), #match+1, true
+ ["D([0-9]+)"] = function(cont, match)
+ return string.format([[do
+ train.atc_delay = %s
+ train.atc_command = %q
+ return
+ end]], match, cont), true
end,
- ["(%bI;)"] = function(match)
+ ["(%bI;)"] = function(cont, match)
local i = 2
local l = #match
local epos
@@ -43,15 +57,15 @@ local matchptn = {
local vtrue = string.sub(match, 3, epos and epos-1 or -2)
local vfalse = epos and string.sub(match, epos+1, -2)
local cstr = (cond == "-") and "not" or ""
- local tstr, err = aj_tostring(vtrue, 1, true)
+ local tstr, err = aj_tostring(vtrue, 1, cont)
if not tstr then return nil, err end
if vfalse then
- local fstr, err = aj_tostring(vfalse, 1, true)
+ local fstr, err = aj_tostring(vfalse, 1, cont)
if not fstr then return nil, err end
return string.format("if %s train.atc_arrow then %s else %s end",
- cstr, tstr, fstr), l, endp
+ cstr, tstr, fstr)
else
- return string.format("if %s train.atc_arrow then %s end", cstr, tstr), l, endp
+ return string.format("if %s train.atc_arrow then %s end", cstr, tstr)
end
else
local op, ref = string.match(match,"^I([<>]=?)([0-9]+)")
@@ -62,22 +76,22 @@ local matchptn = {
local vtrue = string.sub(match, spos, epos and epos-1 or -2)
local vfalse = epos and string.sub(match, epos+1, -2)
local cstr = string.format("train.velocity %s %s", op, ref)
- local tstr = aj_tostring(vtrue, 1, true)
+ local tstr = aj_tostring(vtrue, 1, cont)
if vfalse then
- local fstr, err = aj_tostring(vfalse, 1, true)
+ local fstr, err = aj_tostring(vfalse, 1, cont)
if not fstr then return nil, err end
- return string.format("if %s then %s else %s end", cstr, tstr, fstr), l, endp
+ return string.format("if %s then %s else %s end", cstr, tstr, fstr)
else
- return string.format("if %s then %s end", cstr, tstr), l, endp
+ return string.format("if %s then %s end", cstr, tstr)
end
end
end,
["K"] = function()
return [=[do
if train.door_open == 0 then
- _w[#_w+1] = attrans("ATC Kick command warning: Doors are closed")
+ atwarn(sid(id),attrans("ATC Kick command warning: Doors are closed"))
elseif train.velocity>0 then
- _w[#_w+1] = attrans("ATC Kick command warning: Train moving")
+ atwarn(sid(id),attrans("ATC Kick command warning: Train moving"))
else
local tp = train.trainparts
for i = 1, #tp do
@@ -95,14 +109,14 @@ local matchptn = {
end
end
end
- end]=], 1
+ end]=]
end,
- ["O([LR])"] = function(match)
+ ["O([LR])"] = function(_, match)
local tt = {L = -1, R = 1}
- return string.format("train.door_open = %d*(train.atc_arrow and 1 or -1)",tt[match]), 2
+ return string.format("train.door_open = %d*(train.atc_arrow and 1 or -1)",tt[match])
end,
- ["OC"] = function(match)
- return "train.door_open = 0", 2
+ ["OC"] = function()
+ return "train.door_open = 0"
end,
["R"] = function()
return [[
@@ -110,32 +124,38 @@ local matchptn = {
advtrains.invert_train(id)
advtrains.train_ensure_init(id, train)
else
- _w[#_w+1] = attrans("ATC Reverse command warning: didn't reverse train, train moving!")
- end]], 1
+ atwarn(sid(id),attrans("ATC Reverse command warning: didn't reverse train, train moving!"))
+ end]]
end,
["SM"] = function()
- return "train.tarvelocity=train.max_speed", 2
+ return "train.tarvelocity=train.max_speed"
end,
- ["S([0-9]+)"] = function(match)
- return string.format("train.tarvelocity=%s",match), #match+1
+ ["S([0-9]+)"] = function(_, match)
+ return string.format("train.tarvelocity=%s",match)
end,
- ["W"] = function()
- return "train.atc_wait_finish=true", 1, true
+ ["W"] = function(cont)
+ return string.format([[do
+ train.atc_wait_finish=true
+ train.atc_command=%q
+ return
+ end]], cont), true
end,
}
-local function aj_tostring_single(cmd, pos)
+local function aj_tostring_single(cmd, pos, cont)
if not pos then pos = 1 end
for pattern, func in pairs(matchptn) do
- local match = {string.match(cmd, "^"..pattern, pos)}
+ local match = {string.find(cmd, "^"..pattern, pos)}
if match[1] then
- return func(unpack(match))
+ local e = match[2]
+ match[2] = string.sub(cmd, e+1)..(cont or "")
+ return e+1, func(unpack(match, 2))
end
end
return nil
end
-aj_tostring = function(cmd, pos, noreset)
+aj_tostring = function(cmd, pos, cont)
if not pos then pos = 1 end
local t = {}
local endp = false
@@ -143,18 +163,14 @@ aj_tostring = function(cmd, pos, noreset)
if string.match(cmd,"^%s+$", pos) then break end
local _, e = string.find(cmd, "^%s+", pos)
if e then pos = e+1 end
- local str, len
- str, len, endp = aj_tostring_single(cmd, pos)
+ local nxt, str
+ nxt, str, endp = aj_tostring_single(cmd, pos, cont or "")
if not str then
- return nil, (len or "Invalid command or malformed I statement: "..string.sub(cmd,pos))
+ return nil, (endp or "Invalid command or malformed I statement: "..string.sub(cmd,pos))
end
t[#t+1] = str
- pos = pos+len
+ pos = nxt
if endp then
- local cont = string.sub(cmd, pos)
- if not string.match(cont, "^%s*$") then
- t[#t+1] = string.format("_c[#_c+1]=%q",cont)
- end
break
end
end
@@ -176,12 +192,8 @@ local function aj_compile(cmd)
return nil, err
end
str = string.format([[return function(id, train)
- local _c={}
- local _w={}
%s
- if _c[1] then train.atc_command=table.concat(_c)
- else train.atc_command=nil end
- return _w, nil
+ train.atc_command=nil
end]], str)
local f, e = loadstring(str)
if not f then
--
2.30.0