diff options
| author | Alexander Pickering <alexandermpickering@gmail.com> | 2016-07-01 22:08:45 -0400 |
|---|---|---|
| committer | Alexander Pickering <alexandermpickering@gmail.com> | 2016-07-01 22:08:45 -0400 |
| commit | 774b296d3e49b8be3b0feaee8b5d3154fcec73b6 (patch) | |
| tree | e076254b6332c177dc34b4d87bc222f52ca49646 /src/glum.lua | |
| download | glum-774b296d3e49b8be3b0feaee8b5d3154fcec73b6.tar.gz glum-774b296d3e49b8be3b0feaee8b5d3154fcec73b6.tar.bz2 glum-774b296d3e49b8be3b0feaee8b5d3154fcec73b6.zip | |
Initial commit
Diffstat (limited to 'src/glum.lua')
| -rw-r--r-- | src/glum.lua | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/src/glum.lua b/src/glum.lua new file mode 100644 index 0000000..ff05953 --- /dev/null +++ b/src/glum.lua @@ -0,0 +1,444 @@ +--[[ +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 +]] +local parser = dofile("../src/parser.lua") +local lpeg = require("lpeg") +lpeg.locale(lpeg) + +local glum = {} + +--- Creates a deep copy of a table. +-- Creates a deep copy, will even copy metamethods. +-- @tab orig the original table to copy +-- @return a copy of the table +local function deepcopy(orig) end --Creates a deep copy of a table + +local function getnextvarname(latname) end --generates the next valid variable name from the last valid variable name. + +local function printtable(tbl) end --A debugging function, a replacement for glua PrintTable + +local function stringfor(ast,tbl) end --Returns the string for the given abstract syntax tree, within the scope of tbl + +local function removespaces(string) end --Removes extra spaces and semicolons in string + + +--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 + +--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 io.write("\t") end + io.write(k .. ":") + if type(v) == "table" then + io.write("\n") + printtable(v, tabset + 1) + else + io.write(tostring(v) .. "\n") + end + 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 willox ever decides to add new language features, they need to be added to BOTH parser.lua and here. +local syntax = { + ["Call"] = function(ast,tbl) + local exprname = stringfor(ast[1],tbl) + local argnames = {} + local cursor = 2 + while ast[cursor] ~= nil do + argnames[cursor-1] = stringfor(ast[cursor],tbl) + cursor = cursor + 1 + end + local argstring = table.concat(argnames,",") + local ostring = table.concat({exprname,"(",argstring,")"}) + 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 + if ast[2].tag == "String" and #ast[2][1] < (#func + 2) then + inv = ast[2][1] + output = output .. ":" .. inv .. "(" + else + inv = stringfor(ast[2],tbl) + output = output .. "[" .. inv .. "](" .. func .. "," + end + output = output .. table.concat(invargs,",") + output = output .. ")" + return output + end, + ["String"] = function(ast,tbl) + if tbl.strings[ast[1]] == nil then + local nextvar = getnextvarname(tbl.lname) + tbl.lname = nextvar + tbl.strings[ast[1]] = nextvar + return nextvar + end + return tbl.strings[ast[1]] + end, + ["Id"] = function(ast,tbl) + if tbl.ids[ast[1]] == nil then + return ast[1] + end + return tbl.ids[ast[1]] + end, + ["Index"] = function(ast,tbl) + local globalvar = stringfor(ast[1],tbl) + if ast[2].tag == "String" then + return table.concat({globalvar, ".", ast[2][1]}) + end + return table.concat({globalvar, "[", stringfor(ast[2],tbl), "]"}) + end, + ["Paren"] = function(ast,tbl) + return table.concat({"(" .. stringfor(ast[1],tbl) .. ")"}) + end, + ["Dots"] = function(ast,tbl) + return "..." + end, + ["Forin"] = function(ast,tbl) + local nadd = deepcopy(tbl) + local nl = stringfor(ast[1],nadd) + local el = stringfor(ast[2],nadd) + local code = stringfor(ast[3],nadd) + local output = table.concat({" for ", nl, " in ", el, " do ", code, " end "}) + return output + end, + ["NameList"] = function(ast,tbl) + local outputtbl = {} + 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 + 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 + end + end + local output = table.concat(outputtbl, ",") + return output + end, + ["ExpList"] = function(ast,tbl) + local exprs = {} + for k = 1,#ast do + exprs[#exprs + 1] = stringfor(ast[k],tbl) + end + return table.concat(exprs,",") + end, + ["Nil"] = function(ast,tbl) + return "nil" + end, + ["True"] = function(ast,tbl) + return "true" + end, + ["False"] = function(ast,tbl) + return "false" + end, + ["Return"] = function(ast,tbl) + local retargs = {} + for k,v in ipairs(ast) do + retargs[k] = stringfor(v,tbl) + end + return " return " .. table.concat(retargs,",") + end, + ["If"] = function(ast,tbl) + + local expr1 = stringfor(ast[1],tbl) + local block1 = stringfor(ast[2],tbl) + local codeblocks = {} + codeblocks[#codeblocks + 1] = table.concat({" if ",expr1," then ",block1}) + + for k = 3,#ast-1,2 do + local expr = stringfor(ast[k],tbl) + local block = stringfor(ast[k + 1],tbl) + codeblocks[#codeblocks + 1] = table.concat({" elseif " , expr , " then " , block}) + end + + if #ast % 2 == 1 then + local block = stringfor(ast[#ast],tbl) + codeblocks[#codeblocks + 1] = " else " .. block + end + codeblocks[#codeblocks + 1] = " end " + return table.concat(codeblocks) + end, + ["Fornum"] = function(ast,tbl) + local var + 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 + local start = stringfor(ast[2],tbl) + local endnum = stringfor(ast[3],tbl) + local incrementer = 1 + local code = "" + if ast[4].tag ~= "Block" then -- incrementer + incrementer = stringfor(ast[4],tbl) + code = stringfor(ast[5],tbl) + else + code = stringfor(ast[4],tbl) + end + local incstr = incrementer ~= 1 and ("," .. incrementer) or "" + tbl[var] = nil + return table.concat({" for ",var,"=",start,",",endnum,incstr," do ",code," end "}) + end, + ["Op"] = function(ast,tbl) + 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 + local rhs = stringfor(ast[2],tbl) + return optbl[opname] .. rhs + end + local lhs = stringfor(ast[2],tbl) + local rhs = stringfor(ast[3],tbl) + local output = table.concat(lhs,binop[opname],rhs) + 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 = {} + for k = 1, #ast do + fields[#fields + 1] = stringfor(ast[k],tbl) + end + local fieldstr = table.concat(fields,",") + return table.concat({"{",fieldstr,"}"}) + end, + ["Number"] = function(ast,tbl) + return ast[1] + end, + ["Local"] = function(ast,tbl) + local tblcpy = tbl + local lhs,rhs = stringfor(ast[1],tblcpy),nil + if ast[2].tag ~= nil then + rhs = stringfor(ast[2],tblcpy) + end + local output = "local " .. lhs + if ast[2].tag ~= nil then + 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) + end + return table.concat(vars,",") + end, + ["Set"] = function(ast,tbl) + local lhs = {} + for k = 1,#ast[1] do + lhs[#lhs + 1] = stringfor(ast[1],tbl) + end + local rhs = {} + for k = 1,#ast[2] do + rhs[#rhs + 1] = stringfor(ast[2],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 + 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 + return " goto " .. tbl.nids[ast[1]] + end, + ["Function"] = function(ast,tbl) + local funcargs = stringfor(ast[1],tbl) + local code = stringfor(ast[2],tbl) + return table.concat({" function(",funcargs,")",code," end "}) + end, + ["Localrec"] = function(ast,tbl) + local ident + 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 argstr = stringfor(ast[2][1][1],tbl) + local expr = stringfor(ast[2][1][2],tbl) + return table.concat({" local function ",ident,"(",argstr,")",expr," end "}) + end, + ["Continue"] = function(ast,tbl) + return " continue " + end, + ["While"] = function(ast,tbl) + local expr = stringfor(ast[1],tbl) + local block = stringfor(ast[2],tbl) + local output = table.concat(" while " , expr , " do " , block , " end ") + return output + end, + ["Break"] = function(ast,tbl) + return " break " + 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) + 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 + end, +} + +local function stringfor(ast,tbl) + if syntax[ast.tag] ~= nil then + return syntax[ast.tag](ast,tbl) + else + error("Attempted to use unknown tag type:" .. ast.tag) + end +end + +--Removes extra spaces and duplicated ; from a string +local function removespaces(str) + local removables = { + {"%s*%)%s*","%)"}, --Spaces before or after ) + {"%s*%(%s*","%("}, --Spaces before or after ( + {"%s*;%s*",";"}, --Spaces before or after ; + {"%s*,%s*",","}, --Spaces before or after , + {";+",";"}, --Multiple ; in a row + {"^%s*",""}, --Spaces at the beginning of the file + {"%s*$",""}, --Spaces at the end of the file + {"%s+"," "}, --Multiple spaces in a row + } + --Order is important + for k,v in ipairs(removables) do + str = string.gsub(str,v[1],v[2]) + end + return str +end + +glum.minify = function(str, name) + local ast, error_msg = parser.parse(str, name) + if not ast then + error(error_msg) + end + local localvar = { + ["strings"] = {}, + ["ids"] = {}, + ["lname"] = "", + ["nids"] = {}, + } + return removespaces(stringfor(ast,localvar)) +end + +return glum |
