diff options
Diffstat (limited to 'src/glum.lua')
| -rw-r--r-- | src/glum.lua | 1611 |
1 files changed, 826 insertions, 785 deletions
diff --git a/src/glum.lua b/src/glum.lua index bcdd5e6..446be40 100644 --- a/src/glum.lua +++ b/src/glum.lua @@ -1,785 +1,826 @@ ---[[ -This moudle allows you to minify gLua code - Use: - local x = require("glum.lua") - local str =" - --Here is some code to be minified!\n - for a=1,10,2 do\n - print(a)\n - end - " - print(x.minify(str)) - Dependencies: - lua-parser - 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 -local optimi -if include ~= nil then - parser = include("./parser.lua") - msg = Msg - optimi = include("./ast_opts.lua") -else - parser = dofile("../src/parser.lua") - msg = io.write - optimi = dofile("../src/ast_opts.lua") - print("Just did optimi, it is",type(optimi)) -end -local lpeg = require("lpeg") -lpeg.locale(lpeg) - -local glum = {} - ---Checks if two tables are the same -local function deepcompare(tbl1, tbl2) - for k,v in pairs(tbl1) do - if type(v) == "table" then - if not deepcompare(v,tbl2[k]) then - return false - end - else - if v ~= tbl2[k] then - return false - end - end - end -end - ---Creates a deep copy of a table -local function deepcopy(orig) - local orig_type = type(orig) - local copy - if orig_type == "table" then - copy = {} - for orig_key, orig_value in next, orig, nil do - copy[deepcopy(orig_key)] = deepcopy(orig_value) - end - setmetatable(copy, deepcopy(getmetatable(orig))) - else -- number, string, boolean, etc - copy = orig - end - 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 = {} -for k,v in ipairs(nonames) do - reservednames[v] = true -end - ---A function that generates the next valid variable name from the last valid variable name. -local varchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY" -local function getnextvarname(lname) - local place = string.find(lname,"[" .. varchars .. "]") - local length = string.len(lname) - if place == nil then - return string.rep("a", length + 1) - else - local lbyte = string.byte(lname,place) - local newchar = string.char(lbyte + (lbyte < 122 and 1 or -57)) - local after = string.sub(lname, place + 1, length) - local before = string.rep("a", place-1) - local output = before .. newchar .. after - while reservednames[output] or _G[output] do - output = getnextvarname(output) - end - return output - end -end - ---A debugging function, a replacement for glua PrintTable -local function printtable(tbl, tabset) - tabset = tabset or 0 - for k,v in pairs(tbl) do - for i = 0,tabset do msg("\t") end - msg(k .. ":") - if type(v) == "table" then - msg("\n") - printtable(v, tabset + 1) - else - msg(tostring(v) .. "\n") - end - end -end - -local stringreps -local function findstrings(ast) - if type(ast) ~= "table" then return end - if ast and ast.tag == "String" then - local lnum = stringreps[ast[1]] - stringreps[ast[1]] = lnum and lnum + 1 or 1 - return - end - for k = 1, #ast do - findstrings(ast[k]) - end -end - -local getstringreps = function(ast) - stringreps = {} - findstrings(ast) - - local function bytessaved(str,instances,bytereplacement) - local spacetaken = string.len(str) * instances - local minspacetaken = instances * bytereplacement - return spacetaken - minspacetaken - end - - local sstbl = {} - for k,v in pairs(stringreps) do table.insert(sstbl,{k,bytessaved(k,v,2)}) end - table.sort(sstbl,function(a,b) return a[2] > b[2] end) - - return sstbl -end - -local function astwalker(ast) - for i,j in pairs(optimi) do - local new = j(ast) - changed = changed or new - end - for k,v in pairs(ast) do - if type(v) == "table" then - astwalker(v) - end - end - for i,j in pairs(optimi) do - local new = j(ast) - changed = changed or new - end -end - -local syntax = {} - -local function stringfor(ast,tbl) - if syntax[ast.tag] ~= nil then - return syntax[ast.tag](ast,tbl) - else - print("Valid tags are:") - for k,v in pairs(syntax) do - print(k) - end - printtable(ast) - error("Attempted to use unknown tag type:" .. ast.tag) - end -end - -syntax = { - ["Call"] = function(ast,tbl) - local exprname = stringfor(ast[1],tbl) - 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 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 - ret[2] = ":" - ret[3] = ast[2][1] - ret[4] = "(" - elseif tbl.strings[ast[2][1]] ~= nil then - ret[2] = "[" - ret[3] = tbl.strings[ast[2][1]] - ret[4] = "](" - else - 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 - 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) - if tbl.strings[ast[1]] then - --print("Found it, it is", tbl.strings[ast[1]]) - return tbl.strings[ast[1]] - end - if tbl.strings[ast[1]] == nil then - if string.find(ast[1],"\"") then - sop = "[[" - eop = "]]" - end - return table.concat({sop,ast[1],eop}) - end - --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 tbl.ids[ast[1]] == nil then - ret = ret .. ast[1] - last = true - return ret - end - 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" and tbl.strings[ast[2][1]] == nil then - last = true - return table.concat({globalvar, ".", ast[2][1]}) - end - 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) - 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] - 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) - 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]) - else - if tbl.ids[ast[k][1]] ~= nil then - print("Found id in id table") - outputtbl[#outputtbl + 1] = tbl.ids[ast[k][1]] - else - local newvar = getnextvarname(tbl.lname) - tbl.lname = newvar - tbl.ids[ast[k][1]] = newvar - outputtbl[#outputtbl + 1] = newvar - print("Not found, output is ", newvar) - end - last = false - end - end - local output = bef .. table.concat(outputtbl, ",") - print("Output from namelist is ", output) - 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) - local ret - if last then ret = " nil" else ret = "nil" end - last = true - return ret - end, - ["True"] = function(ast,tbl) - local ret = "!!1" - last = true - return ret - end, - ["False"] = function(ast,tbl) - local ret = "!1" - 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 - 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 - 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(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(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. - if last then - codeblocks[#codeblocks + 1] = " else" .. block - else - codeblocks[#codeblocks + 1] = "else" .. block - end - end - 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 = true - 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]] - else - local newvar = getnextvarname(tbl.lname) - tbl.lname = newvar - 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 - last = false - incrementer = stringfor(ast[4],tbl) - if incrementer ~= 1 then - spargs[7] = "," .. incrementer - else - last = true - 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(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", - ["ne"] = "~=", ["eq"] = "==", - ["le"] = "<=", ["ge"] = ">=", - ["lt"] = "<", ["gt"] = ">", - ["bor"] = "|", ["bxor"] = "~", - ["band"] = "&", ["shl"] = "<<", - ["shr"] = ">>", ["concat"] = "..", - ["add"] = "+", ["sub"] = "-", - ["mul"] = "*", ["div"] = "/", - ["mod"] = "%", ["pow"] = "^", - } - local uniop = { - ["len"] = "#", ["not"] = "not", - ["unm"] = "-", ["bnot"] = "~", - } - local opname = ast[1] - if uniop[opname] ~= nil then - --Some special case where the parser messes up, fix it here. - --It translates ~= into not ==, but the order of operations makes it so == is evaluated first, and not second. - local bef - if opname == "not" and ast[2]["tag"] == "Op" and ast[2][1] == "eq" then - ast[2][1] = "ne" - local ret = stringfor(ast[2],tbl) - return ret - end - if last then bef = " " else bef = "" end - local rhs = stringfor(ast[2],tbl) - 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) - sargs[4] = rhs - local output = table.concat(sargs) - return output - end, - ["Pair"] = function(ast,tbl) - local lhs = stringfor(ast[1],tbl) - local rhs = stringfor(ast[2],tbl) - return table.concat({"[",lhs,"]=",rhs}) - 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) - 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") - local output = bef .. "local" .. lhs - if ast[2].tag ~= nil then - last = false - rhs = stringfor(ast[2],tbl) - output = output .. "=" .. rhs - end - return output - end, - ["VarList"] = function(ast,tbl) - 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) - local lhs = {} - 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 - end, - ["Label"] = function(ast,tbl) - if tbl.nids[ast[1]] == nil then - local nextvar = getnextvarname(tbl.lname) - tbl.lname = nextvar - tbl.nids[ast[1]] = nextvar - end - last = false - return "::" .. tbl.nids[ast[1]] .. "::" - end, - ["Goto"] = function(ast,tbl) - if tbl.nids[ast[1]] == nil then - local nextvar = getnextvarname(tbl.lname) - tbl.lname = nextvar - tbl.nids[ast[1]] = nextvar - end - last = true - return (last and " " or "") .. "goto " .. tbl.nids[ast[1]] - end, - ["Function"] = function(ast,tbl) - --Sometimes the parser fucks up, correct it here - if ast[1][1] ~= nil and ast[1][1].tag == nil then - ast[1] = ast[1][1] - error("Detected parser fuckup") - end - --end of parser-fuckup-fix code - 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) - 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 = ast[1][1] - local args = ast[2][1][1] - local func = ast[2][1][2] - local bf = {} - if last then bf[1] = " local" else bf[1] = "local" end - bf[2] = stringfor(ident,tbl) --ident - bf[3] = " function" - bf[4] = "(" - last = false - bf[5] = stringfor(args,tbl) --args - bf[6] = ")" - last = false - bf[7] = stringfor(func,tbl) -- function - if last then bf[8] = " end" else bf[8] = "end" end - last = true - return table.concat(bf) - - --[==[ - --Sometimes the parser fucks up, correct it here - print("in localrec, ast is") - printtable(ast) - if ast[1][1] ~= nil and ast[1].tag == nil then - ast[1] = ast[1][1] - --error("Detected parser fuckup") - print("after fixing fuckup, ast was") - printtable(ast) - else - print("ast[1][1] is",ast[1][1]) - printtable(ast[1][1]) - end - --end of parser-fuckup-fix code - local ident = stringfor(ast[1],tbl) - --[=[ - if tbl.ids[ast[1][1]] ~= nil then - ident = tbl.ids[ast[1][1]] - else - local newvar = getnextvarname(tbl.lname) - tbl.lname = newvar - 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(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) - 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 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) - local ret - if last then ret = " break" else ret = "break" end - last = true - return ret - end, - ["Block"] = function(ast,oldtbl) - local tbl = deepcopy(oldtbl) - oldtbl.block = true - local codeblocks = {} - for k = 1,#ast do - codeblocks[#codeblocks + 1] = stringfor(ast[k],tbl) - end - local code = table.concat(codeblocks) - oldtbl.numlocals = tbl.numlocals - return code - end, -} - -glum.minify = function(str, name) - name = name or "anonymous" - local ast, error_msg = parser.parse(str, name) - if not ast then - error(error_msg) - return nil - end - astwalker(ast) - print("After astwalker, ast is") - printtable(ast) - print("Finding string reps") - local strreps = getstringreps(ast) - printtable(strreps) - - local olocalvar = { - ["numlocals"] = 0, - ["strings"] = {}, - ["ids"] = {}, - ["lname"] = "", - ["nids"] = {}, - } - - --printtable(ast) - local lvt = deepcopy(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] * #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 - olocalvar.lname = nvar - end - - local lhss,rhss = {},{} - for k,v in pairs(olocalvar.strings) do - lhss[#lhss + 1] = v - rhss[#rhss + 1] = string.format("%q",k) - end - local inits = "" - --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) - - return inits .. stringfor(ast,olocalvar) -end - -return glum +--[[
+This moudle allows you to minify gLua code
+ Use:
+ local x = require("glum")
+ local str ="
+ --Here is some code to be minified!\n
+ for a=1,10,2 do\n
+ print(a)\n
+ end
+ "
+ print(x.minify(str[,name]))
+ Dependencies:
+ lua-parser
+ 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
+local optimi
+parser = require("lua-parser.parser")
+msg = io.write
+optimi = require("glum.ast_opts")
+local lpeg = require("lpeg")
+lpeg.locale(lpeg)
+
+local glum = {}
+
+--Checks if two tables are the same
+local function deepcompare(tbl1, tbl2)
+ for k,v in pairs(tbl1) do
+ if type(v) == "table" then
+ if not deepcompare(v,tbl2[k]) then
+ return false
+ end
+ else
+ if v ~= tbl2[k] then
+ return false
+ end
+ end
+ end
+end
+
+--Creates a deep copy of a table
+local function deepcopy(orig)
+ local orig_type = type(orig)
+ local copy
+ if orig_type == "table" then
+ copy = {}
+ for orig_key, orig_value in next, orig, nil do
+ copy[deepcopy(orig_key)] = deepcopy(orig_value)
+ end
+ setmetatable(copy, deepcopy(getmetatable(orig)))
+ else -- number, string, boolean, etc
+ copy = orig
+ end
+ 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 = true --we can start with no space
+
+--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 = {}
+for k,v in ipairs(nonames) do
+ reservednames[v] = true
+end
+
+--A function that generates the next valid variable name from the last valid variable name.
+local varchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY"
+local function getnextvarname(lname)
+ local place = string.find(lname,"[" .. varchars .. "]")
+ local length = string.len(lname)
+ if place == nil then
+ return string.rep("a", length + 1)
+ else
+ local lbyte = string.byte(lname,place)
+ local newchar = string.char(lbyte + (lbyte < 122 and 1 or -57))
+ local after = string.sub(lname, place + 1, length)
+ local before = string.rep("a", place-1)
+ local output = before .. newchar .. after
+ while reservednames[output] or _G[output] do
+ output = getnextvarname(output)
+ end
+ return output
+ end
+end
+
+--A debugging function, a replacement for glua PrintTable
+local function printtable(tbl, tabset)
+ tabset = tabset or 0
+ for k,v in pairs(tbl) do
+ for i = 0,tabset do msg("\t") end
+ msg(k .. ":")
+ if type(v) == "table" then
+ msg("\n")
+ printtable(v, tabset + 1)
+ else
+ msg(tostring(v) .. "\n")
+ end
+ end
+end
+
+local stringreps
+local function findstrings(ast)
+ if type(ast) ~= "table" then return end
+ if ast and ast.tag == "String" then
+ local lnum = stringreps[ast[1]]
+ stringreps[ast[1]] = lnum and lnum + 1 or 1
+ return
+ end
+ for k = 1, #ast do
+ findstrings(ast[k])
+ end
+end
+
+local getstringreps = function(ast)
+ stringreps = {}
+ findstrings(ast)
+
+ local function bytessaved(str,instances,bytereplacement)
+ local spacetaken = (string.len(str) + 2) * instances
+ local minspacetaken = (instances * bytereplacement) + string.len(str) + 2
+ return spacetaken - minspacetaken
+ end
+
+ local sstbl = {}
+ for k,v in pairs(stringreps) do
+ table.insert(sstbl,{k,bytessaved(k,v,2)})
+ end
+ table.sort(sstbl,function(a,b) return a[2] > b[2] end)
+ return sstbl
+end
+
+local function astwalker(ast)
+ --print("ast walker looking at")
+ --printtable(ast)
+ local changed
+ repeat
+ changed = false
+ for i,j in pairs(optimi) do
+ local new = j(ast)
+ changed = changed or new
+ end
+ for k,v in pairs(ast) do
+ if type(v) == "table" then
+ astwalker(v)
+ end
+ end
+ for i,j in pairs(optimi) do
+ local new = j(ast)
+ changed = changed or new
+ end
+ until changed == false
+end
+
+local syntax = {}
+
+local function stringfor(ast,tbl)
+ if syntax[ast.tag] ~= nil then
+ local r = syntax[ast.tag](ast,tbl)
+ assert(type(r) == "string", "Stringfor did not return a string! returned a " .. type(r) .. " for tag:" .. ast.tag)
+ return r
+ elseif ast.tag == nil and ast[1] and ast[1].tag ~= nil then
+ --TODO: Is this a bug in the parser?
+ --sometimes parts of the ast will be 1 extra level deep
+ --for seemingly no reason
+ return stringfor(ast[1],tbl)
+ else
+ print("Valid tags are:")
+ for k,v in pairs(syntax) do
+ print(k)
+ end
+ print("Tried to get stringfor on:\n----------------")
+ printtable(ast)
+ print("----------------")
+ error("Attempted to use unknown tag type:" .. tostring(ast.tag))
+ end
+end
+
+syntax = {
+ ["Call"] = function(ast,tbl)
+ local exprname = stringfor(ast[1],tbl)
+ 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 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
+ ret[2] = ":"
+ ret[3] = ast[2][1]
+ ret[4] = "("
+ elseif tbl.strings[ast[2][1]] ~= nil then
+ ret[2] = "["
+ ret[3] = tbl.strings[ast[2][1]]
+ ret[4] = "]("
+ else
+ last = false
+ ret[2] = "["
+ ret[3] = stringfor(ast[2],tbl)
+ ret[4] = "]("
+ ret[5] = stringfor(ast[1],tbl)
+ ret[6] = ","
+ end
+ last = false
+ local args = {}
+ for k = 3,#ast do
+ local nar = stringfor(ast[k],tbl)
+ args[#args + 1] = nar
+ last = false
+ end
+ 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)
+ if tbl.strings[ast[1]] then
+ --print("Found it, it is", tbl.strings[ast[1]])
+ return tbl.strings[ast[1]]
+ end
+ if tbl.strings[ast[1]] == nil then
+ if string.find(ast[1],"\"") then
+ sop = "[["
+ eop = "]]"
+ end
+ return table.concat({sop,ast[1],eop})
+ end
+ --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 tbl.ids[ast[1]] == nil then
+ ret = ret .. ast[1]
+ last = true
+ return ret
+ end
+ 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" and tbl.strings[ast[2][1]] == nil and ast[2][1]:find(" ") == nil then
+ last = true
+ return table.concat({globalvar, ".", ast[2][1]})
+ end
+ 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,
+ ["Repeat"] = function(ast,tbl)
+ local codetbl = {}
+ if last then codetbl[1] = " repeat" else codetbl[1] = "repeat" end
+ last = true
+ local scoped = deepcopy(tbl)
+ local block = stringfor(ast[1],scoped)
+ codetbl[2] = block
+ if last then codetbl[3] = " until" else codetbl[3] = "until" end
+ local condition = stringfor(ast[2],scoped)
+ codetbl[4] = condition
+ local output = table.concat(codetbl)
+ tbl.numlocals = scoped.numlocals
+ return output
+ end,
+ ["Forin"] = function(ast,tbl)
+ 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]
+ 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)
+ 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])
+ else
+ if tbl.ids[ast[k][1]] ~= nil then
+ --print("Found id in id table")
+ outputtbl[#outputtbl + 1] = tbl.ids[ast[k][1]]
+ else
+ local newvar = getnextvarname(tbl.lname)
+ tbl.lname = newvar
+ tbl.ids[ast[k][1]] = newvar
+ outputtbl[#outputtbl + 1] = newvar
+ end
+ last = false
+ end
+ end
+ 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)
+ local ret
+ if last then ret = " nil" else ret = "nil" end
+ last = true
+ return ret
+ end,
+ ["True"] = function(ast,tbl)
+ local ret = "!!1"
+ last = true
+ return ret
+ end,
+ ["False"] = function(ast,tbl)
+ local ret = "!1"
+ 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
+ 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
+ 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(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(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.
+ if last then
+ codeblocks[#codeblocks + 1] = " else" .. block
+ else
+ codeblocks[#codeblocks + 1] = "else" .. block
+ end
+ end
+ 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 = true
+ 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]]
+ else
+ local newvar = getnextvarname(tbl.lname)
+ tbl.lname = newvar
+ 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
+ last = false
+ incrementer = stringfor(ast[4],tbl)
+ if incrementer ~= 1 then
+ spargs[7] = "," .. incrementer
+ else
+ last = true
+ 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(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",
+ ["ne"] = "~=", ["eq"] = "==",
+ ["le"] = "<=", ["ge"] = ">=",
+ ["lt"] = "<", ["gt"] = ">",
+ ["bor"] = "|", ["bxor"] = "~",
+ ["band"] = "&", ["shl"] = "<<",
+ ["shr"] = ">>", ["concat"] = "..",
+ ["add"] = "+", ["sub"] = "-",
+ ["mul"] = "*", ["div"] = "/",
+ ["mod"] = "%", ["pow"] = "^",
+ }
+ local uniop = {
+ ["len"] = "#", ["not"] = "not",
+ ["unm"] = "-", ["bnot"] = "~",
+ }
+ local opname = ast[1]
+ if uniop[opname] ~= nil then
+ --Some special case where the parser messes up, fix it here.
+ --It translates ~= into not ==, but the order of operations makes it so == is evaluated first, and not second.
+ local bef
+ if opname == "not" and ast[2]["tag"] == "Op" and ast[2][1] == "eq" then
+ ast[2][1] = "ne"
+ local ret = stringfor(ast[2],tbl)
+ return ret
+ end
+ if last then bef = " " else bef = "" end
+ local rhs = stringfor(ast[2],tbl)
+ return bef .. uniop[opname] .. rhs
+ end
+ local sargs = {}
+ local lhs = stringfor(ast[2],tbl)
+ sargs[1] = lhs
+ if opname == "or" or opname == "and" then
+ if last then
+ sargs[2] = " "
+ else
+ sargs[2] = ""
+ end
+ last = true
+ else
+ sargs[2] = ""
+ last = false
+ end
+ sargs[3] = binop[opname]
+ local rhs = stringfor(ast[3],tbl)
+ sargs[4] = rhs
+ local output = table.concat(sargs)
+ return output
+ end,
+ ["Pair"] = function(ast,tbl)
+ if ast[1].tag == "String" and tbl.strings[ast[1][1]] == nil and ast[1][1]:find(" ") == nil then
+ local lhs = ast[1][1]
+ last=false
+ local rhs = stringfor(ast[2],tbl)
+ return table.concat({lhs,"=",rhs})
+ else
+ local lhs = stringfor(ast[1],tbl)
+ last=false
+ local rhs = stringfor(ast[2],tbl)
+ return table.concat({"[",lhs,"]=",rhs})
+ end
+ 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)
+ 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")
+ local output = bef .. "local" .. lhs
+ if ast[2].tag ~= nil then
+ last = false
+ rhs = stringfor(ast[2],tbl)
+ output = output .. "=" .. rhs
+ end
+ return output
+ end,
+ ["VarList"] = function(ast,tbl)
+ 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)
+ local lhs = {}
+ 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
+ end,
+ ["Label"] = function(ast,tbl)
+ if tbl.nids[ast[1]] == nil then
+ local nextvar = getnextvarname(tbl.lname)
+ tbl.lname = nextvar
+ tbl.nids[ast[1]] = nextvar
+ end
+ last = false
+ return "::" .. tbl.nids[ast[1]] .. "::"
+ end,
+ ["Goto"] = function(ast,tbl)
+ if tbl.nids[ast[1]] == nil then
+ local nextvar = getnextvarname(tbl.lname)
+ tbl.lname = nextvar
+ tbl.nids[ast[1]] = nextvar
+ end
+ last = true
+ return (last and " " or "") .. "goto " .. tbl.nids[ast[1]]
+ end,
+ ["Function"] = function(ast,tbl)
+ --Sometimes the parser fucks up, correct it here
+ if ast[1][1] ~= nil and ast[1][1].tag == nil then
+ ast[1] = ast[1][1]
+ error("Detected parser fuckup")
+ end
+ --end of parser-fuckup-fix code
+ 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)
+ 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 = ast[1][1]
+ local args = ast[2][1][1]
+ local func = ast[2][1][2]
+ local bf = {}
+ if last then bf[1] = " local function" else bf[1] = "local function" end
+ last = true
+ bf[2] = stringfor(ident,tbl) --ident
+ bf[3] = "("
+ last = false
+ if #args ~= 0 then
+ bf[4] = stringfor(args,tbl) --args
+ else
+ bf[4] = ""
+ end
+ bf[5] = ")"
+ last = false
+ bf[6] = stringfor(func,tbl) -- function
+ if last then bf[7] = " end" else bf[7] = "end" end
+ last = true
+ return table.concat(bf)
+
+ --[==[
+ --Sometimes the parser fucks up, correct it here
+ print("in localrec, ast is")
+ printtable(ast)
+ if ast[1][1] ~= nil and ast[1].tag == nil then
+ ast[1] = ast[1][1]
+ --error("Detected parser fuckup")
+ print("after fixing fuckup, ast was")
+ printtable(ast)
+ else
+ print("ast[1][1] is",ast[1][1])
+ printtable(ast[1][1])
+ end
+ --end of parser-fuckup-fix code
+ local ident = stringfor(ast[1],tbl)
+ --[=[
+ if tbl.ids[ast[1][1]] ~= nil then
+ ident = tbl.ids[ast[1][1]]
+ else
+ local newvar = getnextvarname(tbl.lname)
+ tbl.lname = newvar
+ 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(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)
+ 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 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)
+ local ret
+ if last then ret = " break" else ret = "break" end
+ last = true
+ return ret
+ end,
+ ["Block"] = function(ast,oldtbl)
+ local tbl = deepcopy(oldtbl)
+ oldtbl.block = true
+ local codeblocks = {}
+ for k = 1,#ast do
+ codeblocks[#codeblocks + 1] = stringfor(ast[k],tbl)
+ end
+ local code = table.concat(codeblocks)
+ oldtbl.numlocals = tbl.numlocals
+ return code
+ end,
+}
+
+glum.minify = function(str, name)
+ name = name or "anonymous"
+ local ast, error_msg = parser.parse(str, name)
+ if not ast then
+ error(error_msg)
+ return nil
+ end
+ astwalker(ast)
+ --print("After astwalker, ast is")
+ --printtable(ast)
+ --print("Finding string reps")
+ local strreps = getstringreps(ast)
+ --printtable(strreps)
+
+ local olocalvar = {
+ ["numlocals"] = 0,
+ ["strings"] = {},
+ ["ids"] = {},
+ ["lname"] = "",
+ ["nids"] = {},
+ }
+
+ --printtable(ast)
+ local lvt = deepcopy(olocalvar)
+ local numstrreplaced = 0
+ local maxlocals = lvt.numlocals
+ local function shouldreplace(strrep)
+ return strreps[strrep + 1][2] >= 7
+ end
+ while
+ (numstrreplaced + maxlocals < 200) and --We have some locals left
+ (numstrreplaced < #strreps) and --We have more strings to replace
+ shouldreplace(numstrreplaced) 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
+ olocalvar.lname = nvar
+ end
+
+ local lhss,rhss = {},{}
+ for k,v in pairs(olocalvar.strings) do
+ lhss[#lhss + 1] = v
+ rhss[#rhss + 1] = string.format("%q",k)
+ end
+ local inits = ""
+ --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)
+
+ return inits .. stringfor(ast,olocalvar)
+end
+
+return glum
|
