diff --git a/polymer/eduke32/source/lunatic/control.lua b/polymer/eduke32/source/lunatic/control.lua index db9952926..e30770820 100644 --- a/polymer/eduke32/source/lunatic/control.lua +++ b/polymer/eduke32/source/lunatic/control.lua @@ -1276,6 +1276,75 @@ function _setaspect(viewingrange, yxaspect) end +--- Game arrays --- + +local function check_gamearray_idx(gar, idx, addstr) + if (idx >= gar._size+0ULL) then + addstr = addstr or "" + error("invalid "..addstr.."array index "..idx, 3) + end +end + +local gamearray_methods = { + resize = function(gar, newsize) + -- NOTE: size 0 is valid (then, no index is valid) + if (newsize < 0) then + error("invalid new array size "..newsize, 2) + end + + -- clear trailing elements in case we're shrinking + for i=gar._size,newsize-1 do + gar[i] = nil + end + + gar._size = newsize + end, + + copyto = function(sar, sidx, dar, didx, numelts) + -- XXX: Strictest bound checking, see later if we need to relax it. + check_gamearray_idx(sar, sidx, "lower source ") + check_gamearray_idx(sar, sidx+numelts-1, "upper source ") + check_gamearray_idx(dar, didx, "lower destination ") + check_gamearray_idx(dar, didx+numelts-1, "upper destination ") + for i=0,numelts-1 do + dar[dsix+i] = sar[sidx+i] + end + end, +} + +local gamearray_mt = { + __index = function(gar, key) + if (type(key)=="number") then + check_gamearray_idx(key) + return 0 + else + return gamearray_methods[key] + end + end, + + __newindex = function(gar, idx, val) + check_gamearray_idx(idx) + gar[idx] = val + end, + + -- Calling a gamearray causes its cleanup: + -- * All values equal to the default one (0) are cleared. + __call = function(gar) + for i=0,gar._size-1 do + if (gar[i]==0) then + gar[i] = nil + end + end + end, + + __metatable = true, +} + +function _gamearray(size) + return setmetatable({ _size=size }, gamearray_mt) +end + + --- Exported functions --- -- Non-local control flow. These ones call the original error(), not our diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index dd0554a11..a803cca32 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -117,6 +117,8 @@ GVFLAG.USER_MASK = GVFLAG.PERX_MASK + GVFLAG.NODEFAULT + GVFLAG.NORESET local g_funcname = {} -- [identifier] = { name=, flags= } local g_gamevar = {} +-- [identifier] = { name=, size= } +local g_gamearray = {} local g_have_file = {} -- [filename]=true local g_curcode = nil -- a table of string pieces or other "gencode" tables @@ -248,6 +250,7 @@ end local function reset_codegen() g_funcname = {} g_gamevar = new_initial_gvartab() + g_gamearray = {} g_have_file = {} g_curcode = new_initial_codetab() @@ -452,7 +455,18 @@ function lookup.defined_label(pos, maybe_minus_str, identifier) return (maybe_minus_str=="" and 1 or -1) * num end +local function check_sysvar_def_attempt(identifier) + if (identifier=="actorvar") then + errprintf("cannot define reserved symbol `actorvar'") + return true + end +end + local function do_define_label(identifier, num) + if (check_sysvar_def_attempt(identifier)) then + return + end + local oldtype = g_labeltype[identifier] local oldval = g_labeldef[identifier] @@ -726,13 +740,6 @@ local function stripws(str) end function Cmd.definequote(qnum, quotestr) ---[[ - -- have the INT_MAX limit simply for some sanity - if (not (qnum >= 0 and <= 0x7fffffff)) then - errprintf("quote number is negative or exceeds limit of INT32_MAX.") - return - end ---]] if (not (qnum >= 0 and qnum < conl.MAXQUOTES)) then errprintf("quote number is negative or exceeds limit of %d.", conl.MAXQUOTES-1) return @@ -828,7 +835,42 @@ end --- GAMEVARS / GAMEARRAYS +function Cmd.gamearray(identifier, initsize) + if (check_sysvar_def_attempt(identifier)) then + return + end + + if (initsize >= 0x7fffffff+0ULL) then + errprintf("invalid initial size %d for gamearray `%s'", initsize, identifier) + return + end + + local oga = g_gamearray[identifier] + if (oga) then + if (initsize ~= oga.size) then + errprintf("duplicate gamearray definition `%s' has different size", identifier) + return + else + warnprintf("duplicate gamearray definition `%s' ignored", identifier) + return + end + end + + if (g_gamevar[identifier]) then + warnprintf("symbol `%s' already used for game variable", identifier) + end + + local ga = { name=mangle_name(identifier, "A"), size=initsize } + g_gamearray[identifier] = ga + + addcodef("local %s=_con._gamearray(%d)", ga.name, initsize) +end + function Cmd.gamevar(identifier, initval, flags) + if (check_sysvar_def_attempt(identifier)) then + return + end + -- TODO: handle user bits like NORESET or NODEFAULT if (bit.band(flags, bit.bnot(GVFLAG.USER_MASK)) ~= 0) then -- TODO: a couple of the presumably safe ones @@ -897,6 +939,15 @@ function Cmd.gamevar(identifier, initval, flags) end end +function lookup.gamearray(identifier) + local ga = g_gamearray[identifier] + if (ga == nil) then + errprintf("symbol `%s' is not a game array", identifier) + return "_INVALIDGA" + end + return ga.name +end + -- : code for actor or player index function lookup.gamevar(identifier, aorpvar, writable) local gv = g_gamevar[identifier] @@ -967,6 +1018,7 @@ local tok = rvar = Var("t_rvar"), wvar = Var("t_wvar"), + gamearray = Var("t_gamearray"), -- for definelevelname time = lpeg.C(alphanum*alphanum^-1*":"*alphanum*alphanum^-1), @@ -994,8 +1046,9 @@ local function n_defines(n) -- works well only for small n end -local D, R, W, I, AC, MV, AI = -1, -2, -3, -4, -5, -6, -7 -local TOKEN_PATTERN = { [D]=tok.define, [R]=tok.rvar, [W]=tok.wvar, [I]=tok.identifier, +local D, R, W, I, GARI, AC, MV, AI = -1, -2, -3, -4, -5, -6, -7, -8 +local TOKEN_PATTERN = { [D]=tok.define, [R]=tok.rvar, [W]=tok.wvar, + [I]=tok.identifier, [GARI]=tok.gamearray, [AC]=tok.action, [MV]=tok.move, [AI]=tok.ai } -- Generic command pattern, types given by varargs. @@ -1065,7 +1118,7 @@ local Couter = { --- 4. Game Variables / Arrays gamevar = cmd(I,D,D) / Cmd.gamevar, - gamearray = cmd(I,D), + gamearray = cmd(I,D) / Cmd.gamearray, --- 5. Top level commands that are also run-time commands move = sp1 * tok.identifier * (sp1 * tok.define)^-2 / -- hvel, vvel @@ -1188,11 +1241,43 @@ local function StructAccess(Structname, writep, index, membertab) end end - function lookup.array_expr(writep, structname, index, membertab) if (conl.StructAccessCode[structname] == nil) then --- warnprintf("gamearray access: NYI") - return "_NYI" + -- Try a gamearray + local ga = (g_gamearray[structname]) and lookup.gamearray(structname) + if (ga == nil) then + if (structname=="actorvar") then + -- actorvar[] inline array expr + -- XXX: kind of CODEDUP with GetOrSetPerxvarCmd() factory + local gv = g_gamevar[structname] + if (gv and bit.band(gv.flags, GVFLAG.PERX_MASK)~=GVFLAG.PERACTOR) then + errprintf("variable `%s' is not per-actor", structname, "actor") + end + + if (membertab == nil) then + errprintf("actorvar[] requires a pseudo member (gamevar) name") + return "_INVALIDAV" + end + + if (#membertab > 1) then + errprintf("actorvar[] cannot be used with a second parameter") + return "_INVALIDAV" + end + + assert(#membertab == 1) + return lookup.gamevar(membertab[1], index, writep) + end + + errprintf("symbol `%s' is neither a struct nor a gamearray", structname) + return "_INVALIDAR" + end + + if (membertab ~= nil) then + errprintf("gamearrays cannot be indexed with member names") + return "_INVALIDAR" + end + + return format("%s[%s]", ga.name, index) end local membercode, ismethod = StructAccess(structname, writep, index, membertab) @@ -1679,12 +1764,16 @@ local Cinner = { getpname = cmd(R,R), -- array stuff - copy = sp1 * tok.identifier * arraypat * sp1 * tok.identifier * arraypat * sp1 * tok.rvar, - setarray = sp1 * tok.identifier * arraypat * sp1 * tok.rvar, - resizearray = cmd(I,R), - getarraysize = cmd(I,W), - readarrayfromfile = cmd(I,D), - writearraytofile = cmd(I,D), + copy = sp1 * tok.gamearray * arraypat * sp1 * tok.gamearray * arraypat * sp1 * tok.rvar + / "%1:copyto(%2,%3,%4,%5)", + setarray = sp1 * tok.gamearray * arraypat * sp1 * tok.rvar + / "%1[%2]=%3", + resizearray = cmd(GARI,R) + / "%1:resize(%2)", + getarraysize = cmd(GARI,W) + / "%2=%1._size", + readarrayfromfile = cmd(GARI,D), + writearraytofile = cmd(GARI,D), addlogvar = cmd(R) / handle.addlogvar, @@ -2269,7 +2358,6 @@ local function get_deferred_code(tab, lev, code) end local function begin_if_fn(condstr, endifstr, endifelsestr) - condstr = condstr or "TODO" assert(type(condstr)=="string") add_deferred_code(g_endIfCode, g_iflevel, endifstr) @@ -2339,6 +2427,8 @@ local Grammar = Pat{ t_wvar = Var("t_singlearrayexp") / function() errprintf("t_wvar: array exprs NYI") return "_NYIVAR" end + (tok.identifier / function(id) return lookup.gamevar(id, "_aci", true) end), + t_gamearray = Var("t_identifier") / lookup.gamearray, + t_move = POS()*tok.identifier / function(...) return lookup.composite(LABEL.MOVE, ...) end + POS()*tok.number / function(...) return check_composite_literal(LABEL.MOVE, ...) end,