mirror of
https://github.com/DrBeef/Raze.git
synced 2025-02-07 16:31:17 +00:00
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:
parent
d747e26fd0
commit
677c585b72
4 changed files with 167 additions and 18 deletions
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
110
polymer/eduke32/source/lunatic/test/switch.con
Normal file
110
polymer/eduke32/source/lunatic/test/switch.con
Normal 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
|
Loading…
Reference in a new issue