mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-24 10:40:46 +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 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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
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