aboutsummaryrefslogtreecommitdiff
path: root/src/glum.lua
diff options
context:
space:
mode:
authorAlexander Pickering <alexandermpickering@gmail.com>2016-07-01 22:08:45 -0400
committerAlexander Pickering <alexandermpickering@gmail.com>2016-07-01 22:08:45 -0400
commit774b296d3e49b8be3b0feaee8b5d3154fcec73b6 (patch)
treee076254b6332c177dc34b4d87bc222f52ca49646 /src/glum.lua
downloadglum-774b296d3e49b8be3b0feaee8b5d3154fcec73b6.tar.gz
glum-774b296d3e49b8be3b0feaee8b5d3154fcec73b6.tar.bz2
glum-774b296d3e49b8be3b0feaee8b5d3154fcec73b6.zip
Initial commit
Diffstat (limited to 'src/glum.lua')
-rw-r--r--src/glum.lua444
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