diff options
| -rw-r--r-- | src/glum.lua | 145 | ||||
| -rw-r--r-- | src/parser.lua | 10 | ||||
| -rw-r--r-- | test/glum_test.lua | 350 |
3 files changed, 250 insertions, 255 deletions
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) diff --git a/test/glum_test.lua b/test/glum_test.lua index 4bd5e10..9f538ad 100644 --- a/test/glum_test.lua +++ b/test/glum_test.lua @@ -1,254 +1,150 @@ --local glum = dofile("../src/test.lua") -local glum = dofile("../src/test2.lua") -local str1 = [[ ---This is a test -print("hello") -local abracadabra = "b" -print(abracadabra) -]] -local str2 = [[ ---This is another test -local somtbl = {} -somtbl.test = function(self, what, who, where, when) - print("Thanks, I love " .. what .. "!") -end -somtbl:test(23) -]] -local str3 = [[ - ---Assign locals to these to the minifier can compress the file better -local strlen,chrat,min,asrt,prs,iprs,typ,upack,tblins,tblsrt,strsub,tru,fal = string.len,string.byte,math.min,assert,pairs,ipairs,type,unpack,table.insert,table.sort,string.sub,true,false - -local fuzzel = {} - ---A clever way to allow the minifier to minify function names, this basically just assigns variables with their string equivalent. -local da, le, di, ra, fu, fi, so, ex, ha, au = "Damerau", "Levenshtein", "Distance", "Ratio", "Fuzzy", "Find", "Sort", "_extended", "Hamming", "Autocomplete" -local LevenshteinDistance_extended,LevenshteinDistance,LevenshteinRatio,DamerauLevenshteinDistance_extended,DamerauLevenshteinDistance,DamerauLevenshteinRatio,FuzzyFindDistance,FuzzyFindRatio,FuzzySortDistance,FuzzySortRatio,HammingDistance,HammingRatio,FuzzyAutocompleteDistance,FuzzyAutocompleteRatio = le..di..ex,le..di,le..ra,da..le..di..ex,da..le..di,da..le..ra,fu..fi..di,fu..fi..ra,fu..so..di,fu..so..ra,ha..di,ha..ra,fu..au..di,fu..au..ra - -local function genericDistance( stringa, stringb, addcost, subcost, delcost, ...) - local arg = {...} - - --Length of each string - local salen, sblen = strlen(stringa), strlen(stringb) - - --Create a 0 matrix the size of len(a) x len(b) - local dyntbl = {} - for i = 0,salen do - dyntbl[i] = {} - for j = 0,sblen do - dyntbl[i][j] = 0 - end - end - - --Initalize the matrix - for i = 1,salen do - dyntbl[i][0] = i - end - for j = 1,sblen do - dyntbl[0][j] = j - end - - --And build up the matrix based on costs-so-far - for j = 1,sblen do - for i = 1,salen do - local ca,cb = chrat(stringa,i),chrat(stringb,j) - dyntbl[i][j] = min( - dyntbl[i-1][j] + delcost, --deletion - dyntbl[i][j-1] + addcost, --insertion - dyntbl[i-1][j-1] + (ca == cb and 0 or subcost) --substituion - ) - if arg[1] and i > 1 and j > 1 and ca == chrat(stringb,j-1) and chrat(stringa,i-1) == cb then - dyntbl[i][j] = min(dyntbl[i][j], - dyntbl[i-2][j-2] + (ca == cb and 0 or arg[2])) --transposition - end - end - end - - return dyntbl[salen][sblen] -end +local glum = dofile("../src/glum.lua") -fuzzel[LevenshteinDistance_extended] = function(stringa, stringb, addcost, subcost, delcost) - return genericDistance(stringa, stringb, addcost, subcost, delcost) -end -fuzzel.ld_e = fuzzel[LevenshteinDistance_extended] +local f5 = io.open("../src/glum.lua", "r") +local str5 = f5:read("*a") +f5:close() -fuzzel[LevenshteinDistance] = function(stringa,stringb) - return fuzzel.ld_e(stringa,stringb,1,1,1) -end -fuzzel.ld = fuzzel[LevenshteinDistance] +local str = [[ -fuzzel[LevenshteinRatio] = function(stringa,stringb) - return fuzzel.ld(stringa,stringb) / strlen(stringa) -end -fuzzel.lr = fuzzel[LevenshteinRatio] +AddCSLuaFile() -fuzzel[DamerauLevenshteinDistance_extended] = function(stringa, stringb, addcost, subcost, delcost, trncost) - return genericDistance(stringa,stringb,addcost,subcost,delcost,tru,trncost) -end -fuzzel.dld_e = fuzzel[DamerauLevenshteinDistance_extended] +ENT.Type = "point" +ENT.DisableDuplicator = true -fuzzel[DamerauLevenshteinDistance] = function(stringa,stringb) - return fuzzel.dld_e(stringa,stringb,1,1,1,1) -end -fuzzel.dld = fuzzel[DamerauLevenshteinDistance] +-- +-- Make this entity always networked +-- +function ENT:UpdateTransmitState() return TRANSMIT_ALWAYS end -fuzzel[DamerauLevenshteinRatio] = function(stringa,stringb) - return fuzzel.dld(stringa,stringb) / strlen(stringa) -end -fuzzel.dlr = fuzzel[DamerauLevenshteinRatio] - -fuzzel[HammingDistance] = function(stringa,stringb) - local len,dist = strlen(stringa),0 - asrt(len == strlen(stringb), ha.." "..di.." cannot be calculated on two strings of different lengths:\"" .. stringa .. "\" \"" .. stringb .. "\"") - for i = 1,len do - dist = dist + ((chrat(stringa,i) ~= chrat(stringb,i)) and 1 or 0) - end - return dist -end -fuzzel.hd = fuzzel[HammingDistance] +-- +-- Networked / Saved Data +-- +function ENT:SetupDataTables() -fuzzel[HammingRatio] = function(stringa,stringb) - return fuzzel.hd(stringa,stringb) / strlen(stringa) -end -fuzzel.hr = fuzzel[HammingRatio] - -local function FuzzySearch(str,func,...) - local arg = {...} - - --Allow varargs, or a table - local looparg = typ(arg[1]) == "table" and arg[1] or arg - - --Find the string with the shortest distance to the string we were supplied - local tmin,sout = func(looparg[1],str),looparg[1] - for k,v in prs(looparg) do - local t = func(v,str) - if t <= tmin then - tmin,sout = t,k - end - end - return looparg[sout], tmin -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 } } ) -fuzzel[FuzzyFindDistance] = function(str,...) - return upack{FuzzySearch(str,fuzzel.dld,...)} -end -fuzzel.ffd = fuzzel[FuzzyFindDistance] + 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" } } ) -fuzzel[FuzzyFindRatio] = function(str,...) - return upack{FuzzySearch(str,fuzzel.dlr,...)} -end -fuzzel.ffr = fuzzel[FuzzyFindRatio] - -local function FuzzySort(str, func, short, ...) - local arg = {...} - - --allow varargs, or a table - local looparg = typ(arg[1]) == "table" and arg[1] or arg - - --Roughly sort everything by it's distance to the string - local usorted,sorted,otbl,slen = {},{},{},strlen(str) - for k,v in prs(looparg) do - local sstr = short and strsub(v,0,slen) or v - local dist = func(str,sstr) - if usorted[dist] == nil then - usorted[dist] = {} - tblins(sorted,dist) - end - tblins(usorted[dist],v) - end - - --Actually sort them into something can can be iterated with ipairs - tblsrt(sorted) - - --Then build our output table - for k,v in iprs(sorted) do - for i,j in prs(usorted[v]) do - tblins(otbl,j) - end - end - return otbl -end -fuzzel.ffr = fuzzel[FuzzyFindRatio] + 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" } } ) -fuzzel[FuzzySortDistance] = function(str,...) - return FuzzySort(str,fuzzel.dld,fal,...) -end -fuzzel.fsd = fuzzel[FuzzySortDistance] + 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 } } ) -fuzzel[FuzzySortRatio] = function(str,...) - return FuzzySort(str,fuzzel.dlr,fal,...) -end -fuzzel.fsr = fuzzel[FuzzySortRatio] + 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 } } ) + + self:NetworkVar( "Float", 1, "HDRScale", { KeyName = "hdrscale", Edit = { type = "Float", category = "Main", min = 0, max = 1, order = 4 } } ) + + -- + -- Entity defaults + -- + if ( SERVER ) then + + self:SetTopColor( Vector( 0.2, 0.5, 1.0 ) ) + self:SetBottomColor( Vector( 0.8, 1.0, 1.0 ) ) + self:SetFadeBias( 1 ) + + + self:SetSunNormal( Vector( 0.4, 0.0, 0.01 ) ) + self:SetSunColor( Vector( 0.2, 0.1, 0.0 ) ) + self:SetSunSize( 2.0 ) + + self:SetDuskColor( Vector( 1.0, 0.2, 0.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 -fuzzel[FuzzyAutocompleteDistance] = function(str, ...) - return FuzzySort(str,fuzzel.dld,tru,...) end -fuzzel.fad = fuzzel[FuzzyAutocompleteDistance] -fuzzel[FuzzyAutocompleteRatio] = function(str,...) - return FuzzySort(str,fuzzel.dlr,tru,...) +function ENT:Initialize() end -fuzzel.far = fuzzel[FuzzyAutocompleteRatio] -return fuzzel -]] +function ENT:KeyValue( key, value ) -local str4 = [[ -local function safe_str (str) - str = string.gsub(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,"\'", "\\'") - str = string.gsub(str,"\"", "\\\"") - return str -end -]] + if ( self:SetNetworkKeyValue( key, value ) ) then + return + end + + -- TODO: sunposmethod + -- 0 : "Custom - Use the Sun Normal to position the sun" + -- 1 : "Automatic - Find a env_sun entity and use that" -local str6 = [[ -local function findsomenewcharacters(str) - for character in string.gmatch(str,".") do - if character == "\n\\t\n" then - print("I found a newline!") - end - end end -]] ---[[ -local function a(b) - b = string.gsub(b, "\\", "\\\\") - b = string.gsub(b, "\7", "\\\7") - b = string.gsub(b, "\8", "\\\8") - b = string.gsub(b, "\12", "\\\12") - b = string.gsub(b, "\10", "\10") - b = string.gsub(b, "\13", "\10") - b = string.gsub(b, "\9", "\\\9") - b = string.gsub(b, "\11", "\\\11") - b = string.gsub(b, "\'", "\\\'") - b = string.gsub(b, "\"", "\\\"") - - return b + +function ENT:Think() + + -- + -- Find an env_sun - if we don't already have one. + -- + if ( SERVER && self.EnvSun == nil ) then + + -- so this closure only gets called once - even if it fails + self.EnvSun = false + + local list = ents.FindByClass( "env_sun" ) + if ( #list > 0 ) then + self.EnvSun = list[1] + end + + end + + -- + -- If we have a sun - force our sun normal to its value + -- + if ( SERVER && IsValid( self.EnvSun ) ) then + + local vec = self.EnvSun:GetInternalVariable( "m_vDirection" ) + + if ( isvector( vec ) ) then + self:SetSunNormal( vec ) + end + + end + + -- + -- Become the active sky again if we're not already + -- + if ( CLIENT && g_SkyPaint != self ) then + + if ( !IsValid( g_SkyPaint ) ) then + g_SkyPaint = self + end + + end + end -]] -local f5 = io.open("../src/glum.lua", "r") -local str5 = f5:read("*a") -f5:close() +-- +-- To prevent server insanity - only let admins edit the sky. +-- +function ENT:CanEditVariables( ply ) + + return ply:IsAdmin() ---print("Glum:") ---print(glum) ---print(glum.minify) ---local f = glum.minify(str5) ---print(string.dump(loadstring(f))) +end + +]] ---print(glum.minify(str5)) -local min = glum.minify(str5) -local comp = glum.uglify(min) -print(comp) +local min = glum.minify(str) +--local comp = glum.uglify(min) +print(min) --[[ local fuzzel = loadstring(f)() |
