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
This commit is contained in:
helixhorned 2013-02-28 17:29:58 +00:00
parent d747e26fd0
commit 677c585b72
4 changed files with 167 additions and 18 deletions

View file

@ -26,6 +26,8 @@ local tostring = tostring
local type = type local type = type
local unpack = unpack local unpack = unpack
local format = require("string").format
local actor, player = assert(actor), assert(player) local actor, player = assert(actor), assert(player)
local dc = require("defs_common") local dc = require("defs_common")
local cansee, hitscan, neartag = dc.cansee, dc.hitscan, dc.neartag local cansee, hitscan, neartag = dc.cansee, dc.hitscan, dc.neartag

View file

@ -654,6 +654,7 @@ player_static_members.INPUT_EXT_BITS = defs_c.conststruct
INPUT_TURN_RIGHT = 32, INPUT_TURN_RIGHT = 32,
} }
-- XXX: error message will say "g_player_ps"
player = setmtonce({}, defs_c.GenStructMetatable("g_player_ps", "playerswhenstarted", player_static_members)) player = setmtonce({}, defs_c.GenStructMetatable("g_player_ps", "playerswhenstarted", player_static_members))
-- needed by "control" -- needed by "control"

View file

@ -100,6 +100,11 @@ local g_cgopt = { ["no"]=false, }
-- needed to cope with CONs dangling-else resolution -- needed to cope with CONs dangling-else resolution
local g_iflevel = 0 local g_iflevel = 0
local g_ifelselevel = 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 ===--- ---=== Code generation ===---
local GVFLAG = { local GVFLAG = {
@ -1611,7 +1616,11 @@ local userdef_common_pat = (arraypat + sp1)/{} * lpeg.Cc(0) * lpeg.Ct(singlememb
local Cinner = { local Cinner = {
-- these can appear anywhere in the script -- these can appear anywhere in the script
["break"] = cmd() ["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 ["return"] = cmd() -- NLCF
/ "_con.longjmp()", / "_con.longjmp()",
@ -1678,7 +1687,7 @@ local Cinner = {
xorvar = varopf "_bit.bxor", xorvar = varopf "_bit.bxor",
randvar = varop / "%1=_con._rand(%2)", randvar = varop / "%1=_con._rand(%2)",
shiftvarl = varopf "_bit.lshift", shiftvarl = varopf "_bit.lshift",
shiftvarr = varopf "_bit.rshift", shiftvarr = varopf "_bit.arshift",
--- 2. Math operations --- 2. Math operations
sqrt = cmd(R,W) sqrt = cmd(R,W)
@ -2378,7 +2387,7 @@ end
-- if desired. -- if desired.
local function Keyw(kwname) return TraceFunc(kwname, "kw", false) end local function Keyw(kwname) return TraceFunc(kwname, "kw", false) end
local function NotKeyw(text) return TraceFunc(text, "!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 Stmt(cmdpat) return TraceFunc(cmdpat, "st", false) end
--local function Temp(kwname) return TraceFunc(kwname, "temp", true) 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 -- 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 -- overflow (and to make lpeg.match return a subject position at the
-- end) -- 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
end end
@ -2574,7 +2590,7 @@ local function get_deferred_code(tab, lev, code)
return code return code
end end
local function begin_if_fn(condstr, endifstr, endifelsestr) function on.if_begin(condstr, endifstr, endifelsestr)
assert(type(condstr)=="string") assert(type(condstr)=="string")
add_deferred_code(g_endIfCode, g_iflevel, endifstr) 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) return format("if (%s) then", condstr)
end end
local function end_if_fn() function on.if_end()
g_iflevel = g_iflevel-1 g_iflevel = g_iflevel-1
local code = get_deferred_code(g_endIfCode, g_iflevel, "") local code = get_deferred_code(g_endIfCode, g_iflevel, "")
if (code ~= "") then if (code ~= "") then
@ -2594,17 +2610,35 @@ local function end_if_fn()
end end
end end
local function end_if_else_fn() function on.if_else_end()
g_ifelselevel = g_ifelselevel-1 g_ifelselevel = g_ifelselevel-1
return get_deferred_code(g_endIfElseCode, g_ifelselevel, "end ") return get_deferred_code(g_endIfElseCode, g_ifelselevel, "end ")
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) function on.switch_end(testvar, blocks)
local SW = format("_SW[%d]", g_switchCount) local SW = format("_SW[%d]", g_switchCount)
local swcode = { format("%s={", SW) } local swcode = { format("%s={", SW) }
local have = {} local have = {}
local havedefault = false local havedefault = false
table.remove(g_isWhile)
for i=1,#blocks do for i=1,#blocks do
local block = blocks[i] local block = blocks[i]
assert(#block >= 1) assert(#block >= 1)
@ -2712,7 +2746,7 @@ local Grammar = Pat{
t_singlearrayexp = tok.identifier * arraypat * singlememberpat^-1, t_singlearrayexp = tok.identifier * arraypat * singlememberpat^-1,
-- SWITCH -- 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" lpeg.Ct((Var("case_block") + Var("default_block"))^0) * sp1 * "endswitch"
/ on.switch_end, / on.switch_end,
@ -2725,21 +2759,21 @@ local Grammar = Pat{
optional_else = (sp1 * lpeg.C("else") * sp1 * Var("single_stmt"))^-1, 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") * Var("if_else_bodies")
* (Pat("")/end_if_else_fn), * (Pat("")/on.if_else_end),
if_stmt2 = con_if_begs/begin_if_fn * sp1 if_stmt2 = con_if_begs/on.if_begin * sp1
* (-con_if_begs * Var("single_stmt") * (Pat("")/end_if_fn) * (-con_if_begs * Var("single_stmt") * (Pat("")/on.if_end)
+ Var("if_else_bodies")) + 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" while_stmt = Keyw("whilevarvarn") * sp1 * tok.rvar * sp1 * tok.rvar / on.while_begin
* sp1 * Var("single_stmt") * lpeg.Cc("end") * sp1 * Var("single_stmt") * (lpeg.Cc(nil) / on.while_end)
+ Keyw("whilevarn") * sp1 * tok.rvar * sp1 * tok.define / "while (%1~=%2) do" + Keyw("whilevarn") * sp1 * tok.rvar * sp1 * tok.define / on.while_begin
* sp1 * Var("single_stmt") * lpeg.Cc("end"), * sp1 * Var("single_stmt") * (lpeg.Cc(nil) / on.while_end),
stmt_common = Keyw("{") * sp1 * "}" -- space separation of commands in CON is for a reason! stmt_common = Keyw("{") * sp1 * "}" -- space separation of commands in CON is for a reason!
+ Keyw("{") * sp1 * stmt_list * sp1 * "}" + Keyw("{") * sp1 * stmt_list * sp1 * "}"
@ -2803,6 +2837,8 @@ end
function on.parse_begin() function on.parse_begin()
g_iflevel = 0 g_iflevel = 0
g_ifelselevel = 0 g_ifelselevel = 0
g_isWhile = {}
g_whilenum = 0
g_have_file[g_filename] = true g_have_file[g_filename] = true
-- set up new state -- set up new state

View file

@ -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:
// <code...>
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