mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-11-14 08:30:58 +00:00
Lunatic translator: dangling else for the 1000th time: not dangling any more.
Also update test/dangling_else.con. See there for how if* cascades are handled in CON. It's actually kind of pretty. Also, take care to handle code deferred to after the if/if-else properly (ifpdistl, ifpdistg, ifcanseetartet). git-svn-id: https://svn.eduke32.com/eduke32@3534 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
parent
8955316bba
commit
ba0970cede
2 changed files with 117 additions and 77 deletions
|
@ -54,7 +54,7 @@ lpeg.setmaxstack(1024);
|
||||||
|
|
||||||
|
|
||||||
local Pat, Set, Range, Var = lpeg.P, lpeg.S, lpeg.R, lpeg.V
|
local Pat, Set, Range, Var = lpeg.P, lpeg.S, lpeg.R, lpeg.V
|
||||||
local POS = lpeg.Cp
|
local POS, Cc, Ctab = lpeg.Cp, lpeg.Cc, lpeg.Ct
|
||||||
|
|
||||||
-- CON language definitions (among other things, all keywords pattern).
|
-- CON language definitions (among other things, all keywords pattern).
|
||||||
local conl = require("con_lang")
|
local conl = require("con_lang")
|
||||||
|
@ -1597,9 +1597,9 @@ local handle =
|
||||||
-- readgamevar or savegamevar
|
-- readgamevar or savegamevar
|
||||||
RSgamevar = function(identifier, dosave)
|
RSgamevar = function(identifier, dosave)
|
||||||
-- check identifier for sanity
|
-- check identifier for sanity
|
||||||
if (not identifier:match("^[A-Za-z][A-Za-z0-9_]*$")) then
|
if (not identifier:match("^[A-Za-z][A-Za-z0-9_%-]*$")) then
|
||||||
errprintf("%s: invalid identifier name `%s' for config file persistence",
|
errprintf("%s: bad identifier `%s' for config file persistence",
|
||||||
g_lastkw)
|
g_lastkw, identifier)
|
||||||
return "_BADRSGV()"
|
return "_BADRSGV()"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2207,10 +2207,10 @@ local Cif = {
|
||||||
|
|
||||||
ifrnd = cmd(D)
|
ifrnd = cmd(D)
|
||||||
/ "_con.rnd(%1)",
|
/ "_con.rnd(%1)",
|
||||||
ifpdistl = cmd(D)
|
ifpdistl = cmd(D) -- DEFER
|
||||||
/ function(val) return "_dist<"..val, "", "_con._sleepcheck(_aci,_dist)" end,
|
/ function(val) return { "_dist<"..val, nil, "_con._sleepcheck(_aci,_dist)" } end,
|
||||||
ifpdistg = cmd(D)
|
ifpdistg = cmd(D) -- DEFER
|
||||||
/ function(val) return "_dist>"..val, "", "_con._sleepcheck(_aci,_dist)" end,
|
/ function(val) return { "_dist>"..val, nil, "_con._sleepcheck(_aci,_dist)" } end,
|
||||||
ifactioncount = cmd(D)
|
ifactioncount = cmd(D)
|
||||||
/ ACS":get_acount()>=%1",
|
/ ACS":get_acount()>=%1",
|
||||||
ifcount = cmd(D)
|
ifcount = cmd(D)
|
||||||
|
@ -2315,8 +2315,10 @@ local Cif = {
|
||||||
/ "false", -- TODO_MP
|
/ "false", -- TODO_MP
|
||||||
ifcanshoottarget = cmd()
|
ifcanshoottarget = cmd()
|
||||||
/ "_con._canshoottarget(_dist,_aci)",
|
/ "_con._canshoottarget(_dist,_aci)",
|
||||||
ifcanseetarget = cmd() -- XXX: 1536 is SLEEPTIME
|
ifcanseetarget = cmd() -- DEFER -- XXX: 1536 is SLEEPTIME
|
||||||
/ function() return format("_con._canseetarget(%s,%s)", SPS"", PLS""), ACS".timetosleep=1536" end,
|
/ function()
|
||||||
|
return { format("_con._canseetarget(%s,%s)", SPS"", PLS""), ACS".timetosleep=1536" }
|
||||||
|
end,
|
||||||
ifcansee = cmd() * #sp1
|
ifcansee = cmd() * #sp1
|
||||||
/ format("_con._cansee(_aci,%s)", PLS""),
|
/ format("_con._cansee(_aci,%s)", PLS""),
|
||||||
ifbulletnear = cmd()
|
ifbulletnear = cmd()
|
||||||
|
@ -2467,7 +2469,7 @@ local function after_cmd_Cmt(subj, pos, ...)
|
||||||
return true -- don't return any captures
|
return true -- don't return any captures
|
||||||
end
|
end
|
||||||
|
|
||||||
-- attach the command names at the front!
|
-- Attach the command names at the front!
|
||||||
local function attachnames(kwtab, matchtimefunc)
|
local function attachnames(kwtab, matchtimefunc)
|
||||||
for cmdname,cmdpat in pairs(kwtab) do
|
for cmdname,cmdpat in pairs(kwtab) do
|
||||||
-- The match-time function capture at the end is so that every command
|
-- The match-time function capture at the end is so that every command
|
||||||
|
@ -2598,55 +2600,56 @@ local t_good_identifier = Range("AZ", "az", "__") * Range("AZ", "az", "__", "09"
|
||||||
local t_broken_identifier = BadIdent(-((tok.number + t_good_identifier) * (sp1 + Set("[]:"))) *
|
local t_broken_identifier = BadIdent(-((tok.number + t_good_identifier) * (sp1 + Set("[]:"))) *
|
||||||
(alphanum + Set(BAD_ID_CHARS0)) * (alphanum + Set(BAD_ID_CHARS1))^0)
|
(alphanum + Set(BAD_ID_CHARS0)) * (alphanum + Set(BAD_ID_CHARS1))^0)
|
||||||
|
|
||||||
-- These two tables hold code to be inserted at a later point: either at
|
function on.if_else_end(ifconds, ifstmt, elsestmt, ...)
|
||||||
-- the end of the "if" body, or the end of the whole "if [else]" block.
|
assert(#{...}==0)
|
||||||
-- For CON interpreter patterns like these:
|
assert(type(ifconds)=="table" and #ifconds>=1)
|
||||||
-- VM_CONDITIONAL(<condition>);
|
|
||||||
-- <do_something_afterwards>
|
|
||||||
-- (Still not the same if the body returns or jumps out)
|
|
||||||
local g_endIfCode = {}
|
|
||||||
local g_endIfElseCode = {}
|
|
||||||
|
|
||||||
local function add_deferred_code(tab, lev, str)
|
-- A condition may be a table carrying "deferred" code to add either
|
||||||
if (str ~= nil) then
|
-- [1] after the 'if' or
|
||||||
assert(type(str)=="string")
|
-- [2] after the whole if/else block.
|
||||||
tab[lev] = str
|
-- In CON, it's always the same code for the same kind of "deferedness",
|
||||||
end
|
-- and it's always idempotent (executing it multiple times has the same
|
||||||
end
|
-- effect as executing it once), so generate code for it only once, too.
|
||||||
|
local deferred = { nil, nil }
|
||||||
|
|
||||||
local function get_deferred_code(tab, lev, code)
|
local ifcondstr = {}
|
||||||
if (tab[lev]) then
|
for i=1,#ifconds do
|
||||||
code = code..tab[lev]
|
local cond = ifconds[i]
|
||||||
tab[lev] = nil
|
local hasmore = type(cond=="table")
|
||||||
|
|
||||||
|
ifcondstr[i] = hasmore and cond[1] or cond
|
||||||
|
assert(type(ifcondstr[i])=="string")
|
||||||
|
|
||||||
|
if (hasmore) then
|
||||||
|
for i=1,2 do
|
||||||
|
if (deferred[i]==nil) then
|
||||||
|
deferred[i] = cond[i+1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Construct a string of ANDed conditions
|
||||||
|
local conds = "(" .. table.concat(ifcondstr, ")and(") .. ")"
|
||||||
|
|
||||||
|
local code = {
|
||||||
|
format("if %s then", conds),
|
||||||
|
ifstmt,
|
||||||
|
}
|
||||||
|
|
||||||
|
code[#code+1] = deferred[1]
|
||||||
|
|
||||||
|
if (elsestmt~=nil) then
|
||||||
|
code[#code+1] = "else"
|
||||||
|
code[#code+1] = elsestmt
|
||||||
|
end
|
||||||
|
|
||||||
|
code[#code+1] = "end"
|
||||||
|
code[#code+1] = deferred[2]
|
||||||
|
|
||||||
return code
|
return code
|
||||||
end
|
end
|
||||||
|
|
||||||
function on.if_begin(condstr, endifstr, endifelsestr)
|
|
||||||
assert(type(condstr)=="string")
|
|
||||||
|
|
||||||
add_deferred_code(g_endIfCode, g_iflevel, endifstr)
|
|
||||||
add_deferred_code(g_endIfElseCode, g_ifelselevel, endifelsestr)
|
|
||||||
|
|
||||||
g_iflevel = g_iflevel+1
|
|
||||||
g_ifelselevel = g_ifelselevel+1
|
|
||||||
|
|
||||||
return format("if (%s) then", condstr)
|
|
||||||
end
|
|
||||||
|
|
||||||
function on.if_end()
|
|
||||||
g_iflevel = g_iflevel-1
|
|
||||||
local code = get_deferred_code(g_endIfCode, g_iflevel, "")
|
|
||||||
if (code ~= "") then
|
|
||||||
return code
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
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)
|
function on.while_begin(v1, v2)
|
||||||
table.insert(g_isWhile, true)
|
table.insert(g_isWhile, true)
|
||||||
return format("while (%s~=%s) do", v1, v2)
|
return format("while (%s~=%s) do", v1, v2)
|
||||||
|
@ -2788,18 +2791,8 @@ local Grammar = Pat{
|
||||||
default_block = lpeg.Ct(sp1 * Keyw("default") * (sp0*":"*sp0 + sp1) *
|
default_block = lpeg.Ct(sp1 * Keyw("default") * (sp0*":"*sp0 + sp1) *
|
||||||
stmt_list_nosp_or_eps), -- * "break",
|
stmt_list_nosp_or_eps), -- * "break",
|
||||||
|
|
||||||
optional_else = (sp1 * lpeg.C("else") * sp1 * Var("single_stmt"))^-1,
|
if_stmt = lpeg.Ct((con_if_begs * sp1)^1) * Var("single_stmt")
|
||||||
|
* (sp1 * Keyw("else") * sp1 * Var("single_stmt"))^-1 / on.if_else_end,
|
||||||
if_else_bodies = Var("single_stmt2") * (Pat("")/on.if_end) * Var("optional_else"),
|
|
||||||
|
|
||||||
if_stmt = con_if_begs/on.if_begin * sp1
|
|
||||||
* Var("if_else_bodies")
|
|
||||||
* (Pat("")/on.if_else_end),
|
|
||||||
|
|
||||||
if_stmt2 = con_if_begs/on.if_begin * sp1
|
|
||||||
* (-con_if_begs * Var("single_stmt") * (Pat("")/on.if_end)
|
|
||||||
+ Var("if_else_bodies"))
|
|
||||||
* (Pat("")/on.if_else_end),
|
|
||||||
|
|
||||||
while_stmt = Keyw("whilevarvarn") * sp1 * tok.rvar * sp1 * tok.rvar / on.while_begin
|
while_stmt = Keyw("whilevarvarn") * sp1 * tok.rvar * sp1 * tok.rvar / on.while_begin
|
||||||
* sp1 * Var("single_stmt") * (lpeg.Cc(nil) / on.while_end)
|
* sp1 * Var("single_stmt") * (lpeg.Cc(nil) / on.while_end)
|
||||||
|
@ -2807,11 +2800,10 @@ local Grammar = Pat{
|
||||||
* sp1 * Var("single_stmt") * (lpeg.Cc(nil) / on.while_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 * lpeg.Ct(stmt_list) * sp1 * "}"
|
||||||
+ con_inner_command + Var("switch_stmt") + Var("while_stmt"),
|
+ con_inner_command + Var("switch_stmt") + lpeg.Ct(Var("while_stmt")),
|
||||||
|
|
||||||
single_stmt = Stmt( lone_else^-1 * (Var("stmt_common") + Var("if_stmt")) ),
|
single_stmt = Stmt( lone_else^-1 * (Var("stmt_common") + Var("if_stmt")) ),
|
||||||
single_stmt2 = Stmt( lone_else^-1 * (Var("stmt_common") + Var("if_stmt2")) ),
|
|
||||||
|
|
||||||
-- a non-empty statement/command list
|
-- a non-empty statement/command list
|
||||||
stmt_list = Var("single_stmt") * (sp1 * Var("single_stmt"))^0,
|
stmt_list = Var("single_stmt") * (sp1 * Var("single_stmt"))^0,
|
||||||
|
|
|
@ -1,10 +1,58 @@
|
||||||
// a dangling 'else' in CON attaches to the outermost matching 'if'
|
// In CON, the dangling else ambiguity is resolved in a very peculiar way:
|
||||||
onevent EVENT_INIT
|
// A cascade "ifP1 ifP2 ifP3 Stmt1 ... else Stmt2" is considered as if the
|
||||||
redefinequote 114 DANGLING ELSE CHECK FAILED 1
|
// predicates P1, P2, P3, ... are combined with a logical AND.
|
||||||
ifvare 0 1
|
// So effectively, there is no ambiguity at all, and one of Stmt1 and Stmt2
|
||||||
ifvare 0 0 redefinequote 114 DANGLING ELSE CHECK FAILED 2
|
// is guaranteed to be taken.
|
||||||
else
|
|
||||||
redefinequote 114 DANGLING ELSE CHECK PASSED
|
|
||||||
|
|
||||||
echo 114
|
define Q 114
|
||||||
|
define GLOBAL 0
|
||||||
|
|
||||||
|
gamevar x 0 GLOBAL
|
||||||
|
// divisible by 2, 3, 5?
|
||||||
|
gamevar xm2 0 GLOBAL
|
||||||
|
gamevar xm3 0 GLOBAL
|
||||||
|
gamevar xm5 0 GLOBAL
|
||||||
|
|
||||||
|
state testelse
|
||||||
|
setvarvar xm2 x, modvar xm2 2
|
||||||
|
setvarvar xm3 x, modvar xm3 3
|
||||||
|
setvarvar xm5 x, modvar xm5 5
|
||||||
|
|
||||||
|
redefinequote Q ERROR: one path of if/else must be taken
|
||||||
|
|
||||||
|
ifvarn xm2 0
|
||||||
|
ifvarn xm3 0
|
||||||
|
ifvarn xm5 0
|
||||||
|
redefinequote Q x=%d is not divisible by either 2, 3 or 5
|
||||||
|
else
|
||||||
|
redefinequote Q x=%d is divisible by either 2, 3 or 5
|
||||||
|
|
||||||
|
qsprintf Q Q x
|
||||||
|
userquote Q
|
||||||
|
ends
|
||||||
|
|
||||||
|
onevent EVENT_ENTERLEVEL
|
||||||
|
setvar x 0
|
||||||
|
whilevarn x 31
|
||||||
|
{
|
||||||
|
state testelse
|
||||||
|
addvar x 1
|
||||||
|
}
|
||||||
|
endevent
|
||||||
|
|
||||||
|
// Test deferred code. This CON code doesn't make much sense, but check out
|
||||||
|
// the generated Lua code...
|
||||||
|
onevent EVENT_JUMP
|
||||||
|
ifpdistl 100 ifpdistg 0 ifcanseetarget
|
||||||
|
{
|
||||||
|
palfrom 32 32 32
|
||||||
|
ifcanseetarget
|
||||||
|
quote 29
|
||||||
|
ifcanseetarget
|
||||||
|
quote 30
|
||||||
|
setvar x 0
|
||||||
|
state testelse
|
||||||
|
}
|
||||||
|
else ifcanseetarget
|
||||||
|
quote 31
|
||||||
endevent
|
endevent
|
||||||
|
|
Loading…
Reference in a new issue