From 81fb5ea551c3dd8cfcb8e30c0dfed32a9000eea1 Mon Sep 17 00:00:00 2001 From: Alexander Pickering Date: Fri, 2 Sep 2016 21:42:28 -0400 Subject: Added local variable counting and long string replacement --- src/glum.lua | 145 ++++++++++++++++++++++++++++++++++++++++++++++----------- src/parser.lua | 10 +++- 2 files changed, 127 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/glum.lua b/src/glum.lua index 7ee37b6..969dc27 100644 --- a/src/glum.lua +++ b/src/glum.lua @@ -14,7 +14,15 @@ This moudle allows you to minify gLua code lpeg ]] -local parser = dofile("../src/parser.lua") +local parser +local msg +if include ~= nil then + parser = include("./parser.lua") + msg = Msg +else + parser = dofile("../src/parser.lua") + msg = io.write +end local lpeg = require("lpeg") lpeg.locale(lpeg) @@ -67,17 +75,47 @@ end local function printtable(tbl, tabset) tabset = tabset or 0 for k,v in pairs(tbl) do - for i = 0,tabset do io.write("\t") end - io.write(k .. ":") + for i = 0,tabset do msg("\t") end + msg(k .. ":") if type(v) == "table" then - io.write("\n") + msg("\n") printtable(v, tabset + 1) else - io.write(tostring(v) .. "\n") + 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 syntax = {} local function stringfor(ast,tbl) @@ -88,6 +126,7 @@ local function stringfor(ast,tbl) for k,v in pairs(syntax) do print(k) end + printtable(ast) error("Attempted to use unknown tag type:" .. ast.tag) end end @@ -116,9 +155,11 @@ syntax = { local output = func local inv --A short hand if it's a simple thing - if ast[2].tag == "String" and #ast[2][1] < (#func + 2) then + 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, "("}) + elseif tbl.strings[ast[2][1]] ~= nil then + output = table.concat({output,"[", tbl.strings[ast[2][1]], "]","("}) else inv = stringfor(ast[2],tbl) output = table.concat({output, "[", inv, "](", func, ","}) @@ -128,6 +169,12 @@ syntax = { 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 = "[[" @@ -160,9 +207,12 @@ syntax = { ["Forin"] = function(ast,tbl) local nadd = deepcopy(tbl) local nl = stringfor(ast[1],nadd) + nadd.numlocals = nadd.numlocals + #ast[1] + print("Found",#ast[1],"locals as Forin") local el = stringfor(ast[2],nadd) local code = stringfor(ast[3],nadd) local output = table.concat({" for ", nl, " in ", el, " do ", code, " end "}) + tbl.numlocals = nadd.numlocals return output end, ["NameList"] = function(ast,tbl) @@ -207,6 +257,16 @@ syntax = { end return " return " .. table.concat(retargs,",") end, + ["Do"] = function(ast,tbl) + local ntbl = deepcopy(tbl) + local allst = {} + for k = 1,#ast do + allst[k] = stringfor(ast[k],ntbl) + end + local code = table.concat(allst,";") + tbl.numlocals = ntbl.numlocals + return table.concat({" do ", code," end "}) + end, ["If"] = function(ast,tbl) local expr1 = stringfor(ast[1],tbl) @@ -222,7 +282,9 @@ syntax = { if #ast % 2 == 1 then local block = stringfor(ast[#ast],tbl) - codeblocks[#codeblocks + 1] = " else " .. block + if block ~= "" then --If for some reason there's an empty else block, forget about it. + codeblocks[#codeblocks + 1] = " else " .. block + end end codeblocks[#codeblocks + 1] = " end " return table.concat(codeblocks) @@ -253,6 +315,8 @@ syntax = { 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 "}) end, ["Op"] = function(ast,tbl) @@ -305,10 +369,11 @@ syntax = { return ast[1] end, ["Local"] = function(ast,tbl) - local tblcpy = tbl - local lhs,rhs = stringfor(ast[1],tblcpy),nil + local lhs,rhs = stringfor(ast[1],tbl),nil + tbl.numlocals = tbl.numlocals + #ast[1] + print("Found",#ast[1],"locals as Local") if ast[2].tag ~= nil then - rhs = stringfor(ast[2],tblcpy) + rhs = stringfor(ast[2],tbl) end local output = "local " .. lhs if ast[2].tag ~= nil then @@ -371,6 +436,8 @@ syntax = { end local argstr = ast[2][1][1].tag ~= nil and stringfor(ast[2][1][1],tbl) or "" local expr = stringfor(ast[2][1][2],tbl) + tbl.numlocals = tbl.numlocals + 1 + print("Found 1 local as Localrec") return table.concat({" local function ",ident,"(",argstr,")",expr," end "}) end, ["Continue"] = function(ast,tbl) @@ -393,20 +460,8 @@ syntax = { codeblocks[#codeblocks + 1] = stringfor(ast[k],tbl) end local code = table.concat(codeblocks) - local lhss,rhss = {},{} - for k,v in pairs(tbl.strings) do - if oldtbl.strings[k] ~= tbl.strings[k] then - lhss[#lhss + 1] = v - rhss[#rhss + 1] = string.format("%q",k) - end - end - local inits = "" - local lhs = " local " .. table.concat(lhss,",") - local rhs = table.concat(rhss,",") - if string.len(rhs) > 0 then - inits = table.concat({lhs, "=", rhs, ";"}) - end - return inits .. code + oldtbl.numlocals = tbl.numlocals + return code end, } @@ -441,19 +496,57 @@ glum.minify = function(str, name) local ast, error_msg = parser.parse(str, name) if not ast then error(error_msg) + return nil end - local localvar = { + print("Finding string reps") + local strreps = getstringreps(ast) + printtable(strreps) + + local olocalvar = { ["numlocals"] = 0, ["strings"] = {}, ["ids"] = {}, ["lname"] = "", ["nids"] = {}, } + --printtable(ast) - return stringfor(ast,localvar) + 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 " + 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 glum.uglify = function(str) + assert(str ~= nil, "Cannot uglify a nil string") local avalchars = {} local capture_chars = {"%","(","[","\13"} local skipchars = {} diff --git a/src/parser.lua b/src/parser.lua index 0770bf7..56375d6 100644 --- a/src/parser.lua +++ b/src/parser.lua @@ -48,7 +48,12 @@ opid: 'add' | 'sub' | 'mul' | 'div' | 'idiv' | 'mod' | 'pow' | 'concat' ]] local parser = {} local lpeg = require("lpeg") -local scope = dofile("../src/scope.lua") +local scope +if include ~= nil then + scope = include("./scope.lua") +else + scope = dofile("../src/scope.lua") +end lpeg.locale(lpeg) @@ -843,7 +848,8 @@ end function parser.parse (subject, filename) local errorinfo = { subject = subject, filename = filename } - --lpeg.setmaxstack(1000) + 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) -- cgit v1.2.3-70-g09d2