diff --git a/polymer/eduke32/source/lunatic/con_lang.lua b/polymer/eduke32/source/lunatic/con_lang.lua new file mode 100644 index 000000000..ffb4647d9 --- /dev/null +++ b/polymer/eduke32/source/lunatic/con_lang.lua @@ -0,0 +1,370 @@ +-- Use this file like +-- require("lpeg") +-- con_keyword = dofile("con_lang.lua") + +return + +lpeg.P(false) + +"{" + +"}" + +"action" + +"activatebysector" + +"activatecheat" + +"actor" + +"addammo" + +"addinventory" + +"addkills" + +"addlog" + +"addlogvar" + +"addphealth" + +"addstrength" + +"addvar" + +"addvarvar" + +"addweapon" + +"addweaponvar" + +"ai" + +"andvar" + +"andvarvar" + +"angoff" + +"angoffvar" + +"betaname" + +"break" + +"cactor" + +"calchypotenuse" + +"cansee" + +"canseespr" + +"case" + +"changespritesect" + +"changespritestat" + +"cheatkeys" + +"checkactivatormotion" + +"checkavailinven" + +"checkavailweapon" + +"clearmapstate" + +"clipdist" + +"clipmove" + +"clipmovenoslide" + +"cmenu" + +"copy" + +"cos" + +"count" + +"cstat" + +"cstator" + +"debris" + +"debug" + +"default" + +"define" + +"definecheat" + +"definegamefuncname" + +"definegametype" + +"definelevelname" + +"defineprojectile" + +"definequote" + +"defineskillname" + +"definesound" + +"definevolumename" + +"digitalnumber" + +"digitalnumberz" + +"displayrand" + +"displayrandvar" + +"displayrandvarvar" + +"dist" + +"divvar" + +"divvarvar" + +"dragpoint" + +"dynamicremap" + +"echo" + +"else" + +"enda" + +"endevent" + +"endofgame" + +"ends" + +"endswitch" + +"enhanced" + +"eqspawn" + +"eqspawnvar" + +"eshoot" + +"eshootvar" + +"espawn" + +"espawnvar" + +"eventloadactor" + +"ezshoot" + +"ezshootvar" + +"fall" + +"findnearactor" + +"findnearactor3d" + +"findnearactor3dvar" + +"findnearactorvar" + +"findnearactorz" + +"findnearactorzvar" + +"findnearsprite" + +"findnearsprite3d" + +"findnearsprite3dvar" + +"findnearspritevar" + +"findnearspritez" + +"findnearspritezvar" + +"findotherplayer" + +"findplayer" + +"flash" + +"gamearray" + +"gamestartup" + +"gametext" + +"gametextz" + +"gamevar" + +"getactor" + +"getactorangle" + +"getactorvar" + +"getangle" + +"getangletotarget" + +"getarraysize" + +"getceilzofslope" + +"getcurraddress" + +"getflorzofslope" + +"getincangle" + +"getinput" + +"getkeyname" + +"getlastpal" + +"getplayer" + +"getplayerangle" + +"getplayervar" + +"getpname" + +"getprojectile" + +"getsector" + +"gettextureceiling" + +"gettexturefloor" + +"getthisprojectile" + +"getticks" + +"gettimedate" + +"gettspr" + +"getuserdef" + +"getwall" + +"getzrange" + +"globalsound" + +"globalsoundvar" + +"gmaxammo" + +"guniqhudid" + +"guts" + +"headspritesect" + +"headspritestat" + +"hitradius" + +"hitradiusvar" + +"hitscan" + +"ifaction" + +"ifactioncount" + +"ifactor" + +"ifactornotstayput" + +"ifactorsound" + +"ifai" + +"ifangdiffl" + +"ifawayfromwall" + +"ifbulletnear" + +"ifcansee" + +"ifcanseetarget" + +"ifcanshoottarget" + +"ifceilingdistl" + +"ifclient" + +"ifcount" + +"ifdead" + +"iffloordistl" + +"ifgapzl" + +"ifgotweaponce" + +"ifhitspace" + +"ifhitweapon" + +"ifinouterspace" + +"ifinspace" + +"ifinwater" + +"ifmove" + +"ifmultiplayer" + +"ifnosounds" + +"ifnotmoving" + +"ifonwater" + +"ifoutside" + +"ifp" + +"ifpdistg" + +"ifpdistl" + +"ifphealthl" + +"ifpinventory" + +"ifrespawn" + +"ifrnd" + +"ifserver" + +"ifsound" + +"ifspawnedby" + +"ifspritepal" + +"ifsquished" + +"ifstrength" + +"ifvarand" + +"ifvare" + +"ifvareither" + +"ifvarg" + +"ifvarl" + +"ifvarn" + +"ifvaror" + +"ifvarvarand" + +"ifvarvare" + +"ifvarvareither" + +"ifvarvarg" + +"ifvarvarl" + +"ifvarvarn" + +"ifvarvaror" + +"ifvarvarxor" + +"ifvarxor" + +"ifwasweapon" + +"include" + +"includedefault" + +"inittimer" + +"insertspriteq" + +"jump" + +"killit" + +"ldist" + +"lineintersect" + +"loadmapstate" + +"lockplayer" + +"lotsofglass" + +"mail" + +"mikesnd" + +"minitext" + +"modvar" + +"modvarvar" + +"money" + +"move" + +"movesprite" + +"mulscale" + +"mulvar" + +"mulvarvar" + +"music" + +"myos" + +"myospal" + +"myospalx" + +"myosx" + +"neartag" + +"nextspritesect" + +"nextspritestat" + +"nullop" + +"onevent" + +"operate" + +"operateactivators" + +"operatemasterswitches" + +"operaterespawns" + +"operatesectors" + +"orvar" + +"orvarvar" + +"palfrom" + +"paper" + +"pkick" + +"precache" + +"prevspritesect" + +"prevspritestat" + +"pstomp" + +"qgetsysstr" + +"qspawn" + +"qspawnvar" + +"qsprintf" + +"qstrcat" + +"qstrcpy" + +"qstrlen" + +"qstrncat" + +"qsubstr" + +"quake" + +"quote" + +"randvar" + +"randvarvar" + +"rayintersect" + +"readarrayfromfile" + +"readgamevar" + +"redefinequote" + +"resetactioncount" + +"resetcount" + +"resetplayer" + +"resizearray" + +"respawnhitag" + +"return" + +"rotatepoint" + +"rotatesprite" + +"rotatesprite16" + +"save" + +"savegamevar" + +"savemapstate" + +"savenn" + +"scriptsize" + +"sectclearinterpolation" + +"sectgethitag" + +"sectgetlotag" + +"sectorofwall" + +"sectsetinterpolation" + +"setactor" + +"setactorangle" + +"setactorsoundpitch" + +"setactorvar" + +"setarray" + +"setaspect" + +"setcfgname" + +"setdefname" + +"setgamename" + +"setgamepalette" + +"setinput" + +"setplayer" + +"setplayerangle" + +"setplayervar" + +"setprojectile" + +"setsector" + +"setsprite" + +"setthisprojectile" + +"settspr" + +"setuserdef" + +"setvar" + +"setvarvar" + +"setwall" + +"shiftvarl" + +"shiftvarr" + +"shoot" + +"shootvar" + +"showview" + +"showviewunbiased" + +"sin" + +"sizeat" + +"sizeto" + +"sleeptime" + +"smaxammo" + +"sound" + +"soundonce" + +"soundoncevar" + +"soundvar" + +"spawn" + +"spgethitag" + +"spgetlotag" + +"spriteflags" + +"spritenopal" + +"spritenoshade" + +"spritenvg" + +"spritepal" + +"spriteshadow" + +"sqrt" + +"ssp" + +"startlevel" + +"starttrack" + +"starttrackvar" + +"state" + +"stopactorsound" + +"stopallsounds" + +"stopsound" + +"stopsoundvar" + +"strength" + +"subvar" + +"subvarvar" + +"switch" + +"time" + +"tip" + +"tossweapon" + +"updatesector" + +"updatesectorz" + +"useractor" + +"userquote" + +"wackplayer" + +"whilevarn" + +"whilevarvarn" + +"writearraytofile" + +"xorvar" + +"xorvarvar" + +"zshoot" + +"zshootvar" + +lpeg.P(false) diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua new file mode 100644 index 000000000..e0c19487c --- /dev/null +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -0,0 +1,777 @@ +-- LunaCON CON to Lunatic translator +-- requires LPeg, http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html + +local lpeg = require("lpeg") + +local Pat, Set, Range, Var = lpeg.P, lpeg.S, lpeg.R, lpeg.V + + +---- All keywords pattern -- needed for CON syntax +local con_keyword = dofile("con_lang.lua") + + +local function match_until(matchsp, untilsp) -- (!untilsp matchsp)* in PEG + -- sp: string or pattern + return (matchsp - Pat(untilsp))^0 +end + + +----==== patterns ====---- + +---- basic ones +local newline = Pat("\n") -- need to do stuff on newline later... +local anychar = Pat(1) +-- comments +local comment = "/*" * match_until(anychar, "*/") * "*/" +local linecomment = "//" * match_until(anychar, newline) +local whitespace = Var("whitespace") +local sp0 = whitespace^0 +local sp1 = whitespace^1 -- + (-1) -- matches EOF, too +local alpha = Range("AZ", "az") -- locale? +local alphanum = alpha + Range("09") +local alnumtok = alphanum + Set("{}/\\*-_.") -- see isaltok() in gamedef.c + +--- basic lexical elements ("tokens") +local t_number = Range("09")^1 + (Pat("0x") + "0X")*Range("09", "af")^1 +-- Valid identifier names are disjunct from keywords! +-- XXX: CON is more permissive with identifier name characters: +local t_identifier = Var("t_identifier") +-- This one matches keywords, too: +local t_identifier_all = Var("t_identifier_all") +local t_define = Var("t_define") +local t_filename = (anychar-Set(" \t\r\n"))^1 --alnumtok^1 -- XXX +local t_newline_term_str = match_until(anychar, newline) + +-- new-style inline arrays and structures: +local t_arrayexp = Var("t_arrayexp") + +-- defines and constants can take the place of vars that are only read: +local t_rvar = t_arrayexp + t_define +-- not so with written-to vars: +local t_wvar = t_arrayexp + t_identifier + + +---- helper patterns / pattern constructing functions +local maybe_quoted_filename = ('"' * t_filename * '"' + t_filename) +local newline_term_string = (whitespace-newline)^1 * t_newline_term_str -- XXX: empty string? + + +-- (sp1 * t_define) repeated exactly n times +local function n_defines(n) -- works well only for small n + local pat = Pat(true) + for i=1,n do + pat = sp1 * t_define * pat + end + return pat +end + + +local D, R, W, I = -1, -2, -3, -4; + +-- Generic command pattern, types given by varargs. +-- The command name to be matched is attached later. +-- Example: +-- "command" writtenvar readvar def def: gencmd(W,R,D,D) +-- --> sp1 * t_wvar * sp1 * t_rvar * sp1 * t_define * sp1 * t_define +-- "command_with_no_args": gencmd() +-- --> Pat(true) +local function cmd(...) + local pat = Pat(true) + local vartypes = {...} + + local spat = "" + for i=1,#vartypes do + assert(vartypes[i] < 0) + + if (vartypes[i] == D) then + pat = pat * sp1 * t_define + elseif (vartypes[i] == R) then + pat = pat * sp1 * t_rvar + elseif (vartypes[i] == W) then + pat = pat * sp1 * t_wvar + else -- I + assert(vartypes[i] == I) + pat = pat * sp1 * t_identifier + end + end + + return pat +end + + +-- The command names will be attached to the front of the patterns later! + +--== Top level CON commands ==-- +local Co = { + --- 1. Preprocessor + include = sp1 * maybe_quoted_filename, + includedefault = cmd(), + define = cmd(I,D), + --define = sp1 * t_identifier * sp1 * t_define, + + --- 2. Defines and Meta-Settings + dynamicremap = cmd(), + setcfgname = sp1 * t_filename, + setdefname = sp1 * t_filename, + setgamename = newline_term_string, + + precache = cmd(D,D,D), + scriptsize = cmd(D), -- unused + cheatkeys = cmd(D,D), + + definecheat = newline_term_string, -- XXX: actually tricker syntax (TS) + definegamefuncname = newline_term_string, -- XXX: TS? + definegametype = n_defines(2) * newline_term_string, + definelevelname = n_defines(2) * newline_term_string, -- XXX: TS + defineskillname = sp1 * t_define * newline_term_string, + definevolumename = sp1 * t_define * newline_term_string, + + definequote = sp1 * t_define * newline_term_string, + defineprojectile = cmd(D,D,D), + definesound = sp1 * t_define * sp1 * maybe_quoted_filename * n_defines(5), -- XXX: TS + + -- XXX: need to see how that behaves with e.g. stuff like gamevar.ogg: + music = match_until(sp1 * t_filename, con_keyword), + + --- 3. Game Settings + -- gamestartup has 25/29 fixed defines, depending on 1.3D/1.5 version: + gamestartup = (sp1 * t_define)^25 * (sp1 * t_define)^-4, + spritenopal = cmd(D), + spritenoshade = cmd(D), + spritenvg = cmd(D), + spriteshadow = cmd(D), + + spriteflags = cmd(D,D), -- also see inner + + --- 4. Game Variables / Arrays + gamevar = cmd(I,D,D), + gamearray = cmd(I,D), + + --- 5. Top level commands that are also run-time commands + action = sp1 * t_identifier * (sp1 * t_define)^-5, + ai = sp1 * t_identifier * (sp1 * t_define)^0, + move = sp1 * t_identifier * (sp1 * t_define)^-2, + + --- 6. Deprecated TLCs + betaname = newline_term_string, + enhanced = cmd(D), +} + + +--== Run time CON commands ==-- +--- 1. Gamevar Operators + +local varop = cmd(W,D) +local varvarop = cmd(W,R) + +-- t_define is t_rvar without t_arrayexp, actually, so that no nesting is allowed: +local arraypat = sp0 * "[" * sp0 * t_define * sp0 * "]" +-- The member name must match keywords, too (_all), because e.g. cstat is a member +-- of sprite[] +local memberpat = sp0 * "." * sp0 * t_identifier_all + +local getstructcmd = -- get[]. ()? <> + -- existence of a second parameter is determined later +-- This is wrong, (sp1 id)? will match (sp1 wvar) if there's no 2nd param: +-- arraypat * memberpat * (sp1 * t_identifier)^-1 * sp1 * t_wvar +-- XXX: This is still wrong. + arraypat * memberpat * sp1 * (t_identifier * sp1 * t_wvar + t_wvar) + +local setstructcmd = -- set[]. ()? + -- existence of a second parameter is determined later + arraypat * memberpat * sp1 * (t_identifier * sp1 * t_rvar + t_rvar) + +local getperxvarcmd = -- getvar[]. <> + arraypat * memberpat * sp1 * t_wvar + +local setperxvarcmd = -- setvar[]. + arraypat * memberpat * sp1 * t_rvar + + +local Ci = { + -- these can appear anywhere in the script + ["break"] = cmd(), + ["return"] = cmd(), + + state = cmd(I), + + --- 1. get*, set* + getactor = getstructcmd, + getinput = getstructcmd, + getplayer = getstructcmd, + getprojectile = getstructcmd, + getsector = getstructcmd, + getsprite = getstructcmd, + getthisprojectile = getstructcmd, + gettspr = getstructcmd, + getuserdef = getstructcmd, + getwall = getstructcmd, + + getactorvar = getperxvarcmd, + getplayervar = getperxvarcmd, + + setactor = setstructcmd, + setinput = setstructcmd, + setplayer = setstructcmd, + setprojectile = setstructcmd, + setsector = setstructcmd, + setsprite = setstructcmd, + setthisprojectile = setstructcmd, + settspr = setstructcmd, + setuserdef = setstructcmd, + setwall = setstructcmd, + + setactorvar = setperxvarcmd, + setplayervar = setperxvarcmd, + + setvarvar = varvarop, + addvarvar = varvarop, + subvarvar = varvarop, + mulvarvar = varvarop, + divvarvar = varvarop, + modvarvar = varvarop, + andvarvar = varvarop, + orvarvar = varvarop, + xorvarvar = varvarop, + randvarvar = varvarop, + + setvar = varop, + addvar = varop, + subvar = varop, + mulvar = varop, + divvar = varop, + modvar = varop, + andvar = varop, + orvar = varop, + xorvar = varop, + randvar = varop, + shiftvarl = varop, + shiftvarr = varop, + + --- 2. Math operations + sqrt = cmd(R,W), + calchypotenuse = cmd(W,R,R), + sin = cmd(W,R), + cos = cmd(W,R), + mulscale = cmd(W,R,R), + getangle = cmd(W,R,R), + getincangle = cmd(W,R,R), + + --- 3. Actors + -- These three need more attention (different kind of labels; move + -- additionally may accept 0 or 1): + action = cmd(D), + ai = cmd(D), + move = sp1 * t_define * (sp1 * t_define)^0, + + cactor = cmd(D), + count = cmd(D), + cstator = cmd(D), + cstat = cmd(D), + clipdist = cmd(D), + sizeto = cmd(D,D), + sizeat = cmd(D,D), + strength = cmd(D), + addstrength = cmd(D), + spritepal = cmd(D), + + hitradius = cmd(D,D,D,D,D), + hitradiusvar = cmd(R,R,R,R,R), + + -- some commands taking read vars + eshootvar = cmd(R), + espawnvar = cmd(R), + qspawnvar = cmd(R), + eqspawnvar = cmd(R), + operaterespawns = cmd(R), + operatemasterswitches = cmd(R), + checkactivatormotion = cmd(R), + time = cmd(R), -- no-op + inittimer = cmd(R), + lockplayer = cmd(R), + shootvar = cmd(R), + quake = cmd(R), + jump = cmd(R), + cmenu = cmd(R), + soundvar = cmd(R), + globalsoundvar = cmd(R), + stopsoundvar = cmd(R), + soundoncevar = cmd(R), + angoffvar = cmd(R), + checkavailweapon = cmd(R), + checkavailinven = cmd(R), + guniqhudid = cmd(R), + savegamevar = cmd(R), + readgamevar = cmd(R), + userquote = cmd(R), + echo = cmd(R), + starttrackvar = cmd(R), + clearmapstate = cmd(R), + activatecheat = cmd(R), + setgamepalette = cmd(R), + + -- some commands taking defines + addammo = cmd(D,D), + addweapon = cmd(D,D), -- exec SPECIAL HANDLING! + debris = cmd(D,D), + addinventory = cmd(D,D), + guts = cmd(D,D), + + -- cont'd + addkills = cmd(D), + addphealth = cmd(D), + angoff = cmd(D), + debug = cmd(D), + endofgame = cmd(D), + eqspawn = cmd(D), + espawn = cmd(D), + globalsound = cmd(D), + lotsofglass = cmd(D), + mail = cmd(D), + money = cmd(D), + paper = cmd(D), + qspawn = cmd(D), + quote = cmd(D), + savenn = cmd(D), + save = cmd(D), + sleeptime = cmd(D), + soundonce = cmd(D), + sound = cmd(D), + spawn = cmd(D), + stopsound = cmd(D), + + eshoot = cmd(D), + ezshoot = cmd(R,D), + ezshootvar = cmd(R,R), + shoot = cmd(D), + zshoot = cmd(R,D), + zshootvar = cmd(R,R), + + fall = cmd(), + flash = cmd(), + getlastpal = cmd(), + insertspriteq = cmd(), + killit = cmd(), + mikesnd = cmd(), + nullop = cmd(), + operate = cmd(), + pkick = cmd(), + pstomp = cmd(), + resetactioncount = cmd(), + resetcount = cmd(), + resetplayer = cmd(), + respawnhitag = cmd(), + tip = cmd(), + tossweapon = cmd(), + wackplayer = cmd(), + + -- sprite searching + findplayer = cmd(D,D), + findotherplayer = cmd(D,D), + findnearspritezvar = cmd(D,R,R,W), + findnearspritez = cmd(D,D,D,W), + findnearspritevar = cmd(D,R,W), + findnearsprite3dvar = cmd(D,R,W), + findnearsprite3d = cmd(D,D,W), + findnearsprite = cmd(D,D,W), + findnearactorzvar = cmd(D,R,R,W), + findnearactorz = cmd(D,D,D,W), + findnearactorvar = cmd(D,R,W), + findnearactor3dvar = cmd(D,R,W), + findnearactor3d = cmd(D,D,W), + findnearactor = cmd(D,D,W), + + -- quotes + qsprintf = sp1 * t_rvar * sp1 * t_rvar * (sp1 * t_rvar)^-32, + qgetsysstr = cmd(R,R), + qstrcat = cmd(R,R), + qstrcpy = cmd(R,R), + qstrlen = cmd(R,R), + qstrncat = cmd(R,R), + qsubstr = cmd(R,R), + + -- array stuff + copy = sp1 * t_identifier * arraypat * sp1 * t_identifier * arraypat, + setarray = sp1 * t_identifier * arraypat * sp1 * t_rvar, + + activatebysector = cmd(R,R), + addlog = cmd(), + addlogvar = cmd(R), + addweaponvar = cmd(R,R), -- exec SPECIAL HANDLING! + cansee = cmd(R,R,R,R,R,R,R,R,W), + canseespr = cmd(R,R,W), + changespritesect = cmd(R,R), + changespritestat = cmd(R,R), + clipmove = cmd(W,W,W,R,W,R,R,R,R,R,R), + clipmovenoslide = cmd(W,W,W,R,W,R,R,R,R,R,R), + displayrand = cmd(W), + displayrandvar = cmd(W,D), + displayrandvarvar = cmd(W,R), + dist = cmd(W), + dragpoint = cmd(R,R,R), + hitscan = cmd(R,R,R,R,R,R,R,W,W,W,W,W,W,R), -- 7R 6W 1R + + -- screen text and numbers display + gametext = cmd(R,R,R,R,R,R,R,R,R,R,R), -- 11 R + gametextz = cmd(R,R,R,R,R,R,R,R,R,R,R,R), -- 12 R + digitalnumber = cmd(R,R,R,R,R,R,R,R,R,R,R), -- 11R + digitalnumberz = cmd(W,R,R,R,R,R,R,R,R,R,R,R), -- 1W 11R + minitext = cmd(R,R,R,R,R), + + ldist = cmd(W), + lineintersect = cmd(R,R,R,R,R,R,R,R,R,R,W,W,W,W), -- 10R 4W + rayintersect = cmd(R,R,R,R,R,R,R,R,R,R,W,W,W,W), -- 10R 4W + loadmapstate = cmd(), + savemapstate = cmd(), + movesprite = cmd(R,R,R,R,R,W), + neartag = cmd(R,R,R,R,R,W,W,W,W,R,R), + operateactivators = cmd(R), + operatesectors = cmd(R), + palfrom = (sp1 * t_define)^-4, + + myos = cmd(R,R,R,R,R), + myospal = cmd(R,R,R,R,R,R), + myospalx = cmd(R,R,R,R,R,R), + myosx = cmd(R,R,R,R,R), + + headspritesect = cmd(R,R), + headspritestat = cmd(R,R), + nextspritesect = cmd(R,R), + nextspritestat = cmd(R,R), + prevspritesect = cmd(R,R), + prevspritestat = cmd(R,R), + + readarrayfromfile = cmd(I,D), + writearraytofile = cmd(I,D), + + redefinequote = sp1 * t_define * newline_term_string, + resizearray = cmd(I,R), + getarraysize = cmd(I,W), + rotatepoint = cmd(R,R,R,R,R,W,W), + rotatesprite = cmd(R,R,R,R,R,R,R,R,R,R,R,R), -- 12R + rotatesprite16 = cmd(R,R,R,R,R,R,R,R,R,R,R,R), -- 12R + sectorofwall = cmd(W,R,R), + sectclearinterpolation = cmd(R), + sectsetinterpolation = cmd(R), + + sectgethitag = cmd(), + sectgetlotag = cmd(), + spgethitag = cmd(), + spgetlotag = cmd(), + + showview = cmd(R,R,R,R,R,R,R,R,R,R), -- 10R + showviewunbiased = cmd(R,R,R,R,R,R,R,R,R,R), -- 10R + smaxammo = cmd(R,R), + gmaxammo = cmd(R,W), + spriteflags = cmd(R), -- also see outer + ssp = cmd(R,R), + startlevel = cmd(R,R), + starttrack = cmd(D), + stopactorsound = cmd(R,R), + stopallsounds = cmd(), + updatesector = cmd(R,R,W), + updatesectorz = cmd(R,R,R,W), + + getactorangle = cmd(W), + setactorangle = cmd(R), + getplayerangle = cmd(W), + setplayerangle = cmd(R), + getangletotarget = cmd(W), + + getceilzofslope = cmd(R,R,R,W), + getflorzofslope = cmd(R,R,R,W), + getcurraddress = cmd(W), -- XXX + getkeyname = cmd(R,R,R), + getpname = cmd(R,R), + gettextureceiling = cmd(), + gettexturefloor = cmd(), + getticks = cmd(W), + gettimedate = cmd(W,W,W,W,W,W,W,W), + getzrange = cmd(R,R,R,R,W,W,W,W,R,R), + + setactorsoundpitch = cmd(R,R,R), + setaspect = cmd(R,R), +} + +local Cif = { + -- XXX: ai, action, move/def labels + ifai = cmd(D), + ifaction = cmd(D), + ifmove = cmd(D), + + ifrnd = cmd(D), + ifpdistl = cmd(D), + ifpdistg = cmd(D), + ifwasweapon = cmd(D), + ifactioncount = cmd(D), + ifcount = cmd(D), + ifactor = cmd(D), + ifstrength = cmd(D), + ifspawnedby = cmd(D), + ifgapzl = cmd(D), + iffloordistl = cmd(D), + ifceilingdistl = cmd(D), + ifphealthl = cmd(D), + ifspritepal = cmd(D), + ifgotweaponce = cmd(D), + ifangdiffl = cmd(D), + ifsound = cmd(D), + ifpinventory = cmd(D,D), + + ifp = (sp1 * t_define)^1, + + ifclient = cmd(), + ifserver = cmd(), + ifonwater = cmd(), + ifinwater = cmd(), + ifactornotstayput = cmd(), + ifactorsound = cmd(), + ifcansee = cmd(), + ifhitweapon = cmd(), + ifsquished = cmd(), + ifdead = cmd(), + ifcanshoottarget = cmd(), + ifhitspace = cmd(), + ifoutside = cmd(), + ifmultiplayer = cmd(), + ifinspace = cmd(), + ifbulletnear = cmd(), + ifrespawn = cmd(), + ifinouterspace = cmd(), + ifnotmoving = cmd(), + ifawayfromwall = cmd(), + ifcanseetarget = cmd(), + ifnosounds = cmd(), + + ifvarl = cmd(R,D), + ifvarg = cmd(R,D), + ifvare = cmd(R,D), + ifvarn = cmd(R,D), + ifvarand = cmd(R,D), + ifvaror = cmd(R,D), + ifvarxor = cmd(R,D), + ifvareither = cmd(R,D), + + ifactorsound = cmd(R,R), + ifvarvarg = cmd(R,R), + ifvarvarl = cmd(R,R), + ifvarvare = cmd(R,R), + ifvarvarn = cmd(R,R), + ifvarvarand = cmd(R,R), + ifvarvaror = cmd(R,R), + ifvarvarxor = cmd(R,R), + ifvarvareither = cmd(R,R), +} + + +-- A generic trace function, prints a position together with the match content +-- A non-existing 'doit' means 'true'. +function TraceFunc(pat, doit) + pat = Pat(pat) + + if (doit==nil or doit) then + pat = lpeg.Cmt(pat, function (subj, pos, a) print(pos..":"..a); return true; end) + end + return pat +end + +-- These are tracers for specific patterns which can be disabled +-- if desired. +local function Keyw(kwname) return TraceFunc(kwname, false) end +local function Stmt(cmdpat) return TraceFunc(cmdpat, false) end + + +-- attach the command names at the front! +function attachnames(kwtab) + for cmdname,cmdpat in pairs(kwtab) do + kwtab[cmdname] = Keyw(cmdname) * cmdpat + end +end + +attachnames(Co) +attachnames(Ci) +attachnames(Cif) + + +-- Takes one or more tables and +'s all its patterns together in the order of +-- appearance. +-- The tables must map command names to their patterns. +function all_alt_pattern(...) + local pat = Pat(false) + local args = {...} + for argi=1,#args do + local pattab = args[argi] + for cmdname,cmdpat in pairs(pattab) do + pat = pat + cmdpat + end + end + return pat +end + +-- actor ORGANTIC is greeting! +local function warn_on_lonely_else(subj, pos) + print(pos..": warning: found `else' with no `if'") + return true +end + +-- About prefixes: I think it's not a problem if e.g. "getactor" comes before +-- "getactorvar", because the pattern for the former will fail eventually in +-- the ordered choice if fed with the latter. +local con_outer_command = all_alt_pattern(Co) +local con_inner_command = all_alt_pattern(Ci) +local con_if_begs = all_alt_pattern(Cif) + +local lone_else = lpeg.Cmt("else" * sp1, warn_on_lonely_else) + +local stmt_list = Var("stmt_list") +-- possibly empty statement list: +local stmt_list_or_eps = (stmt_list * sp1)^-1 + +-- common to all three: [ [ [ [... ]]]] +local common_actor_end = sp1 * t_define * sp1 * (t_define * sp1)^0 * stmt_list_or_eps * "enda" + +--== block delimiters (no recursion) ==-- +local Cb = { + -- actor (...) + actor = common_actor_end, + -- eventloadactor (...) + eventloadactor = common_actor_end, + -- useractor (...) + useractor = sp1 * t_define * common_actor_end, + + onevent = sp1 * t_define * sp1 * stmt_list_or_eps * "endevent", + + state = sp1 * t_identifier * sp1 * stmt_list_or_eps * "ends", +} + +attachnames(Cb) + + +--- The final grammar! +local Grammar = Pat{ + -- The starting symbol. + -- A translation unit is a (possibly empty) sequence of outer CON + -- commands, separated by at least one whitespace which may be + -- omitted at the EOF. + sp0 * ((con_outer_command + all_alt_pattern(Cb)) * (sp1 + (-1)))^0, + + -- Deps. These appear here because we're hitting a limit with LPeg else: + -- http://lua-users.org/lists/lua-l/2008-11/msg00462.html + whitespace = Set(" \t\r") + newline + Set("(),;") + comment + linecomment, + + t_identifier_all = Range("AZ", "az", "__") * Range("AZ", "az", "__", "09")^0, + -- NOTE: -con_keyword alone would be wrong, e.g. "state breakobject": + -- NOTE 2: The + "[" is so that stuff like + -- getactor[THISACTOR].x x + -- getactor[THISACTOR].y y + -- is parsed correctly. (Compared with this:) + -- getactor[THISACTOR].x x + -- getactor [THISACTOR].y y + -- This is in need of cleanup! + t_identifier = -(con_keyword * (sp1 + "[")) * t_identifier_all, + t_define = Pat("-")^-1 * sp0 * (t_identifier + t_number), + + t_arrayexp = t_identifier * arraypat * memberpat^-1, + + switch_stmt = Keyw("switch") * (sp1 * (Var("case") + Var("default")))^0 * sp1 * "endswitch", + case = Keyw("case") * sp1 * t_define * sp0 * Pat(":")^-1 * sp1 * stmt_list_or_eps * "break", + default = Keyw("default") * sp0 * Pat(":")^-1 * sp1 * stmt_list_or_eps * "break", + + -- The "lone" if statement is tested first, so that a potential dangling "else" is + -- attached to the outermost possible "if", as done by CON + if_stmt = con_if_begs * sp1 * Var("single_stmt") * -(sp1 * Pat("else")) + + con_if_begs * sp1 * Var("single_stmt") * sp1 * "else" * sp1 * Var("single_stmt"), + + while_stmt = Keyw("whilevarvarn") * sp1 * t_rvar * sp1 * t_rvar * sp1 * Var("single_stmt") + + Keyw("whilevarn") * sp1 * t_rvar * sp1 * t_define * sp1 * Var("single_stmt"), + + -- TODO: some sp1 --> sp0? + single_stmt = Stmt( + lone_else^-1 * + ( Keyw("{") * sp0 * "}" + + Keyw("{") * sp1 * stmt_list * sp1 * "}" + + (con_inner_command + Var("switch_stmt") + Var("if_stmt") + Var("while_stmt")) +-- + lpeg.Cmt(t_newline_term_str, function (subj, curpos) print("Error at "..curpos) end) + )), + + -- a non-empty statement/command list + stmt_list = Var("single_stmt") * (sp1 * Var("single_stmt"))^0, +} + +local math = require("math") +local string = require("string") + +local function printf(fmt, ...) + print(string.format(fmt, ...)) +end + +-- Returns index into the sorted table tab such that +-- tab[index] <= searchelt < tab[index+1]. +-- Preconditions: +-- tab[i] < tab[i+1] for i < i+1 and 1 < i < #tab +-- tab[1] <= searchelt < tab[#tab] +-- If tab has less than 2 elements, returns nil. +local function bsearch(tab, searchelt) +-- printf("bsearch(tab, %d)", searchelt) + local l, r = 1, #tab + local i + + if (r <= 1) then + return + end + + while (l ~= r) do + i = l + math.ceil((r-l)/2) -- l < i <= r + assert(l < i and i <= r) + local elt = tab[i] +-- printf("l=%d tab[%d]=%d r=%d", l, i, elt, r) + + if (searchelt == elt) then + return i + end + + if (searchelt < elt) then + r = i-1 + else -- (searchelt > elt) + l = i + end + end + +-- printf("return tab[%d]=%d", l, tab[l]) + return l +end + +---=== stand-alone: ===--- +if (not EDUKE32_LUNATIC) then + local io = require("io") + + local filename = arg[1] + assert(filename) + + local contents = io.open(filename):read("*all") + local idx = lpeg.match(Grammar, contents) + + -- newlineidxs will contain the 1-based file offsets to "\n" characters + local newlineidxs = {} + for i in string.gmatch(contents, "()\n") do + newlineidxs[#newlineidxs+1] = i + end + newlineidxs[#newlineidxs+1] = #contents+1 -- dummy newline + + if (not idx) then + print("Match failed.") + return + end + + if (idx == #contents+1) then + print("Matched whole contents.") + return + end + + printf("Match succeeded up to %d (len=%d)", idx, #contents) + + local i = bsearch(newlineidxs, idx) + local bi, ei = newlineidxs[i]+1, newlineidxs[i+1]-1 + printf("Line goes from %d to %d", bi, ei) + print(string.sub(contents, bi, ei)) +end