Lunatic translator: switch statement.

git-svn-id: https://svn.eduke32.com/eduke32@3513 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2013-02-21 18:54:02 +00:00
parent e6fd95dde8
commit a83b2fee95
2 changed files with 112 additions and 23 deletions

View file

@ -656,6 +656,15 @@ function _qgetsysstr(qdst, what, pli)
end
-- switch statement support
function _switch(swtab, testval, aci,pli,dist)
local func = swtab[testval] or swtab.default
if (func) then
func(aci, pli, dist)
end
end
-- text rendering
function _minitext(x, y, qnum, shade, pal)
local cstr = bcheck.quote_idx(qnum)

View file

@ -115,6 +115,10 @@ GVFLAG.USER_MASK = GVFLAG.PERX_MASK + GVFLAG.NODEFAULT + GVFLAG.NORESET
-- CON --> mangled Lua function name, also existence check:
local g_funcname = {}
-- while parsing a block, it is a table of "gencode" tables:
local g_switchCode = nil
-- Global number of switch statements:
local g_switchCount = 0
-- [identifier] = { name=<mangled name / code>, flags=<gamevar flags> }
local g_gamevar = {}
-- [identifier] = { name=<mangled name / code>, size=<initial size> }
@ -140,7 +144,10 @@ local function new_initial_codetab()
"local _xmath, _geom = require'xmath', require'geom';",
"local sector, sprite, actor, player = sector, sprite, actor, player;",
"local gameactor, gameevent, _gv = gameactor, gameevent, gv;",
"local updatesector, updatesectorz, cansee = updatesector, updatesectorz, cansee;"
"local updatesector, updatesectorz, cansee = updatesector, updatesectorz, cansee;",
-- switch function table, indexed by global switch sequence number:
"local _SW = {};",
}
end
@ -249,6 +256,8 @@ end
local function reset_codegen()
g_funcname = {}
g_switchCode = nil
g_switchCount = 0
g_gamevar = new_initial_gvartab()
g_gamearray = {}
@ -275,7 +284,9 @@ local function add_code_and_end(codetab, endstr)
addcode(endstr)
end
local function on_actor_end(usertype, tsamm, codetab)
local on = {}
function on.actor_end(usertype, tsamm, codetab)
local tilenum = tsamm[1]
local str = ""
@ -310,7 +321,7 @@ local function mangle_name(name, prefix)
return prefix..name
end
local function on_state_begin_Cmt(_subj, _pos, statename)
function on.state_begin_Cmt(_subj, _pos, statename)
-- We must register the state name early (Cmt) because otherwise, it won't
-- be found in a recursive state. XXX: The real issue seems to be the use
-- of "Cmt"s in other places, which messes up the sequence of running the
@ -320,12 +331,12 @@ local function on_state_begin_Cmt(_subj, _pos, statename)
return true, ourname
end
local function on_state_end(funcname, codetab)
function on.state_end(funcname, codetab)
addcodef("local function %s(_aci, _pli, _dist)", funcname)
add_code_and_end(codetab, "end")
end
local function on_event_end(eventidx, codetab)
function on.event_end(eventidx, codetab)
addcodef("gameevent(%d, function (_aci, _pli, _dist)", eventidx)
add_code_and_end(codetab, "end)")
@ -2248,9 +2259,10 @@ end
-- attach the command names at the front!
local function attachnames(kwtab, matchtimefunc)
for cmdname,cmdpat in pairs(kwtab) do
-- The match-time function capture at the end is so that every
-- command acts as a barrier to captures to prevent stack overflow (and
-- to make lpeg.match return a subject position at the end)
-- The match-time function capture at the end is so that every command
-- acts as a barrier to captures to delay (but not fully prevent) stack
-- overflow (and to make lpeg.match return a subject position at the
-- end)
kwtab[cmdname] = lpeg.Cmt(Keyw(cmdname) * cmdpat, matchtimefunc)
end
end
@ -2305,10 +2317,13 @@ local lone_else = (POS() * "else" * sp1)/warn_on_lonely_else
local stmt_list = Var("stmt_list")
-- possibly empty statement list:
local stmt_list_or_eps = lpeg.Ct((stmt_list * sp1)^-1)
local stmt_list_nosp_or_eps = (stmt_list * (sp1 * stmt_list)^0)^-1
local stmt_list_nosp_or_eps = lpeg.Ct((stmt_list * (sp1 * stmt_list)^0)^-1)
-- Reused LPeg patterns
local common = {}
-- common to actor and useractor: <name/tilenum> [<strength> [<action> [<move> [<flags>... ]]]]
local common_actor_end = sp1 * lpeg.Ct(tok.define *
common.actor_end = sp1 * lpeg.Ct(tok.define *
(sp1 * tok.define *
(sp1 * tok.action *
(sp1 * tok.move *
@ -2318,23 +2333,38 @@ local common_actor_end = sp1 * lpeg.Ct(tok.define *
)^-1)
* sp1 * stmt_list_or_eps * "enda"
common.block_begin = lpeg.Cc(nil) / function()
g_switchCode = {}
end
common.block_end = lpeg.Cc(nil) / function()
if (#g_switchCode > 0) then
addcode(g_switchCode)
end
g_switchCode = nil
end
--== block delimiters (no syntactic recursion) ==--
local Cblock = {
-- actor (...)
actor = lpeg.Cc(nil) * common_actor_end / on_actor_end,
actor = lpeg.Cc(nil) * common.actor_end / on.actor_end,
-- useractor <actortype> (...)
useractor = sp1 * tok.define * common_actor_end / on_actor_end,
useractor = sp1 * tok.define * common.actor_end / on.actor_end,
-- eventloadactor <name/tilenum>
eventloadactor = lpeg.Cc(nil) * sp1 * lpeg.Ct(tok.define)
* sp1 * stmt_list_or_eps * "enda" / on_actor_end,
* sp1 * stmt_list_or_eps * "enda" / on.actor_end,
onevent = sp1 * tok.define * sp1 * stmt_list_or_eps * "endevent"
/ on_event_end,
/ on.event_end,
state = sp1 * (lpeg.Cmt(tok.identifier, on_state_begin_Cmt)) * sp1 * stmt_list_or_eps * tok.state_ends
/ on_state_end,
state = sp1 * (lpeg.Cmt(tok.identifier, on.state_begin_Cmt)) * sp1 * stmt_list_or_eps * tok.state_ends
/ on.state_end,
}
for cmdname, cmdpat in pairs(Cblock) do
Cblock[cmdname] = common.block_begin * cmdpat * common.block_end
end
attachnames(Cblock, after_cmd_Cmt)
@ -2399,6 +2429,54 @@ local function end_if_else_fn()
return get_deferred_code(g_endIfElseCode, g_ifelselevel, "end ")
end
function on.switch_end(testvar, blocks)
local SW = format("_SW[%d]", g_switchCount)
local swcode = { format("%s={", SW) }
local have = {}
local havedefault = false
for i=1,#blocks do
local block = blocks[i]
assert(#block >= 1)
local isdefault = (#block==1)
local index = isdefault and "'default'" or tostring(block[1])
if (have[index]) then
if (isdefault) then
errprintf("duplicate 'default' block in switch statement")
return "_INVALIDSW()"
else
warnprintf("duplicate case %s in switch statement", index)
end
end
have[index] = true
swcode[#swcode+1] = format("[%s]=function(_aci,_pli,_dist)", index)
-- insert the case/default code:
swcode[#swcode+1] = block[#block]
swcode[#swcode+1] = "end,"
end
swcode[#swcode+1] = "}"
-- insert additional case test numbers (e.g. case 0: >>> case 1 <<<: <code...>)
for i=1,#blocks do
local block = blocks[i]
for j=2,#block-1 do
local index = tostring(block[j])
swcode[#swcode+1] = format("%s[%d]=%s[%d]", SW, index, SW, tostring(block[1]))
end
end
assert(g_switchCode ~= nil)
g_switchCode[#g_switchCode+1] = swcode
-- The code for the switch statement itself:
local code = format("_con._switch(_SW[%d], %s, _aci,_pli,_dist)", g_switchCount, testvar)
g_switchCount = g_switchCount+1
return code
end
--- The final grammar!
local Grammar = Pat{
@ -2465,13 +2543,15 @@ local Grammar = Pat{
-- SWITCH
switch_stmt = Keyw("switch") * sp1 * tok.rvar *
(Var("case_block") + Var("default_block"))^0 * sp1 * "endswitch",
lpeg.Ct((Var("case_block") + Var("default_block"))^0) * sp1 * "endswitch"
/ on.switch_end,
-- NOTE: some old DNWMD has "case: PIGCOP". I don't think I'll allow that.
case_block = (sp1 * Keyw("case") * sp1 * tok.define/"XXX_CASE" * (sp0*":")^-1)^1 * sp1 *
stmt_list_nosp_or_eps, -- * "break",
case_block = lpeg.Ct((sp1 * Keyw("case") * sp1 * tok.define * (sp0*":")^-1)^1 * sp1 *
stmt_list_nosp_or_eps), -- * "break",
default_block = sp1 * Keyw("default") * (sp0*":"*sp0 + sp1) * stmt_list_nosp_or_eps, -- * "break",
default_block = lpeg.Ct(sp1 * Keyw("default") * (sp0*":"*sp0 + sp1) *
stmt_list_nosp_or_eps), -- * "break",
optional_else = (sp1 * lpeg.C("else") * sp1 * Var("single_stmt"))^-1,
@ -2550,7 +2630,7 @@ local function get_code_string(codetab)
return table.concat(flatten_codetab(g_curcode), "\n")
end
local function on_parse_begin()
function on.parse_begin()
g_iflevel = 0
g_ifelselevel = 0
g_have_file[g_filename] = true
@ -2569,7 +2649,7 @@ function parse(contents) -- local
local lastkw, lastkwpos, numerrors = g_lastkw, g_lastkwpos, g_numerrors
local newlineidxs = g_newlineidxs
on_parse_begin()
on.parse_begin()
g_newlineidxs = setup_newlineidxs(contents)
@ -2614,7 +2694,7 @@ function parse(contents) -- local
-- restore outer state
g_lastkw, g_lastkwpos = lastkw, lastkwpos
g_numerrors = (g_numerrors==inf and inf) or numerrors
g_numerrors = math.max(g_numerrors, numerrors)
g_newlineidxs = newlineidxs
end