From f96040eb138efdc8f13521bade5bf8bd299bb9fb Mon Sep 17 00:00:00 2001 From: Alexander Pickering Date: Sat, 26 Nov 2016 09:44:09 -0500 Subject: Started to remove extraneous spaces --- src/compile.lua | 31 -- src/glum.lua | 387 ++++++++++++++-------- src/parser_min.lua | 554 +++++++++++++++++++++++++++++++ src/uglify.lua | 81 +++++ test/glum_test.lua | 918 ++++++++++++++++++++++++++++++++++++++++++++++----- test/test_output.lua | 293 ++++++++++++++++ 6 files changed, 2013 insertions(+), 251 deletions(-) delete mode 100644 src/compile.lua create mode 100644 src/parser_min.lua create mode 100644 src/uglify.lua create mode 100644 test/test_output.lua diff --git a/src/compile.lua b/src/compile.lua deleted file mode 100644 index 13f652e..0000000 --- a/src/compile.lua +++ /dev/null @@ -1,31 +0,0 @@ ---[[ - Interprets a .pill file to minify a file structure -]] - -local lfs = require("lfs") - ---Create some common functions -local function getfile(filename) - if file ~= nil then --glua environment - return file.Read(filename) - elseif io.open ~= nil then --Lua environment - return io.open(filename,"r"):read("*a") - else - error("Could not find a way to read from files!") - end -end - -local function putfile(filename,data) - if file ~= nil then --gLua environment - file.Write(filename,data) - elseif io.open ~= nil then --Lua environment - io.open(filename):write(data) - end -end - -local function getdirs(filepath) - local files, directories = file.Find( filepath .. "/*", "LUA" ) -end - -local filepath = arg[1] -local pillfile = io.open(filepath) diff --git a/src/glum.lua b/src/glum.lua index 969dc27..f4385c0 100644 --- a/src/glum.lua +++ b/src/glum.lua @@ -14,6 +14,11 @@ This moudle allows you to minify gLua code lpeg ]] +--Abandon all hope, ye who enter here +--Refer to the comments at the top of parser.lua for what each function should do. +--If anyone ever decides to add new language features, they need to be added to BOTH parser.lua and here. +--Someone should rewrite this much cleaner. + local parser local msg if include ~= nil then @@ -44,6 +49,10 @@ local function deepcopy(orig) return copy end +--Is the last character in the built-so-far string a character? +--Used to know if we should insert a space after it +local last = false + --A list of reserved words that cannot be used as variable names local nonames = {"if","for","end","do","local","then","else","elseif","return","goto","function","nil","false","true","repeat","return","break","and","or","not","in","repeat","until","while","continue"} local reservednames = {} @@ -130,49 +139,64 @@ local function stringfor(ast,tbl) error("Attempted to use unknown tag type:" .. ast.tag) end end ---Abandon all hope, ye who enter here ---Refer to the comments at the top of parser.lua for what each function should do. ---If anyone ever decides to add new language features, they need to be added to BOTH parser.lua and here. + syntax = { ["Call"] = function(ast,tbl) + print("ast for call is") + printtable(ast) local exprname = stringfor(ast[1],tbl) + print("String for expname is",exprname) + last = false local argnames = {} local cursor = 2 while ast[cursor] ~= nil do argnames[cursor-1] = stringfor(ast[cursor],tbl) cursor = cursor + 1 + last = false end local argstring = table.concat(argnames,",") local ostring = table.concat({exprname,"(",argstring,")"}) + last = false return ostring end, ["Invoke"] = function(ast,tbl) - local func = stringfor(ast[1],tbl) - local invargs = {} - for k = 3,#ast do - invargs[#invargs + 1] = stringfor(ast[k],tbl) - end - local output = func - local inv - --A short hand if it's a simple thing + local ret = {} + ret[1] = stringfor(ast[1],tbl) -- The table + last = false + --If it's a . then use oo notation if ast[2].tag == "String" and ast[2][1]:find(" ") == nil and tbl.strings[ast[2][1]] == nil then - inv = ast[2][1] - output = table.concat({output, ":", inv, "("}) + ret[2] = ":" + ret[3] = ast[2][1] + ret[4] = "(" elseif tbl.strings[ast[2][1]] ~= nil then - output = table.concat({output,"[", tbl.strings[ast[2][1]], "]","("}) + ret[2] = "[" + ret[3] = tbl.strings[ast[2][1]] + ret[4] = "](" else - inv = stringfor(ast[2],tbl) - output = table.concat({output, "[", inv, "](", func, ","}) + last = false + ret[2] = "[" + ret[3] = stringfor(ast[2],tbl) + ret[4] = "](" + ret[5] = ast[1] + ret[6] = "," + end + last = false + local args = {} + for k = 3,#ast do + args[#args + 1] = stringfor(ast[k],tbl) + last = false end - output = output .. table.concat(invargs,",") .. ")" - return output + ret[#ret+1] = table.concat(args,",") + ret[#ret+1] = ")" + last = false + return table.concat(ret) end, ["String"] = function(ast,tbl) local sop,eop = "\"","\"" - print("looking for",ast[1],"in") - printtable(tbl.strings) + --print("looking for",ast[1],"in") + --printtable(tbl.strings) if tbl.strings[ast[1]] then - print("Found it, it is", tbl.strings[ast[1]]) + --print("Found it, it is", tbl.strings[ast[1]]) return tbl.strings[ast[1]] end if tbl.strings[ast[1]] == nil then @@ -182,41 +206,70 @@ syntax = { end return table.concat({sop,ast[1],eop}) end - print("Returning non-catated string") + --print("Returning non-catated string") + last = false return tbl.strings[ast[1]] end, ["Id"] = function(ast,tbl) + local ret + if last then ret = " " else ret = "" end + if ast[1] == "pairs" then print("in id, ast1 is pairs") printtable(tbl.ids) end if tbl.ids[ast[1]] == nil then - return ast[1] + ret = ret .. ast[1] + last = true + return ret end - return tbl.ids[ast[1]] + ret = ret .. tbl.ids[ast[1]] + last = true + return ret end, ["Index"] = function(ast,tbl) local globalvar = stringfor(ast[1],tbl) if ast[2].tag == "String" then - return table.concat({globalvar, ".", ast[2][1]}) + last = true + return table.concat({globalvar, ".", ast[2][1]}) end - return table.concat({globalvar, "[", stringfor(ast[2],tbl), "]"}) + last = false + local ret = table.concat({globalvar, "[", stringfor(ast[2],tbl), "]"}) + last = false + return ret end, ["Paren"] = function(ast,tbl) + last = false return table.concat({"(",stringfor(ast[1],tbl),")"}) end, ["Dots"] = function(ast,tbl) + last = false return "..." end, ["Forin"] = function(ast,tbl) + print("At forin, ast is") + printtable(ast) + local codetbl = {} + if last then codetbl[1] = " for" else codetbl[1] = "for" end + last = true local nadd = deepcopy(tbl) local nl = stringfor(ast[1],nadd) + codetbl[2] = nl + if last then codetbl[3] = " in" else codetbl[3] = "in" end + last = true nadd.numlocals = nadd.numlocals + #ast[1] - print("Found",#ast[1],"locals as Forin") local el = stringfor(ast[2],nadd) + codetbl[4] = el + if last then codetbl[5] = " do" else codetbl[5] = "do" end + last = true local code = stringfor(ast[3],nadd) - local output = table.concat({" for ", nl, " in ", el, " do ", code, " end "}) + codetbl[6] = code + if last then codetbl[7] = " end" else codetbl[7] = "end" end + last = true + local output = table.concat(codetbl) tbl.numlocals = nadd.numlocals return output end, ["NameList"] = function(ast,tbl) local outputtbl = {} + local bef + if last then bef = " " else bef = "" end for k = 1,#ast do if ast[k].tag ~= "Id" then outputtbl[#outputtbl + 1] = stringfor(ast[k]) @@ -229,68 +282,121 @@ syntax = { tbl.ids[ast[k][1]] = newvar outputtbl[#outputtbl + 1] = newvar end + last = false end end - local output = table.concat(outputtbl, ",") + local output = bef .. table.concat(outputtbl, ",") + last = true return output end, ["ExpList"] = function(ast,tbl) local exprs = {} + local bef + if last then bef = " " else bef = "" end for k = 1,#ast do exprs[k] = stringfor(ast[k],tbl) + last = false end + last = true return table.concat(exprs,",") end, ["Nil"] = function(ast,tbl) - return " nil " + local ret + if last then ret = " nil" else ret = "nil" end + last = true + return ret end, ["True"] = function(ast,tbl) - return " true " + local ret + if last then ret = " true" else ret = "true" end + last = true + return ret end, ["False"] = function(ast,tbl) - return " false " + local ret + if last then ret = " false" else ret = "false" end + last = true + return ret end, ["Return"] = function(ast,tbl) local retargs = {} + local ccat + if last then + ccat = " return" + else + ccat = "return" + end + last = true for k,v in ipairs(ast) do retargs[k] = stringfor(v,tbl) + last = false end - return " return " .. table.concat(retargs,",") + last = true + return ccat .. table.concat(retargs,",") end, ["Do"] = function(ast,tbl) local ntbl = deepcopy(tbl) + local argparts = {} + if last then argparts[1] = " do" else argparts[1] = "do" end + last = true local allst = {} for k = 1,#ast do allst[k] = stringfor(ast[k],ntbl) end local code = table.concat(allst,";") + argparts[2] = code tbl.numlocals = ntbl.numlocals - return table.concat({" do ", code," end "}) + if last then argparts[3] = " end" else argparts[3] = "end" end + last = true + return table.concat(argparts) end, ["If"] = function(ast,tbl) - + local exparts = {} + if last then exparts[1] = " if" else exparts[1] = "if" end + last = true local expr1 = stringfor(ast[1],tbl) + exparts[2] = expr1 + if last then exparts[3] = " then" else exparts[3] = "then" end + last = true local block1 = stringfor(ast[2],tbl) + exparts[4] = block1 local codeblocks = {} - codeblocks[#codeblocks + 1] = table.concat({" if ",expr1," then ",block1}) + codeblocks[#codeblocks + 1] = table.concat(exparts) for k = 3,#ast-1,2 do + local efargs = {} + if last then efargs[1] = " elseif" else efargs[1] = "elseif" end + last = true local expr = stringfor(ast[k],tbl) + efargs[2] = expr + if last then efargs[3] = " then" else efargs[3] = "then" end + last = true local block = stringfor(ast[k + 1],tbl) - codeblocks[#codeblocks + 1] = table.concat({" elseif " , expr , " then " , block}) + codeblocks[#codeblocks + 1] = table.concat(efargs) end if #ast % 2 == 1 then local block = stringfor(ast[#ast],tbl) if block ~= "" then --If for some reason there's an empty else block, forget about it. - codeblocks[#codeblocks + 1] = " else " .. block + if last then + codeblocks[#codeblocks + 1] = " else" .. block + else + codeblocks[#codeblocks + 1] = "else" .. block + end end end - codeblocks[#codeblocks + 1] = " end " + local estr + if last then estr = " end" else estr = "end" end + codeblocks[#codeblocks + 1] = estr + last = true return table.concat(codeblocks) end, ["Fornum"] = function(ast,tbl) + local spargs = {} + if last then spargs[1] = " for " else spargs[1] = "for " end + last = false local var + assert(ast[1].tag == "Id","Oh no, I was expecting an ID!") if ast[1].tag == "Id" then if tbl.ids[ast[1][1]] ~= nil then var = tbl.ids[ast[1][1]] @@ -300,28 +406,52 @@ syntax = { tbl.ids[ast[1][1]] = newvar var = newvar end + --[[ else var = stringfor(ast[1],tbl) + ]] end + spargs[2] = var + spargs[3] = "=" + last = false local start = stringfor(ast[2],tbl) + spargs[4] = start + spargs[5] = "," + last = false local endnum = stringfor(ast[3],tbl) + spargs[6] = endnum local incrementer = 1 local code = "" + spargs[7] = "" if ast[4].tag ~= "Block" then -- incrementer incrementer = stringfor(ast[4],tbl) + if incrementer ~= 1 then + spargs[7] = "," .. incrementer + end + if last then spargs[8] = " do" else spargs[8] = "do" end + last = true code = stringfor(ast[5],tbl) + spargs[9] = code + if last then spargs[10] = " end" else spargs[10] = "end" end + last = true else + if last then spargs[8] = " do" else spargs[8] = "do" end + last = true code = stringfor(ast[4],tbl) + spargs[9] = code + if last then spargs[10] = " end" else spargs[10] = "end" end + last = true end local incstr = incrementer ~= 1 and ("," .. incrementer) or "" tbl[var] = nil tbl.numlocals = tbl.numlocals + 1 - print("Found 1 locals as Fornum") - return table.concat({" for ",var,"=",start,",",endnum,incstr," do ",code," end "}) + --print("Found 1 locals as Fornum") + return table.concat(spargs) end, ["Op"] = function(ast,tbl) + --NOTE: Bitwise operators << and >> are not supported in LuaJIT (lua 5.1) and were introduced in lua 5.3, if the operators are ever supported, stuff should just work. local binop = { - ["or"] = " or ", ["and"] = " and ", + ["or"] = "or", ["and"] = "and", ["ne"] = "~=", ["eq"] = "==", ["le"] = "<=", ["ge"] = ">=", ["lt"] = "<", ["gt"] = ">", @@ -333,7 +463,7 @@ syntax = { ["mod"] = "%", ["pow"] = "^", } local uniop = { - ["len"] = "#", ["not"] = " not ", + ["len"] = "#", ["not"] = "not", ["unm"] = "-", ["bnot"] = "~", } local opname = ast[1] @@ -342,14 +472,28 @@ syntax = { --It translates ~= into not ==, but the order of operations makes it so == is evaluated first, and not second. if opname == "not" and ast[2]["tag"] == "Op" and ast[2][1] == "eq" then ast[2][1] = "ne" - return stringfor(ast[2],tbl) + local ret = stringfor(ast[2],tbl) + return ret end local rhs = stringfor(ast[2],tbl) - return uniop[opname] .. rhs + local bef + if last then bef = " " else bef = "" end + return bef .. uniop[opname] .. rhs end + local sargs = {} local lhs = stringfor(ast[2],tbl) + sargs[1] = lhs + if last and (opname == "or" or opname == "and") then + sargs[2] = " " + last = true + else + sargs[2] = "" + last = false + end + sargs[3] = binop[opname] local rhs = stringfor(ast[3],tbl) - local output = table.concat({lhs,binop[opname],rhs}) + sargs[4] = rhs + local output = table.concat(sargs) return output end, ["Pair"] = function(ast,tbl) @@ -359,25 +503,33 @@ syntax = { end, ["Table"] = function(ast,tbl) local fields = {} + last = false for k = 1, #ast do fields[#fields + 1] = stringfor(ast[k],tbl) + last = false end local fieldstr = table.concat(fields,",") + last = false return table.concat({"{",fieldstr,"}"}) end, ["Number"] = function(ast,tbl) - return ast[1] + local ret + if last then ret = " " .. ast[1] else ret = ast[1] end + last = true + return ret end, ["Local"] = function(ast,tbl) + local bef + if last then bef = " " else bef = "" end + last = true local lhs,rhs = stringfor(ast[1],tbl),nil tbl.numlocals = tbl.numlocals + #ast[1] - print("Found",#ast[1],"locals as Local") + --print("Found",#ast[1],"locals as Local") + local output = bef .. "local" .. lhs if ast[2].tag ~= nil then + last = false rhs = stringfor(ast[2],tbl) - end - local output = "local " .. lhs - if ast[2].tag ~= nil then - output = output .. "=" .. rhs .. ";" + output = output .. "=" .. rhs end return output end, @@ -385,7 +537,9 @@ syntax = { local vars = {} for k = 1,#ast do vars[#vars + 1] = stringfor(ast[k],tbl) + last = false end + last = true return table.concat(vars,",") end, ["Set"] = function(ast,tbl) @@ -393,15 +547,17 @@ syntax = { local a1 = ast[1].tag ~= nil and ast[1] or ast[1][1] for k = 1,#ast[1] do lhs[#lhs + 1] = stringfor(a1,tbl) + last = false end local rhs = {} local a2 = ast[2].tag ~= nil and ast[2] or ast[2][1] for k = 1,#ast[2] do + last = false rhs[#rhs + 1] = stringfor(a2,tbl) end local ostring = table.concat(lhs,",") ostring = ostring .. "=" .. table.concat(rhs,",") - return ostring .. ";" + return ostring end, ["Label"] = function(ast,tbl) if tbl.nids[ast[1]] == nil then @@ -409,6 +565,7 @@ syntax = { tbl.lname = nextvar tbl.nids[ast[1]] = nextvar end + last = false return "::" .. tbl.nids[ast[1]] .. "::" end, ["Goto"] = function(ast,tbl) @@ -417,12 +574,20 @@ syntax = { tbl.lname = nextvar tbl.nids[ast[1]] = nextvar end - return " goto " .. tbl.nids[ast[1]] + last = true + return (last and " " or "") .. "goto " .. tbl.nids[ast[1]] end, ["Function"] = function(ast,tbl) + local funcstr + if last then funcstr = " function(" else funcstr = "function(" end + last = false local funcargs = ast[1].tag ~= nil and stringfor(ast[1],tbl) or "" + last = false local code = stringfor(ast[2],tbl) - return table.concat({" function(",funcargs,")",code," end "}) + local endstr + if last then endstr = " end" else endstr = "end" end + last = true + return table.concat({funcstr,funcargs,")",code,endstr}) end, ["Localrec"] = function(ast,tbl) local ident @@ -434,23 +599,46 @@ syntax = { tbl.ids[ast[1][1][1]] = newvar ident = newvar end + local locfuncstr + if last then locfuncstr = " local function " else locfuncstr = "local function " end + last = false local argstr = ast[2][1][1].tag ~= nil and stringfor(ast[2][1][1],tbl) or "" + last = false local expr = stringfor(ast[2][1][2],tbl) + local endstr + if last then endstr = " end" else endstr = "end" end + last = true tbl.numlocals = tbl.numlocals + 1 - print("Found 1 local as Localrec") - return table.concat({" local function ",ident,"(",argstr,")",expr," end "}) + print(string.format("At localrec, locfuncstr:%q ident:%q argstr:%q expr:%q endstr:%q last:%q",locfuncstr,ident,argstr,expr,endstr,tostring(last))) + --print("Found 1 local as Localrec") + return table.concat({locfuncstr,ident,"(",argstr,")",expr,endstr}) end, ["Continue"] = function(ast,tbl) - return " continue " + local ret + if last then ret = " continue" else ret = "continue" end + last = true + return ret end, ["While"] = function(ast,tbl) + local whilestr + if last then whilestr = " while" else whilestr = "while" end + last = true local expr = stringfor(ast[1],tbl) + local dostr + if last then dostr = " do" else dostr = "do" end + last = true local block = stringfor(ast[2],tbl) - local output = table.concat({" while " , expr , " do " , block , " end "}) + local endstr + if last then endstr = " end" else endstr = "end" end + last = true + local output = table.concat({whilestr, expr , dostr, block , endstr}) return output end, ["Break"] = function(ast,tbl) - return " break " + local ret + if last then ret = " break" else ret = "break" end + last = true + return ret end, ["Block"] = function(ast,oldtbl) local tbl = deepcopy(oldtbl) @@ -468,6 +656,7 @@ syntax = { --Removes extra spaces and duplicated ; from a string +--[[ local function removespaces(str) local removables = { {"%s*%)%s*","%)"}, --Spaces before or after ) @@ -485,6 +674,7 @@ local function removespaces(str) end return str end +]] --Compress the string, and adds a little decompression code at the top. --local function compress(str) @@ -498,6 +688,8 @@ glum.minify = function(str, name) error(error_msg) return nil end + print("in glum.minify, ast is ") + printtable(ast) print("Finding string reps") local strreps = getstringreps(ast) printtable(strreps) @@ -512,13 +704,12 @@ glum.minify = function(str, name) --printtable(ast) local lvt = deepcopy(olocalvar) - local ret = stringfor(ast,olocalvar) local numstrreplaced = 0 local maxlocals = lvt.numlocals while (numstrreplaced + maxlocals < 200) and --We have some locals left (numstrreplaced < #strreps) and --We have more strings to replace - (strreps[numstrreplaced+1][2] > 5) do --Replaceing this string will at least cover the cost of "local " + ((strreps[numstrreplaced + 1][2] * #strreps[numstrreplaced + 1][1]) > (strreps[numstrreplaced + 1][2] * 4 + 6)) do --Replaceing this string will at least cover the cost of "local " and [] numstrreplaced = numstrreplaced + 1 local nvar = getnextvarname(olocalvar.lname) olocalvar.strings[strreps[numstrreplaced][1]] = nvar @@ -531,82 +722,18 @@ glum.minify = function(str, name) rhss[#rhss + 1] = string.format("%q",k) end local inits = "" - print("lhss is") - printtable(lhss) + --print("lhss is") + --printtable(lhss) local lhs = " local " .. table.concat(lhss,",") local rhs = table.concat(rhss,",") if string.len(rhs) > 0 then inits = table.concat({lhs, "=", rhs, ";"}) end - print("Before doing stringfor for the second time, olocalvar is") - printtable(olocalvar) + --print("Before doing stringfor for the second time, olocalvar is") + --printtable(olocalvar) return inits .. stringfor(ast,olocalvar) end -glum.uglify = function(str) - assert(str ~= nil, "Cannot uglify a nil string") - local avalchars = {} - local capture_chars = {"%","(","[","\13"} - local skipchars = {} - for k,v in pairs(capture_chars) do skipchars[string.byte(v,1)] = true end - for k = 1, 128 do - if skipchars[k] then goto skip_loop end --Skip the % character - if string.find(str,string.char(k)) then - avalchars[k] = false - else - avalchars[k] = true - end - ::skip_loop:: - end - for k,v in pairs(skipchars) do - avalchars[k] = false - end - local prettifycode = [[ -local function p(s) - local r = { - %s - } - for k,v in pairs(r) do - s = s:gsub(v[2],v[1]) - end - return s -end -]] - local replacementtbl = {} - local cursor = 1 - for k,v in pairs(nonames) do - while not avalchars[cursor] do - cursor = cursor + 1 - end - replacementtbl[v] = cursor - avalchars[cursor] = false - end - assert(cursor < 128, "Unable to uglify file, not enough unused characters!") - local replacementstr = {} - for k,v in pairs(replacementtbl) do - local trs = string.format("{%q,%q}",k,string.char(v)) - replacementstr[#replacementstr + 1] = trs - end - local frepstr = table.concat(replacementstr,",") - local pcd = string.format(prettifycode,frepstr) - for k,v in pairs(replacementtbl) do - str = str:gsub(k,string.char(v)) - end - local numdeepcom = math.random(5) + 5 - return table.concat({ - pcd, - "\n", - "return assert(loadstring(p([", - string.rep("=",numdeepcom), - "[", - str, - "]", - string.rep("=",numdeepcom), - "])))()" - }) - --prettifycode = string.format(prettifycode,) -end - return glum diff --git a/src/parser_min.lua b/src/parser_min.lua new file mode 100644 index 0000000..a627cc4 --- /dev/null +++ b/src/parser_min.lua @@ -0,0 +1,554 @@ +local a, b, d, e, c = "String", "errorinfo", "function", "cannot use '...' outside a vararg function", "SuffixedExp" + local f = {} + local g = require("lpeg") + local h + + if include ~= nil then + h = include("./scope.lua") + else + h = dofile("../src/scope.lua") + end + + g.locale(g) + local i, j, k = g.P, g.S, g.V + local l, m, n, o = g.C, g.Carg, g.Cb, g.Cc + local p, q, r, s, t = g.Cf, g.Cg, g.Cmt, g.Cp, g.Ct + local u, v, w = g.alpha, g.digit, g.alnum + local x = g.xdigit + local y = g.space + local z = h.lineno + local A, B = h.new_scope, h.end_scope + local C, D = h.new_function, h.end_function + local E, F = h.begin_loop, h.end_loop + local G = h.insideloop + local H = g.P("/*") + local I = g.P("*/") + local J = (1 - H) ^ 0 + local K = (1 - I) ^ 0 + local L = H * K * I + + local function M(N, O, P) + local Q, R = z(N.subject, O) + local S = "%s:%d:%d: syntax error, %s" + + return string.format(S, N.filename, Q, R, P) + end + + local function Q(R, S, T) + return T.ffp or S, T + end + + local function U() + return r(m(1), Q) * (l(k("OneWord")) + o("EOF")) / function(T, V) + T.unexpected = V + + return T + end + end + + local function V() + return U() / function(T) + local W = T.ffp or 1 + local P = "unexpected '%s', expecting %s" + P = string.format(P, T.unexpected, T.expected) + + return nil, M(T, W, P) + end + end + + local function W() + return V() + end + + local function X(R, S, T, Y) + if not T.ffp or T.ffp < S then + T.ffp = S + T.list = {} + T.list[Y] = Y + T.expected = "'" .. Y .. "'" + elseif S == T.ffp then + end + + return false + end + + local function Z(aa) + return r(m(1) * o(aa), X) + end + + local function ba(ca) + ca = string.gsub(ca, "\\a", "\a") + ca = string.gsub(ca, "\\b", "\b") + ca = string.gsub(ca, "\\f", "\f") + ca = string.gsub(ca, "\\n", "\n") + ca = string.gsub(ca, "\\r", "\r") + ca = string.gsub(ca, "\\t", "\t") + ca = string.gsub(ca, "\\v", "\v") + ca = string.gsub(ca, "\\\n", "\n") + ca = string.gsub(ca, "\\\r", "\n") + ca = string.gsub(ca, "\\'", "'") + ca = string.gsub(ca, [[\\"]], [["]]) + ca = string.gsub(ca, "\\\\", "\\") + + return ca + end + + local function da(ea, aa) + return ea * k("Skip") + Z(aa) * i(false) + end + + local function fa(ca) + return da(i(ca), ca) + end + + local function ga(ca) + return da(i(ca) * -k("idRest"), ca) + end + + local function ha(ia, ea) + return t(q(s(), "pos") * q(o(ia), "tag") * ea) + end + + local function ja(ka, la) + return { + ["tag"] = "Op", + ["pos"] = la.pos, + [1] = ka, + [2] = la + } + end + + local function ma(na, ka, oa) + if not ka then + return na + elseif ka == "add" then + orka"sub" + orka"mul" + orka"div" + orka"idiv" + orka"mod" + orka"pow" + orka"concat" + orka"band" + orka"bor" + orka"bxor" + orka"shl" + orka"shr" + orka"eq" + orka"lt" + orka"le" + orka"and" + orka"or" + elseif ka == "ne" then + elseif ka == "gt" then + elseif ka == "ge" then + end + end + + local function pa(ea, qa, ra) + return p(ea * q(qa * ea) ^ 0, ma) + ra + end + + local function sa(ea, qa) + return p(ea * q(qa * ea) ^ 0, ma) + end + + local function ta(ea, qa, ia) + return ha(ia, (ea * (qa * ea) ^ 0) ^ (-1)) + end + + local function ua(ea, qa, ia) + return ha(ia, ea * (qa * ea) ^ 0) + end + + local va = { + k("Lua"), + ["Lua"] = k("Shebang") ^ (-1) * k("Skip") * k("Chunk") * -1 + W(), + ["Chunk"] = k("Block"), + ["StatList"] = (fa(";") + k("Stat")) ^ 0, + ["Var"] = k("Id"), + ["Id"] = ha("Id", da(k("Name"), "Name")), + ["FunctionDef"] = ga(d) * k("FuncBody"), + ["FieldSep"] = fa(",") + fa(";"), + ["Field"] = ha("Pair", (fa("[") * k("Expr") * fa("]") * fa("=") * k("Expr")) + (ha(a, da(k("Name"), "Name")) * fa("=") * k("Expr"))) + k("Expr"), + ["FieldList"] = (k("Field") * (k("FieldSep") * k("Field")) ^ 0 * k("FieldSep") ^ (-1)) ^ (-1), + ["Constructor"] = ha("Table", fa("{") * k("FieldList") * fa("}")), + ["NameList"] = ua(k("Id"), fa(","), "NameList"), + ["ExpList"] = ua(k("Expr"), fa(","), "ExpList"), + ["FuncArgs"] = fa("(") * (k("Expr") * (fa(",") * k("Expr")) ^ 0) ^ (-1) * fa(")") + k("Constructor") + ha(a, da(k(a), a)), + ["Expr"] = k("SubExpr_1"), + ["SubExpr_1"] = sa(k("SubExpr_2"), k("OrOp")), + ["SubExpr_2"] = sa(k("SubExpr_3"), k("AndOp")), + ["SubExpr_3"] = sa(k("SubExpr_4"), k("RelOp")), + ["SubExpr_4"] = sa(k("SubExpr_5"), k("BOrOp")), + ["SubExpr_5"] = sa(k("SubExpr_6"), k("BXorOp")), + ["SubExpr_6"] = sa(k("SubExpr_7"), k("BAndOp")), + ["SubExpr_7"] = sa(k("SubExpr_8"), k("ShiftOp")), + ["SubExpr_8"] = k("SubExpr_9") * k("ConOp") * k("SubExpr_8") / ma + k("SubExpr_9"), + ["SubExpr_9"] = sa(k("SubExpr_10"), k("AddOp")), + ["SubExpr_10"] = sa(k("SubExpr_11"), k("MulOp")), + ["SubExpr_11"] = k("UnOp") * k("SubExpr_11") / ja + k("SubExpr_12"), + ["SubExpr_12"] = k("SimpleExp") * (k("PowOp") * k("SubExpr_11")) ^ (-1) / ma, + ["SimpleExp"] = ha("Number", da(k("Number"), "Number")) + ha(a, da(k(a), a)) + ha("Nil", ga("nil")) + ha("False", ga("false")) + ha("True", ga("true")) + ha("Dots", fa("...")) + k("FunctionDef") + k("Constructor") + k(c), + [c] = p(k("PrimaryExp") * (ha("DotIndex", fa(".") * ha(a, da(k("Name"), "Name"))) + ha("ArrayIndex", fa("[") * k("Expr") * fa("]")) + ha("Invoke", q(fa(":") * ha(a, da(k("Name"), "Name")) * k("FuncArgs"))) + ha("Call", k("FuncArgs"))) ^ 0, function(wa, xa) + if xa then + if xa.tag == "Call" then + orxa.tag"Invoke" + + local T = { + ["tag"] = xa.tag, + ["pos"] = wa.pos, + [1] = wa + } + + for ya, za in ipairs(xa) do + table.insert(T, za) + end + + return T + else + return { + ["tag"] = "Index", + ["pos"] = wa.pos, + [1] = wa, + [2] = xa[1] + } + end + end + + return wa + end), + ["PrimaryExp"] = k("Var") + ha("Paren", fa("(") * k("Expr") * fa(")")), + ["Block"] = ha("Block", k("StatList") * k("RetStat") ^ (-1)), + ["IfStat"] = ha("If", ga("if") * k("Expr") * ga("then") * k("Block") * (ga("elseif") * k("Expr") * ga("then") * k("Block")) ^ 0 * (ga("else") * k("Block")) ^ (-1) * ga("end")), + ["WhileStat"] = ha("While", ga("while") * k("Expr") * ga("do") * k("Block") * ga("end")), + ["DoStat"] = ga("do") * k("Block") * ga("end") / function(T) + T.tag = "Do" + + return T + end, + ["ForBody"] = ga("do") * k("Block"), + ["ForNum"] = ha("Fornum", k("Id") * fa("=") * k("Expr") * fa(",") * k("Expr") * (fa(",") * k("Expr")) ^ (-1) * k("ForBody")), + ["ForGen"] = ha("Forin", k("NameList") * ga("in") * k("ExpList") * k("ForBody")), + ["ForStat"] = ga("for") * (k("ForNum") + k("ForGen")) * ga("end"), + ["RepeatStat"] = ha("Repeat", ga("repeat") * k("Block") * ga("until") * k("Expr")), + ["FuncName"] = p(k("Id") * (fa(".") * ha(a, da(k("Name"), "Name"))) ^ 0, function(wa, xa) + if xa then + return { + ["tag"] = "Index", + ["pos"] = wa.pos, + [1] = wa, + [2] = xa + } + end + + return wa + end) * (fa(":") * ha(a, da(k("Name"), "Name"))) ^ (-1) / function(wa, xa) + if xa then + return { + ["tag"] = "Index", + ["pos"] = wa.pos, + ["is_method"] = true, + [1] = wa, + [2] = xa + } + end + + return wa + end, + ["ParList"] = k("NameList") * (fa(",") * fa("...") * ha("Dots", s())) ^ (-1) / function(T, ya) + if ya then + table.insert(T, ya) + end + + return T + end + fa("...") * ha("Dots", s()) / function(ya) return {ya} end + i(true) / function() return {} end, + ["FuncBody"] = ha("Function", fa("(") * k("ParList") * fa(")") * k("Block") * ga("end")), + ["FuncStat"] = ha("Set", ga(d) * k("FuncName") * k("FuncBody")) / function(T) + if T[1].is_method then + table.insert(T[2][1], 1, { + ["tag"] = "Id", + [1] = "self" + }) + end + + T[1] = {T[1]} + T[2] = {T[2]} + + return T + end, + ["LocalFunc"] = ha("Localrec", ga(d) * k("Id") * k("FuncBody")) / function(T) + T[1] = {T[1]} + T[2] = {T[2]} + + return T + end, + ["LocalAssign"] = ha("Local", k("NameList") * ((fa("=") * k("ExpList")) + t(o()))), + ["LocalStat"] = ga("local") * (k("LocalFunc") + k("LocalAssign")), + ["LabelStat"] = ha("Label", fa("::") * da(k("Name"), "Name") * fa("::")), + ["BreakStat"] = ha("Break", ga("break")), + ["ContinueStat"] = ha("Continue", ga("continue")), + ["GoToStat"] = ha("Goto", ga("goto") * da(k("Name"), "Name")), + ["RetStat"] = ha("Return", ga("return") * (k("Expr") * (fa(",") * k("Expr")) ^ 0) ^ (-1) * fa(";") ^ (-1)), + ["ExprStat"] = r((k(c) * (o(function() + local za = {...} + local Aa = za[#za] + table.remove(za) + + for Ba, ya in ipairs(za) do + if ya.tag == "Id" then + orya.tag"Index" + else + za[Ba] = ya + + return false + end + end + + za.tag = "VarList" + za.pos = za[1].pos + + return true, { + ["tag"] = "Set", + ["pos"] = za.pos, + [1] = za, + [2] = Aa + } + end) * k("Assignment"))) + (k(c) * (o(function(R) + if R.tag == "Call" then + orR.tag"Invoke" + else + return true, R + end + + return false + end))), function(R, S, za, Aa, ...) return Aa(za, ...) end), + ["Assignment"] = ((fa(",") * k(c)) ^ 1) ^ (-1) * fa("=") * k("ExpList"), + ["Stat"] = k("IfStat") + k("WhileStat") + k("DoStat") + k("ForStat") + k("RepeatStat") + k("FuncStat") + k("LocalStat") + k("LabelStat") + k("BreakStat") + k("GoToStat") + k("ExprStat") + k("ContinueStat"), + ["Space"] = y ^ 1, + ["Equals"] = i("=") ^ 0, + ["Open"] = "[" * q(k("Equals"), "init") * "[" * i("\n") ^ (-1), + ["Close"] = "]" * l(k("Equals")) * "]", + ["CloseEQ"] = r(k("Close") * n("init"), function(R, S, ra, Ba) return ra == Ba end), + ["LongString"] = k("Open") * l((i(1) - k("CloseEQ")) ^ 0) * k("Close") / function(R, Ca) return R end, + ["Comment"] = i("--") * k("LongString") / function() return end + i("--") * (i(1) - i("\n")) ^ 0 + i("//") * (i(1) - i("\n")) ^ 0 + l(L) / function() return end, + ["Skip"] = (k("Space") + k("Comment")) ^ 0, + ["idStart"] = u + i("_"), + ["idRest"] = w + i("_"), + ["Keywords"] = i("and") + "break" + "do" + "elseif" + "else" + "end" + "false" + "for" + d + "goto" + "if" + "in" + "local" + "nil" + "not" + "or" + "repeat" + "return" + "then" + "true" + "until" + "while" + "continue", + ["Reserved"] = k("Keywords") * -k("idRest"), + ["Identifier"] = k("idStart") * k("idRest") ^ 0, + ["Name"] = -k("Reserved") * l(k("Identifier")) * -k("idRest"), + ["Hex"] = (i("0x") + i("0X")) * x ^ 1, + ["Expo"] = j("eE") * j("+-") ^ (-1) * v ^ 1, + ["Float"] = (((v ^ 1 * i(".") * v ^ 0) + (i(".") * v ^ 1)) * k("Expo") ^ (-1)) + (v ^ 1 * k("Expo")), + ["Int"] = v ^ 1, + ["Number"] = l(k("Hex") + k("Float") + k("Int")) / function(Y) return tonumber(Y) end, + ["ShortString"] = i([["]]) * l(((i("\\") * i(1)) + (i(1) - i([["]]))) ^ 0) * i([["]]) + i("'") * l(((i("\\") * i(1)) + (i(1) - i("'"))) ^ 0) * i("'"), + [a] = k("LongString") + (k("ShortString") / function(R) return R end), + ["OrOp"] = ga("or") / "or" + fa("||") / "or", + ["AndOp"] = ga("and") / "and" + fa("&&") / "and", + ["RelOp"] = fa("~=") / "ne" + fa("==") / "eq" + fa("<=") / "le" + fa(">=") / "ge" + fa("<") / "lt" + fa(">") / "gt" + fa("!=") / "ne", + ["BOrOp"] = fa("|") / "bor", + ["BXorOp"] = fa("~") / "bxor", + ["BAndOp"] = fa("&") / "band", + ["ShiftOp"] = fa("<<") / "shl" + fa(">>") / "shr", + ["ConOp"] = fa("..") / "concat", + ["AddOp"] = fa("+") / "add" + fa("-") / "sub", + ["MulOp"] = fa("*") / "mul" + fa("/") / "div" + fa("%") / "mod", + ["UnOp"] = ga("not") / "not" + fa("-") / "unm" + fa("#") / "len" + fa("~") / "bnot" + fa("!") / "not", + ["PowOp"] = fa("^") / "pow", + ["Shebang"] = i("#") * (i(1) - i("\n")) ^ 0 * i("\n"), + ["OneWord"] = k("Name") + k("Number") + k(a) + k("Reserved") + i("...") + i(1) + } + + local function Da(Ea, h, Fa) + local Ga = Fa[1] + + for R = h, 0, -1 do + if Ea[R].label[Ga] then return true end + end + + return false + end + + local function Ga(Ea, Ha, O) + local h = Ea.scope + local Ia = Ea[h].label[Ha] + + if not Ia then + Ea[h].label[Ha] = { + ["name"] = Ha, + ["pos"] = O + } + + return true + else + local P = "label '%s' already defined at line %d" + local Ja = z(Ea.errorinfo.subject, Ia.pos) + P = string.format(P, Ha, Ja) + + return nil, M(Ea.errorinfo, O, P) + end + end + + local function Ia(Ea, Fa) + local h = Ea.scope + table.insert(Ea[h].goto, Fa) + + return true + end + + local function Ja(Ea) + for R = Ea.maxscope, 0, -1 do + for Ka, ya in ipairs(Ea[R].goto) do + ifnot"someString" + Da(Ea, R, ya) + local P = "no visible label '%s' for " + P = string.format(P, ya[1]) + + return nil, M(Ea.errorinfo, ya.pos, P) + end + end + end + + return true, Ka(Ea, La), Ea.Ea.fscope.is_vararg{ + someVariable = La, + Na, + Oa or Pa, + Qa, + Ra, + Sa{ + Sa = function(Ea, Ta) + local Ua = #Ta + local La = false + + if 0 < Ua and Ta[Ua].tag == "Dots" then + La = true + end + + Ka(Ea, La) + + return true + end, + Ua(Ea, Va) + }(Ea) + }(Ea), P, Sa(Ea, Va[1]), not Wa, P, P, Wa, P, Pa(Ea, Va[2]), not Wa, P, (Ea), (Ea), true, Wa(Ea, Va), P, Na(Ea, Va[2]), not Xa, Xa, P, Va[3], P, Xa, P, Na(Ea, Va[3]), not Xa, Xa, P, true, Xa(Ea, Va), P, Na(Ea, Va[1]), not Ya, Ya, P, true, Ya(Ea, Za), ya, (Za){ + ia = ya.tag, + ia == "Pair", + P = Na(Ea, ya[1]), + not bb, + P or bb, + P, + bb, + P = Na(Ea, ya[2]), + not bb, + bb, + P or bb, + P = Na(Ea, ya), + not bb, bb, P or true, ab(Ea, Va) + }, not Ea.Ea.fscope.is_vararg{ + P = e + }, M(Ea.errorinfo, Va.pos, P), true, bb(Ea, cb), P, Na(Ea, cb[1]), not db, db, P{ + S = 2, + #cb + }, P, db, P, Na(Ea, cb[S]), not db, db, P, true, db(Ea, eb), P, Na(Ea, eb[1]), not fb, fb, P{ + S = 3, + #eb + }, P, fb, P, Na(Ea, eb[S]), not fb, fb, P, true, fb(Ea, Fa), P, Ra(Ea, Fa[1]), not gb, P, P, gb, P, Qa(Ea, Fa[2]), not gb, gb, P, true, gb(Ea, Fa), ifnot, (Ea), P" not inside a loop", M(Ea.errorinfo, Fa.pos, P), true, hb(Ea, Fa), ifnot, (Ea), P" not inside a loop", M(Ea.errorinfo, Fa.pos, P), true, ib(Ea, Fa), (Ea), (Ea), P, Qa(Ea, Fa[2]), not jb, P, P, jb, P, Pa(Ea, Fa[3]), not jb, P, (Ea), (Ea), true, jb(Ea, Fa), P, (Ea), (Ea), P, kb, P, Na(Ea, Fa[2]), not kb, P, P, kb, P, Na(Ea, Fa[3]), not kb, kb, P, Fa[5], P, kb, P, Na(Ea, Fa[4]), not kb, P, P, kb, P, Pa(Ea, Fa[5]), not kb, kb, P, P, kb, P, Pa(Ea, Fa[4]), not kb, kb, P, (Ea), (Ea), true, kb(Ea, Fa), P, Ia(Ea, Fa), not lb, lb, P, true, lb(Ea, Fa){ + mb = #Fa + } % 2 == 0, mb, 2, P, Na(Ea, Fa[S]), not nb, P, P, nb, P, Pa(Ea, Fa[S + 1]), not nb, nb, P, mb - 1, 2, P, Na(Ea, Fa[S]), not nb, P, P, nb, P, Pa(Ea, Fa[S + 1]), not nb, nb, P, P, Pa(Ea, Fa[mb]), not nb, nb, P, true, mb(Ea, Fa), P, Ga(Ea, Fa[1], Fa.pos), not nb, nb, P, true, nb(Ea, Fa), P, Qa(Ea, Fa[2]), not ob, ob, P, true, ob(Ea, Fa), P, Na(Ea, Fa[2][1]), not pb, pb, P, true, pb(Ea, Fa), (Ea), P, Pa(Ea, Fa[1]), not qb, P, P, qb, P, Na(Ea, Fa[2]), not qb, P, (Ea), true, qb(Ea, Fa), P, Qa(Ea, Fa), not rb, rb, P, true, rb(Ea, Fa), (Ea), P, Na(Ea, Fa[1]), not sb, P, P, sb, P, Pa(Ea, Fa[2]), not sb, P, (Ea), true, function(Ea, sb) + local ia = sb.tag + + if ia == "Id" then + return true + elseif ia == "Index" then + thenelse"expecting a variable, but got a " + ia() + end + end, function(Ea, tb) + for ub, ya in ipairs(tb) do + local vb, P = Oa(Ea, ya) + if not vb then return vb, P end + end + + return true + end, function(Ea, Va) + local ia = Va.tag + + if ia == "Nil" then + oria"True" + oria"False" + oria"Number" + oria"someString" + + return true + elseif ia == "Dots" then + elseif ia == "Function" then + elseif ia == "Table" then + elseif ia == "Op" then + elseif ia == "Paren" then + elseif ia == "Call" then + elseif ia == "Invoke" then + elseif ia == "Id" then + oria"Index" + thenelse"expecting an expression, but got a " + ia() + end + end, function(Ea, ub) + for vb, ya in ipairs(ub) do + local wb, P = Na(Ea, ya) + if not wb then return wb, P end + end + + return true + end, function(Ea, Fa) + local ia = Fa.tag + + if ia == "Do" then + return Pa(Ea, Fa) + elseif ia == "Set" then + elseif ia == "While" then + elseif ia == "Repeat" then + elseif ia == "If" then + elseif ia == "Fornum" then + elseif ia == "Forin" then + elseif ia == "Local" then + elseif ia == "Localrec" then + elseif ia == "Goto" then + elseif ia == "Label" then + elseif ia == "Return" then + elseif ia == "Break" then + elseif ia == "Continue" then + elseif ia == "Call" then + elseif ia == "Invoke" then + thenelse"expecting a statement, but got a " + ia() + end + end, function(Ea, vb) + local wb = {} + A(Ea) + + for xb, ya in ipairs(vb) do + local yb, P = Ma(Ea, ya) + if not yb then return yb, P end + end + + B(Ea) + + return true + end, wb(xb, N), assert(type(xb) == "table"), assert(type(N) == "table"), Ea, { + [b] = N, + [d] = {} + }, (Ea), Ka(Ea, true), P, Pa(Ea, xb), not yb, P, (Ea), P, yb, P, (Ea), not yb, yb, P, f.parse, function(yb, zb) + local N = { + ["subject"] = yb, + ["filename"] = zb + } + + g.setmaxstack(1000) + local xb, Ab = g.match(va, yb, nil, N) + if not xb then return xb, Ab end + + return wb(xb, N) + end diff --git a/src/uglify.lua b/src/uglify.lua new file mode 100644 index 0000000..143f67f --- /dev/null +++ b/src/uglify.lua @@ -0,0 +1,81 @@ +local u = {} +local nonames = {"if", "for", "end", "do", "local", "then", "else", "elseif", "return", "goto", "function", "nil", "false", "true", "repeat", "return", "break", "and", "or", "not", "in", "repeat", "until", "while", "continue"} + +u.uglify = function(str) + assert(str ~= nil, "Cannot uglify a nil string") + local avalchars = {} + local capture_chars = {"%", "(", "[", "\13"} + local skipchars = {} + + for k, v in pairs(capture_chars) do + skipchars[string.byte(v, 1)] = true + end + + for k = 1, 128 do + if not skipchars[k] then + if string.find(str, string.char(k)) then + avalchars[k] = false + else + avalchars[k] = true + end + end + end + + for k, v in pairs(skipchars) do + avalchars[k] = false + end + + local counts = {} + for k, v in ipairs(nonames) do + counts[v] = 0 + end + for k,_ in pairs(counts) do + for i,j in string.gmatch(str,k) do + counts[k] = counts[k] + 1 + end + end + + local prettifycode = [[ + local function p(s) local r = {%s} for k,v in pairs(r) do s = s:gsub(v[2],v[1]) end return s end]] + local replacementtbl = {} + local cursor = 1 --Start at 1 because 0 is the null character + + for k, v in ipairs(nonames) do + if counts[v] ~= 0 then + while not avalchars[cursor] do + cursor = cursor + 1 + end + + replacementtbl[v] = cursor + avalchars[cursor] = false + end + end + + assert(cursor < 128, "Unable to uglify file, not enough unused characters!") + local replacementstr = {} + + for k, v in pairs(replacementtbl) do + local trs = string.format("{%q,%q}", k, string.char(v)) + replacementstr[#replacementstr + 1] = trs + end + + local frepstr = table.concat(replacementstr, ",") + local pcd = string.format(prettifycode, frepstr) + + for k, v in pairs(replacementtbl) do + str = str:gsub(k, string.char(v)) + end + + local numdeepcom = 1 + + local uglified = table.concat({pcd, "\n", "return assert(loadstring(p([", string.rep("=", numdeepcom), "[", str, "]", string.rep("=", numdeepcom), "])))()"}) + + if #uglified > #str then + print("WARNING: Uglification hurt rather than helped! Put more code in one file!") + end + + return uglified +end + +--prettifycode = string.format(prettifycode,) +return u diff --git a/test/glum_test.lua b/test/glum_test.lua index 9f538ad..e9447dd 100644 --- a/test/glum_test.lua +++ b/test/glum_test.lua @@ -1,150 +1,888 @@ --local glum = dofile("../src/test.lua") local glum = dofile("../src/glum.lua") +local ugly = dofile("../src/uglify.lua") local f5 = io.open("../src/glum.lua", "r") local str5 = f5:read("*a") f5:close() -local str = [[ +local str = [==[ +--[[ +This module implements a parser for Lua 5.2 with LPeg, +and generates an Abstract Syntax Tree in the Metalua format. +For more information about Metalua, please, visit: +https://github.com/fab13n/metalua-parser + +block: { stat* } + +stat: + `Do{ stat* } + | `Set{ {lhs+} {expr+} } -- lhs1, lhs2... = e1, e2... + | `While{ expr block } -- while e do b end + | `Repeat{ block expr } -- repeat b until e + | `If{ (expr block)+ block? } -- if e1 then b1 [elseif e2 then b2] ... [else bn] end + | `Fornum{ ident expr expr expr? block } -- for ident = e, e[, e] do b end + | `Forin{ {ident+} {expr+} block } -- for i1, i2... in e1, e2... do b end + | `Local{ {ident+} {expr+}? } -- local i1, i2... = e1, e2... + | `Localrec{ ident expr } -- only used for 'local function' + | `Goto{ } -- goto str + | `Label{ } -- ::str:: + | `Return{ } -- return e1, e2... + | `Break -- break + | apply + +expr: + `Nil + | `Dots + | `True + | `False + | `Number{ } + | `String{ } + | `Function{ { `Id{ }* `Dots? } block } + | `Table{ ( `Pair{ expr expr } | expr )* } + | `Op{ opid expr expr? } + | `Paren{ expr } -- significant to cut multiple values returns + | apply + | lhs + +apply: + `Call{ expr expr* } + | `Invoke{ expr `String{ } expr* } + +lhs: `Id{ } | `Index{ expr expr } + +opid: 'add' | 'sub' | 'mul' | 'div' | 'idiv' | 'mod' | 'pow' | 'concat' + | 'band' | 'bor' | 'bxor' | 'shl' | 'shr' | 'eq' | 'lt' | 'le' + | 'and' | 'or' | 'not' | 'unm' | 'len' | 'bnot' +]] +local parser = {} +local lpeg = require("lpeg") +local scope +if include ~= nil then + scope = include("./scope.lua") +else + scope = dofile("../src/scope.lua") +end -AddCSLuaFile() +lpeg.locale(lpeg) + +local P, S, V = lpeg.P, lpeg.S, lpeg.V +local C, Carg, Cb, Cc = lpeg.C, lpeg.Carg, lpeg.Cb, lpeg.Cc +local Cf, Cg, Cmt, Cp, Ct = lpeg.Cf, lpeg.Cg, lpeg.Cmt, lpeg.Cp, lpeg.Ct +local alpha, digit, alnum = lpeg.alpha, lpeg.digit, lpeg.alnum +local xdigit = lpeg.xdigit +local space = lpeg.space + +local lineno = scope.lineno +local new_scope, end_scope = scope.new_scope, scope.end_scope +local new_function, end_function = scope.new_function, scope.end_function +local begin_loop, end_loop = scope.begin_loop, scope.end_loop +local insideloop = scope.insideloop + +--some stuff for getting glua comments +local BEGIN_COMMENT = lpeg.P("/*") +local END_COMMENT = lpeg.P("*/") +local NOT_BEGIN = (1 - BEGIN_COMMENT)^0 +local NOT_END = (1 - END_COMMENT)^0 +local FULL_COMMENT_CONTENTS = BEGIN_COMMENT * NOT_END * END_COMMENT + +-- error message auxiliary functions + +-- creates an error message for the input string +local function syntaxerror (errorinfo, pos, msg) + local l, c = lineno(errorinfo.subject, pos) + local error_msg = "%s:%d:%d: syntax error, %s" + return string.format(error_msg, errorinfo.filename, l, c, msg) +end -ENT.Type = "point" -ENT.DisableDuplicator = true +-- gets the farthest failure position +local function getffp (s, i, t) + return t.ffp or i, t +end --- --- Make this entity always networked --- -function ENT:UpdateTransmitState() return TRANSMIT_ALWAYS end +-- gets the table that contains the error information +local function geterrorinfo () + return Cmt(Carg(1), getffp) * (C(V"OneWord") + Cc("EOF")) / + function (t, u) + t.unexpected = u + return t + end +end --- --- Networked / Saved Data --- -function ENT:SetupDataTables() +-- creates an errror message using the farthest failure position +local function errormsg () + return geterrorinfo() / + function (t) + local p = t.ffp or 1 + local msg = "unexpected '%s', expecting %s" + msg = string.format(msg, t.unexpected, t.expected) + return nil, syntaxerror(t, p, msg) + end +end - self:NetworkVar( "Vector", 0, "TopColor", { KeyName = "topcolor", Edit = { type = "VectorColor", category = "Main", order = 1 } } ) - self:NetworkVar( "Vector", 1, "BottomColor", { KeyName = "bottomcolor", Edit = { type = "VectorColor", category = "Main", title = "Color Bottom", order = 2 } } ) - self:NetworkVar( "Float", 0, "FadeBias", { KeyName = "fadebias", Edit = { type = "Float", category = "Main", min = 0, max = 1, order = 3 } } ) +-- reports a syntactic error +local function report_error () + return errormsg() +end - self:NetworkVar( "Float", 4, "SunSize", { KeyName = "sunsize", Edit = { type = "Float", min = 0, max = 10, category = "Sun" } } ) - self:NetworkVar( "Vector", 2, "SunNormal", { KeyName = "sunnormal" } ) -- No editing this - it's for coders only - self:NetworkVar( "Vector", 3, "SunColor", { KeyName = "suncolor", Edit = { type = "VectorColor", category = "Sun" } } ) +-- sets the farthest failure position and the expected tokens +local function setffp (s, i, t, n) + if not t.ffp or i > t.ffp then + t.ffp = i + t.list = {} ; t.list[n] = n + t.expected = "'" .. n .. "'" + elseif i == t.ffp then + if not t.list[n] then + t.list[n] = n + t.expected = "'" .. n .. "', " .. t.expected + end + end + return false +end - self:NetworkVar( "Float", 2, "DuskScale", { KeyName = "duskscale", Edit = { type = "Float", min = 0, max = 1, category = "Dusk" } } ) - self:NetworkVar( "Float", 3, "DuskIntensity", { KeyName = "duskintensity", Edit = { type = "Float", min = 0, max = 10, category = "Dusk" } } ) - self:NetworkVar( "Vector", 4, "DuskColor", { KeyName = "duskcolor", Edit = { type = "VectorColor", category = "Dusk" } } ) +local function updateffp (name) + return Cmt(Carg(1) * Cc(name), setffp) +end - self:NetworkVar( "Bool", 0, "DrawStars", { KeyName = "drawstars", Edit = { type = "Boolean", category = "Stars", order = 10 } } ) - self:NetworkVar( "String", 0, "StarTexture", { KeyName = "startexture", Edit = { type = "Texture", group = "Stars", category = "Stars", order = 11 } } ) +--Fixes strings +local function fix_str (str) + str = string.gsub(str, "\\a", "\a") + str = string.gsub(str, "\\b", "\b") + str = string.gsub(str, "\\f", "\f") + str = string.gsub(str, "\\n", "\n") + str = string.gsub(str, "\\r", "\r") + str = string.gsub(str, "\\t", "\t") + str = string.gsub(str, "\\v", "\v") + str = string.gsub(str, "\\\n", "\n") + str = string.gsub(str, "\\\r", "\n") + str = string.gsub(str, "\\'", "'") + str = string.gsub(str, '\\"', '"') + str = string.gsub(str, '\\\\', '\\') + return str +end - self:NetworkVarElement( "Angle", 0, 'p', "StarScale", { KeyName = "starscale", Edit = { type = "Float", min = 0, max = 5, category = "Stars", order = 12 } } ) - self:NetworkVarElement( "Angle", 0, 'y', "StarFade", { KeyName = "starfade", Edit = { type = "Float", min = 0, max = 5, category = "Stars", order = 13 } } ) - self:NetworkVarElement( "Angle", 0, 'r', "StarSpeed", { KeyName = "starspeed", Edit = { type = "Float", min = 0, max = 5, category = "Stars", order = 14 } } ) +-- regular combinators and auxiliary functions - self:NetworkVar( "Float", 1, "HDRScale", { KeyName = "hdrscale", Edit = { type = "Float", category = "Main", min = 0, max = 1, order = 4 } } ) +local function token (pat, name) + return pat * V"Skip" + updateffp(name) * P(false) +end - -- - -- Entity defaults - -- - if ( SERVER ) then +local function symb (str) + return token (P(str), str) +end - self:SetTopColor( Vector( 0.2, 0.5, 1.0 ) ) - self:SetBottomColor( Vector( 0.8, 1.0, 1.0 ) ) - self:SetFadeBias( 1 ) +local function kw (str) + return token (P(str) * -V"idRest", str) +end +local function taggedCap (tag, pat) + return Ct(Cg(Cp(), "pos") * Cg(Cc(tag), "tag") * pat) +end - self:SetSunNormal( Vector( 0.4, 0.0, 0.01 ) ) - self:SetSunColor( Vector( 0.2, 0.1, 0.0 ) ) - self:SetSunSize( 2.0 ) +local function unaryop (op, e) + return { tag = "Op", pos = e.pos, [1] = op, [2] = e } +end - self:SetDuskColor( Vector( 1.0, 0.2, 0.0 ) ) - self:SetDuskScale( 1 ) - self:SetDuskIntensity( 1 ) +local function binaryop (e1, op, e2) + if not op then + return e1 + elseif op == "add" or + op == "sub" or + op == "mul" or + op == "div" or + op == "idiv" or + op == "mod" or + op == "pow" or + op == "concat" or + op == "band" or + op == "bor" or + op == "bxor" or + op == "shl" or + op == "shr" or + op == "eq" or + op == "lt" or + op == "le" or + op == "and" or + op == "or" then + return { tag = "Op", pos = e1.pos, [1] = op, [2] = e1, [3] = e2 } + elseif op == "ne" then + return unaryop ("not", { tag = "Op", pos = e1.pos, [1] = "eq", [2] = e1, [3] = e2 }) + elseif op == "gt" then + return { tag = "Op", pos = e1.pos, [1] = "lt", [2] = e2, [3] = e1 } + elseif op == "ge" then + return { tag = "Op", pos = e1.pos, [1] = "le", [2] = e2, [3] = e1 } + end +end - self:SetDrawStars( true ) - self:SetStarSpeed( 0.01 ) - self:SetStarScale( 0.5 ) - self:SetStarFade( 1.5 ) - self:SetStarTexture( "skybox/starfield" ) +local function chainl (pat, sep, a) + return Cf(pat * Cg(sep * pat)^0, binaryop) + a +end - self:SetHDRScale( 0.66 ) +local function chainl1 (pat, sep) + return Cf(pat * Cg(sep * pat)^0, binaryop) +end - end +local function sepby (pat, sep, tag) + return taggedCap(tag, (pat * (sep * pat)^0)^(-1)) +end +local function sepby1 (pat, sep, tag) + return taggedCap(tag, pat * (sep * pat)^0) end -function ENT:Initialize() +-- grammar + +local G = { V"Lua", + Lua = V"Shebang"^(-1) * V"Skip" * V"Chunk" * -1 + report_error(); + -- parser + Chunk = V"Block"; + StatList = (symb(";") + V"Stat")^0; + Var = V"Id"; + Id = taggedCap("Id", token(V"Name", "Name")); + FunctionDef = kw("function") * V"FuncBody"; + FieldSep = symb(",") + symb(";"); + Field = taggedCap("Pair", (symb("[") * V"Expr" * symb("]") * symb("=") * V"Expr") + + (taggedCap("String", token(V"Name", "Name")) * symb("=") * V"Expr")) + + V"Expr"; + FieldList = (V"Field" * (V"FieldSep" * V"Field")^0 * V"FieldSep"^(-1))^(-1); + Constructor = taggedCap("Table", symb("{") * V"FieldList" * symb("}")); + NameList = sepby1(V"Id", symb(","), "NameList"); + ExpList = sepby1(V"Expr", symb(","), "ExpList"); + FuncArgs = symb("(") * (V"Expr" * (symb(",") * V"Expr")^0)^(-1) * symb(")") + + V"Constructor" + + taggedCap("String", token(V"String", "String")); + Expr = V"SubExpr_1"; + SubExpr_1 = chainl1(V"SubExpr_2", V"OrOp"); + SubExpr_2 = chainl1(V"SubExpr_3", V"AndOp"); + SubExpr_3 = chainl1(V"SubExpr_4", V"RelOp"); + SubExpr_4 = chainl1(V"SubExpr_5", V"BOrOp"); + SubExpr_5 = chainl1(V"SubExpr_6", V"BXorOp"); + SubExpr_6 = chainl1(V"SubExpr_7", V"BAndOp"); + SubExpr_7 = chainl1(V"SubExpr_8", V"ShiftOp"); + SubExpr_8 = V"SubExpr_9" * V"ConOp" * V"SubExpr_8" / binaryop + + V"SubExpr_9"; + SubExpr_9 = chainl1(V"SubExpr_10", V"AddOp"); + SubExpr_10 = chainl1(V"SubExpr_11", V"MulOp"); + SubExpr_11 = V"UnOp" * V"SubExpr_11" / unaryop + + V"SubExpr_12"; + SubExpr_12 = V"SimpleExp" * (V"PowOp" * V"SubExpr_11")^(-1) / binaryop; + SimpleExp = taggedCap("Number", token(V"Number", "Number")) + + taggedCap("String", token(V"String", "String")) + + taggedCap("Nil", kw("nil")) + + taggedCap("False", kw("false")) + + taggedCap("True", kw("true")) + + taggedCap("Dots", symb("...")) + + V"FunctionDef" + + V"Constructor" + + V"SuffixedExp"; + SuffixedExp = Cf(V"PrimaryExp" * ( + taggedCap("DotIndex", symb(".") * taggedCap("String", token(V"Name", "Name"))) + + taggedCap("ArrayIndex", symb("[") * V"Expr" * symb("]")) + + taggedCap("Invoke", Cg(symb(":") * taggedCap("String", token(V"Name", "Name")) * V"FuncArgs")) + + taggedCap("Call", V"FuncArgs") + )^0, function (t1, t2) + if t2 then + if t2.tag == "Call" or t2.tag == "Invoke" then + local t = {tag = t2.tag, pos = t1.pos, [1] = t1} + for k, v in ipairs(t2) do + table.insert(t, v) + end + return t + else + return {tag = "Index", pos = t1.pos, [1] = t1, [2] = t2[1]} + end + end + return t1 + end); + PrimaryExp = V"Var" + + taggedCap("Paren", symb("(") * V"Expr" * symb(")")); + Block = taggedCap("Block", V"StatList" * V"RetStat"^(-1)); + IfStat = taggedCap("If", + kw("if") * V"Expr" * kw("then") * V"Block" * + (kw("elseif") * V"Expr" * kw("then") * V"Block")^0 * + (kw("else") * V"Block")^(-1) * + kw("end")); + WhileStat = taggedCap("While", kw("while") * V"Expr" * + kw("do") * V"Block" * kw("end")); + DoStat = kw("do") * V"Block" * kw("end") / + function (t) + t.tag = "Do" + return t + end; + ForBody = kw("do") * V"Block"; + ForNum = taggedCap("Fornum", + V"Id" * symb("=") * V"Expr" * symb(",") * + V"Expr" * (symb(",") * V"Expr")^(-1) * + V"ForBody"); + ForGen = taggedCap("Forin", V"NameList" * kw("in") * V"ExpList" * V"ForBody"); + ForStat = kw("for") * (V"ForNum" + V"ForGen") * kw("end"); + RepeatStat = taggedCap("Repeat", kw("repeat") * V"Block" * + kw("until") * V"Expr"); + FuncName = Cf(V"Id" * (symb(".") * taggedCap("String", token(V"Name", "Name")))^0, + function (t1, t2) + if t2 then + return {tag = "Index", pos = t1.pos, [1] = t1, [2] = t2} + end + return t1 + end) * (symb(":") * taggedCap("String", token(V"Name", "Name")))^(-1) / + function (t1, t2) + if t2 then + return {tag = "Index", pos = t1.pos, is_method = true, [1] = t1, [2] = t2} + end + return t1 + end; + ParList = V"NameList" * (symb(",") * symb("...") * taggedCap("Dots", Cp()))^(-1) / + function (t, v) + if v then table.insert(t, v) end + return t + end + + symb("...") * taggedCap("Dots", Cp()) / + function (v) + return {v} + end + + P(true) / function () return {} end; + -- Cc({}) generates a strange bug when parsing [[function t:a() end ; function t.a() end]] + -- the bug is to add the parameter self to the second function definition + --FuncBody = taggedCap("Function", symb("(") * (V"ParList" + Cc({})) * symb(")") * V"Block" * kw("end")); + FuncBody = taggedCap("Function", symb("(") * V"ParList" * symb(")") * V"Block" * kw("end")); + FuncStat = taggedCap("Set", kw("function") * V"FuncName" * V"FuncBody") / + function (t) + if t[1].is_method then table.insert(t[2][1], 1, {tag = "Id", [1] = "self"}) end + t[1] = {t[1]} + t[2] = {t[2]} + return t + end; + LocalFunc = taggedCap("Localrec", kw("function") * V"Id" * V"FuncBody") / + function (t) + t[1] = {t[1]} + t[2] = {t[2]} + return t + end; + LocalAssign = taggedCap("Local", V"NameList" * ((symb("=") * V"ExpList") + Ct(Cc()))); + LocalStat = kw("local") * (V"LocalFunc" + V"LocalAssign"); + LabelStat = taggedCap("Label", symb("::") * token(V"Name", "Name") * symb("::")); + BreakStat = taggedCap("Break", kw("break")); + ContinueStat = taggedCap("Continue", kw("continue")); + GoToStat = taggedCap("Goto", kw("goto") * token(V"Name", "Name")); + RetStat = taggedCap("Return", kw("return") * (V"Expr" * (symb(",") * V"Expr")^0)^(-1) * symb(";")^(-1)); + ExprStat = Cmt( + (V"SuffixedExp" * + (Cc(function (...) + local vl = {...} + local el = vl[#vl] + table.remove(vl) + for k, v in ipairs(vl) do + if v.tag == "Id" or v.tag == "Index" then + vl[k] = v + else + -- invalid assignment + return false + end + end + vl.tag = "VarList" + vl.pos = vl[1].pos + return true, {tag = "Set", pos = vl.pos, [1] = vl, [2] = el} + end) * V"Assignment")) + + + (V"SuffixedExp" * + (Cc(function (s) + if s.tag == "Call" or + s.tag == "Invoke" then + return true, s + end + -- invalid statement + return false + end))) + , function (s, i, s1, f, ...) return f(s1, ...) end); + Assignment = ((symb(",") * V"SuffixedExp")^1)^(-1) * symb("=") * V"ExpList"; + Stat = V"IfStat" + V"WhileStat" + V"DoStat" + V"ForStat" + + V"RepeatStat" + V"FuncStat" + V"LocalStat" + V"LabelStat" + + V"BreakStat" + V"GoToStat" + V"ExprStat" + V"ContinueStat"; + -- lexer + Space = space^1; + Equals = P"="^0; + Open = "[" * Cg(V"Equals", "init") * "[" * P"\n"^(-1); + Close = "]" * C(V"Equals") * "]"; + CloseEQ = Cmt(V"Close" * Cb("init"), + function (s, i, a, b) return a == b end); + LongString = V"Open" * C((P(1) - V"CloseEQ")^0) * V"Close" / + function (s, o) return s end; + Comment = P"--" * V"LongString" / function () return end + + P"--" * (P(1) - P"\n")^0 + + P"//" * (P(1) - P"\n")^0 + + C(FULL_COMMENT_CONTENTS) / function() return end; + Skip = (V"Space" + V"Comment")^0; + idStart = alpha + P("_"); + idRest = alnum + P("_"); + Keywords = P("and") + "break" + "do" + "elseif" + "else" + "end" + + "false" + "for" + "function" + "goto" + "if" + "in" + + "local" + "nil" + "not" + "or" + "repeat" + "return" + + "then" + "true" + "until" + "while" + "continue"; + Reserved = V"Keywords" * -V"idRest"; + Identifier = V"idStart" * V"idRest"^0; + Name = -V"Reserved" * C(V"Identifier") * -V"idRest"; + Hex = (P("0x") + P("0X")) * xdigit^1; + Expo = S("eE") * S("+-")^(-1) * digit^1; + Float = (((digit^1 * P(".") * digit^0) + + (P(".") * digit^1)) * V"Expo"^(-1)) + + (digit^1 * V"Expo"); + Int = digit^1; + Number = C(V"Hex" + V"Float" + V"Int") / + function (n) return tonumber(n) end; + ShortString = P'"' * C(((P'\\' * P(1)) + (P(1) - P'"'))^0) * P'"' + + P"'" * C(((P"\\" * P(1)) + (P(1) - P"'"))^0) * P"'"; + String = V"LongString" + (V"ShortString" / function (s) + --print("instead of " .. s .. " i return " .. fix_str(s)) + return s end); + --return fix_str(s) end); + OrOp = kw("or") / "or" + + symb("||") / "or"; + AndOp = kw("and") / "and" + + symb("&&") / "and"; + RelOp = symb("~=") / "ne" + + symb("==") / "eq" + + symb("<=") / "le" + + symb(">=") / "ge" + + symb("<") / "lt" + + symb(">") / "gt" + + symb("!=") / "ne"; + BOrOp = symb("|") / "bor"; + BXorOp = symb("~") / "bxor"; + BAndOp = symb("&") / "band"; + ShiftOp = symb("<<") / "shl" + + symb(">>") / "shr"; + ConOp = symb("..") / "concat"; + AddOp = symb("+") / "add" + + symb("-") / "sub"; + MulOp = symb("*") / "mul" + + --symb("//") / "idiv" + + symb("/") / "div" + + symb("%") / "mod"; + UnOp = kw("not") / "not" + + symb("-") / "unm" + + symb("#") / "len" + + symb("~") / "bnot" + + symb("!") / "not"; + PowOp = symb("^") / "pow"; + Shebang = P"#" * (P(1) - P"\n")^0 * P"\n"; + -- for error reporting + OneWord = V"Name" + V"Number" + V"String" + V"Reserved" + P("...") + P(1); +} + +local function exist_label (env, scope, stm) + local l = stm[1] + for s=scope, 0, -1 do + if env[s]["label"][l] then return true end + end + return false end -function ENT:KeyValue( key, value ) +local function set_label (env, label, pos) + local scope = env.scope + local l = env[scope]["label"][label] + if not l then + env[scope]["label"][label] = { name = label, pos = pos } + return true + else + local msg = "label '%s' already defined at line %d" + local line = lineno(env.errorinfo.subject, l.pos) + msg = string.format(msg, label, line) + return nil, syntaxerror(env.errorinfo, pos, msg) + end +end - if ( self:SetNetworkKeyValue( key, value ) ) then - return - end +local function set_pending_goto (env, stm) + local scope = env.scope + table.insert(env[scope]["goto"], stm) + return true +end - -- TODO: sunposmethod - -- 0 : "Custom - Use the Sun Normal to position the sun" - -- 1 : "Automatic - Find a env_sun entity and use that" +local function verify_pending_gotos (env) + for s=env.maxscope, 0, -1 do + for k, v in ipairs(env[s]["goto"]) do + if not exist_label(env, s, v) then + local msg = "no visible label '%s' for " + msg = string.format(msg, v[1]) + return nil, syntaxerror(env.errorinfo, v.pos, msg) + end + end + end + return true +end +local function set_vararg (env, is_vararg) + env["function"][env.fscope].is_vararg = is_vararg end -function ENT:Think() +local traverse_stm, traverse_exp, traverse_var +local traverse_block, traverse_explist, traverse_varlist, traverse_parlist + +function traverse_parlist (env, parlist) + local len = #parlist + local is_vararg = false + if len > 0 and parlist[len].tag == "Dots" then + is_vararg = true + end + set_vararg(env, is_vararg) + return true +end - -- - -- Find an env_sun - if we don't already have one. - -- - if ( SERVER && self.EnvSun == nil ) then +local function traverse_function (env, exp) + new_function(env) + new_scope(env) + local status, msg = traverse_parlist(env, exp[1]) + if not status then return status, msg end + status, msg = traverse_block(env, exp[2]) + if not status then return status, msg end + end_scope(env) + end_function(env) + return true +end - -- so this closure only gets called once - even if it fails - self.EnvSun = false +local function traverse_op (env, exp) + local status, msg = traverse_exp(env, exp[2]) + if not status then return status, msg end + if exp[3] then + status, msg = traverse_exp(env, exp[3]) + if not status then return status, msg end + end + return true +end - local list = ents.FindByClass( "env_sun" ) - if ( #list > 0 ) then - self.EnvSun = list[1] - end +local function traverse_paren (env, exp) + local status, msg = traverse_exp(env, exp[1]) + if not status then return status, msg end + return true +end - end +local function traverse_table (env, fieldlist) + for k, v in ipairs(fieldlist) do + local tag = v.tag + if tag == "Pair" then + local status, msg = traverse_exp(env, v[1]) + if not status then return status, msg end + status, msg = traverse_exp(env, v[2]) + if not status then return status, msg end + else + local status, msg = traverse_exp(env, v) + if not status then return status, msg end + end + end + return true +end - -- - -- If we have a sun - force our sun normal to its value - -- - if ( SERVER && IsValid( self.EnvSun ) ) then +local function traverse_vararg (env, exp) + if not env["function"][env.fscope].is_vararg then + local msg = "cannot use '...' outside a vararg function" + return nil, syntaxerror(env.errorinfo, exp.pos, msg) + end + return true +end - local vec = self.EnvSun:GetInternalVariable( "m_vDirection" ) +local function traverse_call (env, call) + local status, msg = traverse_exp(env, call[1]) + if not status then return status, msg end + for i=2, #call do + status, msg = traverse_exp(env, call[i]) + if not status then return status, msg end + end + return true +end - if ( isvector( vec ) ) then - self:SetSunNormal( vec ) - end +local function traverse_invoke (env, invoke) + local status, msg = traverse_exp(env, invoke[1]) + if not status then return status, msg end + for i=3, #invoke do + status, msg = traverse_exp(env, invoke[i]) + if not status then return status, msg end + end + return true +end - end +local function traverse_assignment (env, stm) + local status, msg = traverse_varlist(env, stm[1]) + if not status then return status, msg end + status, msg = traverse_explist(env, stm[2]) + if not status then return status, msg end + return true +end - -- - -- Become the active sky again if we're not already - -- - if ( CLIENT && g_SkyPaint != self ) then +local function traverse_break (env, stm) + if not insideloop(env) then + local msg = " not inside a loop" + return nil, syntaxerror(env.errorinfo, stm.pos, msg) + end + return true +end - if ( !IsValid( g_SkyPaint ) ) then - g_SkyPaint = self - end +local function traverse_continue (env, stm) + if not insideloop(env) then + local msg = " not inside a loop" + return nil, syntaxerror(env.errorinfo, stm.pos, msg) + end + return true +end + +local function traverse_forin (env, stm) + begin_loop(env) + new_scope(env) + local status, msg = traverse_explist(env, stm[2]) + if not status then return status, msg end + status, msg = traverse_block(env, stm[3]) + if not status then return status, msg end + end_scope(env) + end_loop(env) + return true +end + +local function traverse_fornum (env, stm) + local status, msg + begin_loop(env) + new_scope(env) + status, msg = traverse_exp(env, stm[2]) + if not status then return status, msg end + status, msg = traverse_exp(env, stm[3]) + if not status then return status, msg end + if stm[5] then + status, msg = traverse_exp(env, stm[4]) + if not status then return status, msg end + status, msg = traverse_block(env, stm[5]) + if not status then return status, msg end + else + status, msg = traverse_block(env, stm[4]) + if not status then return status, msg end + end + end_scope(env) + end_loop(env) + return true +end + +local function traverse_goto (env, stm) + local status, msg = set_pending_goto(env, stm) + if not status then return status, msg end + return true +end + +local function traverse_if (env, stm) + local len = #stm + if len % 2 == 0 then + for i=1, len, 2 do + local status, msg = traverse_exp(env, stm[i]) + if not status then return status, msg end + status, msg = traverse_block(env, stm[i+1]) + if not status then return status, msg end + end + else + for i=1, len-1, 2 do + local status, msg = traverse_exp(env, stm[i]) + if not status then return status, msg end + status, msg = traverse_block(env, stm[i+1]) + if not status then return status, msg end + end + local status, msg = traverse_block(env, stm[len]) + if not status then return status, msg end + end + return true +end + +local function traverse_label (env, stm) + local status, msg = set_label(env, stm[1], stm.pos) + if not status then return status, msg end + return true +end + +local function traverse_let (env, stm) + local status, msg = traverse_explist(env, stm[2]) + if not status then return status, msg end + return true +end + +local function traverse_letrec (env, stm) + local status, msg = traverse_exp(env, stm[2][1]) + if not status then return status, msg end + return true +end + +local function traverse_repeat (env, stm) + begin_loop(env) + local status, msg = traverse_block(env, stm[1]) + if not status then return status, msg end + status, msg = traverse_exp(env, stm[2]) + if not status then return status, msg end + end_loop(env) + return true +end - end +local function traverse_return (env, stm) + local status, msg = traverse_explist(env, stm) + if not status then return status, msg end + return true +end +local function traverse_while (env, stm) + begin_loop(env) + local status, msg = traverse_exp(env, stm[1]) + if not status then return status, msg end + status, msg = traverse_block(env, stm[2]) + if not status then return status, msg end + end_loop(env) + return true end --- --- To prevent server insanity - only let admins edit the sky. --- -function ENT:CanEditVariables( ply ) +function traverse_var (env, var) + local tag = var.tag + if tag == "Id" then -- `Id{ } + return true + elseif tag == "Index" then -- `Index{ expr expr } + local status, msg = traverse_exp(env, var[1]) + if not status then return status, msg end + status, msg = traverse_exp(env, var[2]) + if not status then return status, msg end + return true + else + error("expecting a variable, but got a " .. tag) + end +end - return ply:IsAdmin() +function traverse_varlist (env, varlist) + for k, v in ipairs(varlist) do + local status, msg = traverse_var(env, v) + if not status then return status, msg end + end + return true +end + +function traverse_exp (env, exp) + local tag = exp.tag + if tag == "Nil" or + tag == "True" or + tag == "False" or + tag == "Number" or -- `Number{ } + tag == "String" then -- `String{ } + return true + elseif tag == "Dots" then + return traverse_vararg(env, exp) + elseif tag == "Function" then -- `Function{ { `Id{ }* `Dots? } block } + return traverse_function(env, exp) + elseif tag == "Table" then -- `Table{ ( `Pair{ expr expr } | expr )* } + return traverse_table(env, exp) + elseif tag == "Op" then -- `Op{ opid expr expr? } + return traverse_op(env, exp) + elseif tag == "Paren" then -- `Paren{ expr } + return traverse_paren(env, exp) + elseif tag == "Call" then -- `Call{ expr expr* } + return traverse_call(env, exp) + elseif tag == "Invoke" then -- `Invoke{ expr `String{ expr* } + return traverse_invoke(env, exp) + elseif tag == "Id" or -- `Id{ } + tag == "Index" then -- `Index{ expr expr } + return traverse_var(env, exp) + else + error("expecting an expression, but got a " .. tag) + end +end + +function traverse_explist (env, explist) + for k, v in ipairs(explist) do + local status, msg = traverse_exp(env, v) + if not status then return status, msg end + end + return true +end + +function traverse_stm (env, stm) + local tag = stm.tag + if tag == "Do" then -- `Do{ stat* } + return traverse_block(env, stm) + elseif tag == "Set" then -- `Set{ {lhs+} {expr+} } + return traverse_assignment(env, stm) + elseif tag == "While" then -- `While{ expr block } + return traverse_while(env, stm) + elseif tag == "Repeat" then -- `Repeat{ block expr } + return traverse_repeat(env, stm) + elseif tag == "If" then -- `If{ (expr block)+ block? } + return traverse_if(env, stm) + elseif tag == "Fornum" then -- `Fornum{ ident expr expr expr? block } + return traverse_fornum(env, stm) + elseif tag == "Forin" then -- `Forin{ {ident+} {expr+} block } + return traverse_forin(env, stm) + elseif tag == "Local" then -- `Local{ {ident+} {expr+}? } + return traverse_let(env, stm) + elseif tag == "Localrec" then -- `Localrec{ ident expr } + return traverse_letrec(env, stm) + elseif tag == "Goto" then -- `Goto{ } + return traverse_goto(env, stm) + elseif tag == "Label" then -- `Label{ } + return traverse_label(env, stm) + elseif tag == "Return" then -- `Return{ * } + return traverse_return(env, stm) + elseif tag == "Break" then + return traverse_break(env, stm) + elseif tag == "Continue" then + return traverse_continue(env,stm) + elseif tag == "Call" then -- `Call{ expr expr* } + return traverse_call(env, stm) + elseif tag == "Invoke" then -- `Invoke{ expr `String{ } expr* } + return traverse_invoke(env, stm) + else + error("expecting a statement, but got a " .. tag) + end +end +function traverse_block (env, block) + local l = {} + new_scope(env) + for k, v in ipairs(block) do + local status, msg = traverse_stm(env, v) + if not status then return status, msg end + end + end_scope(env) + return true end + +local function traverse (ast, errorinfo) + assert(type(ast) == "table") + assert(type(errorinfo) == "table") + local env = { errorinfo = errorinfo, ["function"] = {} } + new_function(env) + set_vararg(env, true) + local status, msg = traverse_block(env, ast) + if not status then return status, msg end + end_function(env) + status, msg = verify_pending_gotos(env) + if not status then return status, msg end + return ast +end + +function parser.parse (subject, filename) + local errorinfo = { subject = subject, filename = filename } + lpeg.setmaxstack(1000) + --debug.getregistry()["lpeg-maxstack"] = 1000 + local ast, error_msg = lpeg.match(G, subject, nil, errorinfo) + if not ast then return ast, error_msg end + return traverse(ast, errorinfo) +end + +return parser +]==] + +local str2 = [=[ +--[[///////////////// + ]] +local foo = 1 +if foo then print("bar") end +local function something(foo, bar) + print("foobar") +end +something(foo,4) +]=] local min = glum.minify(str) ---local comp = glum.uglify(min) +--local comp = ugly.uglify(min) print(min) +print(string.format("Old size:%d\nNew size:%d",#str,#min)) --[[ local fuzzel = loadstring(f)() diff --git a/test/test_output.lua b/test/test_output.lua new file mode 100644 index 0000000..44eb5f6 --- /dev/null +++ b/test/test_output.lua @@ -0,0 +1,293 @@ +Finding string reps + 1: + 1:NetworkVar + 2:96 + 2: + 1:category + 2:84 + 3: + 1:KeyName + 2:75 + 4: + 1:NetworkVarElement + 2:45 + 5: + 1:Float + 2:39 + 6: + 1:VectorColor + 2:36 + 7: + 1:Edit + 2:28 + 8: + 1:type + 2:28 + 9: + 1:order + 2:27 + 10: + 1:EnvSun + 2:20 + 11: + 1:SetSunNormal + 2:20 + 12: + 1:Vector + 2:20 + 13: + 1:Stars + 2:18 + 14: + 1:UpdateTransmitState + 2:17 + 15: + 1:GetInternalVariable + 2:17 + 16: + 1:SetNetworkKeyValue + 2:16 + 17: + 1:DisableDuplicator + 2:15 + 18: + 1:skybox/starfield + 2:14 + 19: + 1:CanEditVariables + 2:14 + 20: + 1:SetDuskIntensity + 2:14 + 21: + 1:SetupDataTables + 2:13 + 22: + 1:SetStarTexture + 2:12 + 23: + 1:SetBottomColor + 2:12 + 24: + 1:duskintensity + 2:11 + 25: + 1:DuskIntensity + 2:11 + 26: + 1:m_vDirection + 2:10 + 27: + 1:SetDrawStars + 2:10 + 28: + 1:SetDuskColor + 2:10 + 29: + 1:SetDuskScale + 2:10 + 30: + 1:SetStarSpeed + 2:10 + 31: + 1:Color Bottom + 2:10 + 32: + 1:SetStarScale + 2:10 + 33: + 1:StarTexture + 2:9 + 34: + 1:SetFadeBias + 2:9 + 35: + 1:SetStarFade + 2:9 + 36: + 1:SetHDRScale + 2:9 + 37: + 1:Angle + 2:9 + 38: + 1:startexture + 2:9 + 39: + 1:BottomColor + 2:9 + 40: + 1:FindByClass + 2:9 + 41: + 1:SetTopColor + 2:9 + 42: + 1:SetSunColor + 2:9 + 43: + 1:bottomcolor + 2:9 + 44: + 1:min + 2:8 + 45: + 1:SetSunSize + 2:8 + 46: + 1:Main + 2:8 + 47: + 1:Initialize + 2:8 + 48: + 1:max + 2:8 + 49: + 1:drawstars + 2:7 + 50: + 1:duskscale + 2:7 + 51: + 1:DuskColor + 2:7 + 52: + 1:sunnormal + 2:7 + 53: + 1:starspeed + 2:7 + 54: + 1:DuskScale + 2:7 + 55: + 1:SunNormal + 2:7 + 56: + 1:DrawStars + 2:7 + 57: + 1:duskcolor + 2:7 + 58: + 1:StarSpeed + 2:7 + 59: + 1:starscale + 2:7 + 60: + 1:StarScale + 2:7 + 61: + 1:topcolor + 2:6 + 62: + 1:hdrscale + 2:6 + 63: + 1:fadebias + 2:6 + 64: + 1:suncolor + 2:6 + 65: + 1:SunColor + 2:6 + 66: + 1:StarFade + 2:6 + 67: + 1:FadeBias + 2:6 + 68: + 1:TopColor + 2:6 + 69: + 1:HDRScale + 2:6 + 70: + 1:KeyValue + 2:6 + 71: + 1:starfade + 2:6 + 72: + 1:Dusk + 2:6 + 73: + 1:Texture + 2:5 + 74: + 1:SunSize + 2:5 + 75: + 1:IsAdmin + 2:5 + 76: + 1:env_sun + 2:5 + 77: + 1:Boolean + 2:5 + 78: + 1:sunsize + 2:5 + 79: + 1:String + 2:4 + 80: + 1:group + 2:3 + 81: + 1:point + 2:3 + 82: + 1:Think + 2:3 + 83: + 1:title + 2:3 + 84: + 1:Bool + 2:2 + 85: + 1:Type + 2:2 + 86: + 1:Sun + 2:2 + 87: + 1:r + 2:-1 + 88: + 1:y + 2:-1 + 89: + 1:p + 2:-1 +Found 1 locals as Local +Found 1 locals as Local +lhss is + 1:e + 2:a + 3:b + 4:c + 5:f + 6:d +Before doing stringfor for the second time, olocalvar is + strings: + Float:e + NetworkVar:a + category:b + KeyName:c + VectorColor:f + NetworkVarElement:d + numlocals:2 + lname:f + block:true + nids: + ids: +Found 1 locals as Local +Found 1 locals as Local + local e,a,b,c,f,d="Float","NetworkVar","category","KeyName","VectorColor","NetworkVarElement";AddCSLuaFile()ENT.Type="point";ENT.DisableDuplicator= true ;ENT.UpdateTransmitState= function() return TRANSMIT_ALWAYS end ;ENT.SetupDataTables= function()self[a]("Vector",0,"TopColor",{[c]="topcolor",["Edit"]={["type"]=f,[b]="Main",["order"]=1}})self[a]("Vector",1,"BottomColor",{[c]="bottomcolor",["Edit"]={["type"]=f,[b]="Main",["title"]="Color Bottom",["order"]=2}})self[a](e,0,"FadeBias",{[c]="fadebias",["Edit"]={["type"]=e,[b]="Main",["min"]=0,["max"]=1,["order"]=3}})self[a](e,4,"SunSize",{[c]="sunsize",["Edit"]={["type"]=e,["min"]=0,["max"]=10,[b]="Sun"}})self[a]("Vector",2,"SunNormal",{[c]="sunnormal"})self[a]("Vector",3,"SunColor",{[c]="suncolor",["Edit"]={["type"]=f,[b]="Sun"}})self[a](e,2,"DuskScale",{[c]="duskscale",["Edit"]={["type"]=e,["min"]=0,["max"]=1,[b]="Dusk"}})self[a](e,3,"DuskIntensity",{[c]="duskintensity",["Edit"]={["type"]=e,["min"]=0,["max"]=10,[b]="Dusk"}})self[a]("Vector",4,"DuskColor",{[c]="duskcolor",["Edit"]={["type"]=f,[b]="Dusk"}})self[a]("Bool",0,"DrawStars",{[c]="drawstars",["Edit"]={["type"]="Boolean",[b]="Stars",["order"]=10}})self[a]("String",0,"StarTexture",{[c]="startexture",["Edit"]={["type"]="Texture",["group"]="Stars",[b]="Stars",["order"]=11}})self[d]("Angle",0,"p","StarScale",{[c]="starscale",["Edit"]={["type"]=e,["min"]=0,["max"]=5,[b]="Stars",["order"]=12}})self[d]("Angle",0,"y","StarFade",{[c]="starfade",["Edit"]={["type"]=e,["min"]=0,["max"]=5,[b]="Stars",["order"]=13}})self[d]("Angle",0,"r","StarSpeed",{[c]="starspeed",["Edit"]={["type"]=e,["min"]=0,["max"]=5,[b]="Stars",["order"]=14}})self[a](e,1,"HDRScale",{[c]="hdrscale",["Edit"]={["type"]=e,[b]="Main",["min"]=0,["max"]=1,["order"]=4}}) if (SERVER) then self:SetTopColor(Vector(0.2,0.5,1))self:SetBottomColor(Vector(0.8,1,1))self:SetFadeBias(1)self:SetSunNormal(Vector(0.4,0,0.01))self:SetSunColor(Vector(0.2,0.1,0))self:SetSunSize(2)self:SetDuskColor(Vector(1,0.2,0))self:SetDuskScale(1)self:SetDuskIntensity(1)self:SetDrawStars( true )self:SetStarSpeed(0.01)self:SetStarScale(0.5)self:SetStarFade(1.5)self:SetStarTexture("skybox/starfield")self:SetHDRScale(0.66) end end ;ENT.Initialize= function() end ;ENT.KeyValue= function(g,h,i) if (g:SetNetworkKeyValue(h,i)) then return end end ;ENT.Think= function() if (SERVER and g.EnvSun== nil ) then g.EnvSun= false ;local j=ents.FindByClass("env_sun"); if (0<#j) then g.EnvSun=j[1]; end end if (SERVER and IsValid(g.EnvSun)) then local j=g.EnvSun:GetInternalVariable("m_vDirection"); if (isvector(j)) then g:SetSunNormal(j) end end if (CLIENT and not g_SkyPaint~=g) then if ( not IsValid(g_SkyPaint)) then g_SkyPaint=g; end end end ;ENT.CanEditVariables= function(g,j) return j:IsAdmin() end ; -- cgit v1.2.3-70-g09d2