From 677c585b72c485e8343f75c42d0837f70a3cc71f Mon Sep 17 00:00:00 2001 From: helixhorned Date: Thu, 28 Feb 2013 17:29:58 +0000 Subject: [PATCH] Lunatic translator: fix codegen for "break" inside while loops. Note: usually, languages call this "continue". Jeez, CON... git-svn-id: https://svn.eduke32.com/eduke32@3529 1a8010ca-5511-0410-912e-c29ae57300e0 --- polymer/eduke32/source/lunatic/control.lua | 2 + polymer/eduke32/source/lunatic/defs.ilua | 1 + polymer/eduke32/source/lunatic/lunacon.lua | 72 +++++++++--- .../eduke32/source/lunatic/test/switch.con | 110 ++++++++++++++++++ 4 files changed, 167 insertions(+), 18 deletions(-) create mode 100644 polymer/eduke32/source/lunatic/test/switch.con diff --git a/polymer/eduke32/source/lunatic/control.lua b/polymer/eduke32/source/lunatic/control.lua index d14c09abe..9271aa0fd 100644 --- a/polymer/eduke32/source/lunatic/control.lua +++ b/polymer/eduke32/source/lunatic/control.lua @@ -26,6 +26,8 @@ local tostring = tostring local type = type local unpack = unpack +local format = require("string").format + local actor, player = assert(actor), assert(player) local dc = require("defs_common") local cansee, hitscan, neartag = dc.cansee, dc.hitscan, dc.neartag diff --git a/polymer/eduke32/source/lunatic/defs.ilua b/polymer/eduke32/source/lunatic/defs.ilua index dc6e213e1..9514f9c4e 100644 --- a/polymer/eduke32/source/lunatic/defs.ilua +++ b/polymer/eduke32/source/lunatic/defs.ilua @@ -654,6 +654,7 @@ player_static_members.INPUT_EXT_BITS = defs_c.conststruct INPUT_TURN_RIGHT = 32, } +-- XXX: error message will say "g_player_ps" player = setmtonce({}, defs_c.GenStructMetatable("g_player_ps", "playerswhenstarted", player_static_members)) -- needed by "control" diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index 4d6a640dd..e5e08d67a 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -100,6 +100,11 @@ local g_cgopt = { ["no"]=false, } -- needed to cope with CONs dangling-else resolution local g_iflevel = 0 local g_ifelselevel = 0 +-- Stack with *true* on top if the innermost block is a "whilevar*n". +local g_isWhile = {} +-- Sequence number of 'while' statements, used to implement CON "break" inside +-- whilevar*n, which really behaves like what sane languages call "continue"... +local g_whilenum = 0 ---=== Code generation ===--- local GVFLAG = { @@ -1611,7 +1616,11 @@ local userdef_common_pat = (arraypat + sp1)/{} * lpeg.Cc(0) * lpeg.Ct(singlememb local Cinner = { -- these can appear anywhere in the script ["break"] = cmd() - / "do return end", + / function() + return g_isWhile[#g_isWhile] + and format("goto l%d", g_whilenum) + or "do return end" + end, ["return"] = cmd() -- NLCF / "_con.longjmp()", @@ -1678,7 +1687,7 @@ local Cinner = { xorvar = varopf "_bit.bxor", randvar = varop / "%1=_con._rand(%2)", shiftvarl = varopf "_bit.lshift", - shiftvarr = varopf "_bit.rshift", + shiftvarr = varopf "_bit.arshift", --- 2. Math operations sqrt = cmd(R,W) @@ -2378,7 +2387,7 @@ end -- if desired. local function Keyw(kwname) return TraceFunc(kwname, "kw", false) end local function NotKeyw(text) return TraceFunc(text, "!kw", false) end -local function Ident(idname) return TraceFunc(idname, "id", false) end +--local function Ident(idname) return TraceFunc(idname, "id", false) end local function Stmt(cmdpat) return TraceFunc(cmdpat, "st", false) end --local function Temp(kwname) return TraceFunc(kwname, "temp", true) end @@ -2433,7 +2442,14 @@ local function attachnames(kwtab, matchtimefunc) -- 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) + local newpat = Keyw(cmdname) * cmdpat + if (cmdname~="break") then + kwtab[cmdname] = lpeg.Cmt(newpat, matchtimefunc) + else + -- Must not attack a Cmt to "break" because it would break the + -- while/switch sequencing. + kwtab[cmdname] = newpat + end end end @@ -2574,7 +2590,7 @@ local function get_deferred_code(tab, lev, code) return code end -local function begin_if_fn(condstr, endifstr, endifelsestr) +function on.if_begin(condstr, endifstr, endifelsestr) assert(type(condstr)=="string") add_deferred_code(g_endIfCode, g_iflevel, endifstr) @@ -2586,7 +2602,7 @@ local function begin_if_fn(condstr, endifstr, endifelsestr) return format("if (%s) then", condstr) end -local function end_if_fn() +function on.if_end() g_iflevel = g_iflevel-1 local code = get_deferred_code(g_endIfCode, g_iflevel, "") if (code ~= "") then @@ -2594,17 +2610,35 @@ local function end_if_fn() end end -local function end_if_else_fn() +function on.if_else_end() g_ifelselevel = g_ifelselevel-1 return get_deferred_code(g_endIfElseCode, g_ifelselevel, "end ") end +function on.while_begin(v1, v2) + table.insert(g_isWhile, true) + return format("while (%s~=%s) do", v1, v2) +end + +function on.while_end() + table.remove(g_isWhile) + local code=format("::l%d:: end", g_whilenum) + g_whilenum = g_whilenum+1 + return code +end + +function on.switch_begin() + table.insert(g_isWhile, false) +end + function on.switch_end(testvar, blocks) local SW = format("_SW[%d]", g_switchCount) local swcode = { format("%s={", SW) } local have = {} local havedefault = false + table.remove(g_isWhile) + for i=1,#blocks do local block = blocks[i] assert(#block >= 1) @@ -2712,7 +2746,7 @@ local Grammar = Pat{ t_singlearrayexp = tok.identifier * arraypat * singlememberpat^-1, -- SWITCH - switch_stmt = Keyw("switch") * sp1 * tok.rvar * + switch_stmt = Keyw("switch") * sp1 * tok.rvar * (lpeg.Cc(nil)/on.switch_begin) * lpeg.Ct((Var("case_block") + Var("default_block"))^0) * sp1 * "endswitch" / on.switch_end, @@ -2725,21 +2759,21 @@ local Grammar = Pat{ optional_else = (sp1 * lpeg.C("else") * sp1 * Var("single_stmt"))^-1, - if_else_bodies = Var("single_stmt2") * (Pat("")/end_if_fn) * Var("optional_else"), + if_else_bodies = Var("single_stmt2") * (Pat("")/on.if_end) * Var("optional_else"), - if_stmt = con_if_begs/begin_if_fn * sp1 + if_stmt = con_if_begs/on.if_begin * sp1 * Var("if_else_bodies") - * (Pat("")/end_if_else_fn), + * (Pat("")/on.if_else_end), - if_stmt2 = con_if_begs/begin_if_fn * sp1 - * (-con_if_begs * Var("single_stmt") * (Pat("")/end_if_fn) + if_stmt2 = con_if_begs/on.if_begin * sp1 + * (-con_if_begs * Var("single_stmt") * (Pat("")/on.if_end) + Var("if_else_bodies")) - * (Pat("")/end_if_else_fn), + * (Pat("")/on.if_else_end), - while_stmt = Keyw("whilevarvarn") * sp1 * tok.rvar * sp1 * tok.rvar / "while (%1~=%2) do" - * sp1 * Var("single_stmt") * lpeg.Cc("end") - + Keyw("whilevarn") * sp1 * tok.rvar * sp1 * tok.define / "while (%1~=%2) do" - * sp1 * Var("single_stmt") * lpeg.Cc("end"), + while_stmt = Keyw("whilevarvarn") * sp1 * tok.rvar * sp1 * tok.rvar / on.while_begin + * sp1 * Var("single_stmt") * (lpeg.Cc(nil) / on.while_end) + + Keyw("whilevarn") * sp1 * tok.rvar * sp1 * tok.define / on.while_begin + * sp1 * Var("single_stmt") * (lpeg.Cc(nil) / on.while_end), stmt_common = Keyw("{") * sp1 * "}" -- space separation of commands in CON is for a reason! + Keyw("{") * sp1 * stmt_list * sp1 * "}" @@ -2803,6 +2837,8 @@ end function on.parse_begin() g_iflevel = 0 g_ifelselevel = 0 + g_isWhile = {} + g_whilenum = 0 g_have_file[g_filename] = true -- set up new state diff --git a/polymer/eduke32/source/lunatic/test/switch.con b/polymer/eduke32/source/lunatic/test/switch.con new file mode 100644 index 000000000..fb3be3d63 --- /dev/null +++ b/polymer/eduke32/source/lunatic/test/switch.con @@ -0,0 +1,110 @@ +gamevar j 0 0 +gamevar k 0 0 + +definequote 400 QWE + +definequote 500 === + +onevent EVENT_ENTERLEVEL + switch j + case 0 + case -1 + userquote 29 + break + case 1 + userquote 30 + break + default + userquote 31 + break + case 0 + redefinequote 400 ASD + userquote 400 + break + endswitch + + addvar j 1 + + switch j + case 0 + case -2 + userquote 29 + break + case 3 + userquote 30 + break + default + userquote 31 + break + endswitch + + // test "-" codegen (must not generate comment!). + subvar j -3 + + // result: + // ASD (from second case 0) + // BRIGHTNESS LEVEL: THREE (from default) + + + ////////// test nested switch/while ////////// + + ifvare 0 1 + break // outer level: "do return end" + + setvar j 0 + whilevarn j 3 + { + addvar j 1 + userquote 500 + break // while is inner: "continue" + } // 3x "===" + + setvar j 0 + whilevarn j 1 + { + switch j + case 0 + addvar j 1 + // switch is inner: "do return end" (likewise with following + // "break" commands): + break + addvar j 1 + break + case 1 + nullop + break + default + nullop + break + endswitch + } + + // NOTE: this is syntactically invalid, though may be useful: + // case X: + // default: + // + + setvar j 0 + switch j + case 0, + whilevarn j 1 + { + addvar j 1 + break // while is inner: "continue" + } + break + endswitch +/* + // LunaCON only: nested switches. + // Untested, but the generated code looks sane. + switch j + case 0, + switch k + default + userquote 29 // BRIGHTNESS LEVEL: ONE + endswitch + case 1, + userquote 30 + endswitch +*/ +endevent