1 line
44 KiB
Lua
1 line
44 KiB
Lua
function a(U)for V,W in pairs(U)do U[W]=true end return U end function b(U)local V=0 for W in pairs(U)do V=V+1 end return V end function c(U,V,W)if U.Print then return U.Print()end V=V or 0 local X=(b(U)>1)local Y=string.rep(' ',V+1)local Z="{"..(X and'\n'or'')for ab,bb in pairs(U)do if type(bb)~='function'and not W(ab)then Z=Z..(X and Y or'')if type(ab)=='number'then elseif type(ab)=='string'and ab:match("^[A-Za-z_][A-Za-z0-9_]*$")then Z=Z..ab.." = "elseif type(ab)=='string'then Z=Z.."[\""..ab.."\"] = "else Z=Z.."["..tostring(ab).."] = "end if type(bb)=='string'then Z=Z.."\""..bb.."\""elseif type(bb)=='number'then Z=Z..bb elseif type(bb)=='table'then Z=Z..c(bb,V+(X and 1 or 0),W)else Z=Z..tostring(bb)end if next(U,ab)then Z=Z..","end if X then Z=Z..'\n'end end end Z=Z..(X and string.rep(' ',V)or'').."}"return Z end function d(U,V)V=V or function()return false end return c(U,0,V)end local k=a{' ','\n','\t','\r'}local l={['\r']='\\r',['\n']='\\n',['\t']='\\t',['"']='\\"',["'"]="\\'",['\\']='\\'}local m={['r']='\r',['n']='\n',['t']='\t',['"']='"',["'"]="'",['\\']='\\'}local n=a{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','_'}local o=a{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','_','0','1','2','3','4','5','6','7','8','9'}local p=a{'0','1','2','3','4','5','6','7','8','9'}local q=a{'0','1','2','3','4','5','6','7','8','9','A','a','B','b','C','c','D','d','E','e','F','f'}local r=a{'+','-','*','/','^','%',',','{','}','[',']','(',')',';','#','.',':'}local s=a{'~','=','>','<'}local t=a{'and','break','do','else','elseif','end','false','for','function','goto','if','in','local','nil','not','or','repeat','return','then','true','until','while',}local u=a{'else','elseif','until','end'}local v=a{'-','not','#'}local w=a{'+','-','*','/','%','^','#','..','.',':','>','<','<=','>=','~=','==','and','or'}local x=a{}local y={['+']={6,6};['-']={6,6};['*']={7,7};['/']={7,7};['%']={7,7};['^']={10,9};['..']={5,4};['==']={3,3};['~=']={3,3};['>']={3,3};['<']={3,3};['>=']={3,3};['<=']={3,3};['and']={2,2};['or']={1,1};}local z=8 function e(U)local V=1 local W=#U local X={}local function Y(hb)hb=V+(hb or 0)if hb<=W then return U:sub(hb,hb)else return''end end local function Z()if V<=W then local hb=U:sub(V,V)V=V+1 return hb else return''end end local ab=error local function bb(hb)local ib=1 local jb=1 local kb=1 while ib<=V do if U:sub(ib,ib)=='\n'then jb=jb+1 kb=1 else kb=kb+1 end ib=ib+1 end for lb,mb in pairs(X)do print(mb.Type.."<"..mb.Source..">")end ab("file<"..jb..":"..kb..">: "..hb)end local function cb(hb)while true do local ib=Z()if ib==''then bb("Unfinished long string.")elseif ib==']'then local jb=true for kb=1,hb do if Y()=='='then V=V+1 else jb=false break end end if jb and Z()==']'then return end end end end local function db()local hb=V while Y()=='='do V=V+1 end if Y()=='['then V=V+1 return V-hb-1 else V=hb return nil end end local eb=1 local fb=1 local function gb(hb)local ib={Type=hb;LeadingWhite=U:sub(eb,fb-1);Source=U:sub(fb,V-1);}table.insert(X,ib)eb=V fb=V return ib end while true do eb=V while true do local jb=Y()if jb==''then break elseif jb=='-'then if Y(1)=='-'then V=V+2 if Y()=='['then V=V+1 local kb=db()if kb then cb(kb)else while true do local lb=Z()if lb==''or lb=='\n'then break end end end else while true do local kb=Z()if kb==''or kb=='\n'then break end end end else break end elseif k[jb]then V=V+1 else break end end local hb=U:sub(eb,V-1)fb=V local ib=Z()if ib==''then gb('Eof')break elseif ib=='\''or ib=='\"'then while true do local jb=Z()if jb=='\\'then local kb=Z()local lb=m[kb]if not lb then bb("Invalid Escape Sequence `"..kb.."`.")end elseif jb==ib then break end end gb('String')elseif n[ib]then while o[Y()]do V=V+1 end if t[U:sub(fb,V-1)]then gb('Keyword')else gb('Ident')end elseif p[ib]or(ib=='.'and p[Y()])then if ib=='0'and Y()=='x'then V=V+1 while q[Y()]do V=V+1 end else while p[Y()]do V=V+1 end if Y()=='.'then V=V+1 while p[Y()]do V=V+1 end end if Y()=='e'or Y()=='E'then V=V+1 if Y()=='-'then V=V+1 end while p[Y()]do V=V+1 end end end gb('Number')elseif ib=='['then local jb=db()if jb then cb(jb)gb('String')else gb('Symbol')end elseif ib=='.'then if Y()=='.'then Z()if Y()=='.'then Z()end end gb('Symbol')elseif s[ib]then if Y()=='='then V=V+1 end gb('Symbol')elseif r[ib]then gb('Symbol')else bb("Bad symbol `"..ib.."` in source.")end end return X end function f(U)local V=e(U)local W=1 local function X()local Bb=V[W]if W<#V then W=W+1 end return Bb end local function Y(Bb)Bb=W+(Bb or 0)return V[Bb]or V[#V]end local function Z(Bb)local Cb=1 local Db=0 local Eb=1 while true do local Fb=V[Eb]local Gb if Fb==Bb then Gb=Fb.LeadingWhite else Gb=Fb.LeadingWhite..Fb.Source end for Hb=1,#Gb do local Ib=Gb:sub(Hb,Hb)if Ib=='\n'then Cb=Cb+1 Db=0 else Db=Db+1 end end if Fb==Bb then break end Eb=Eb+1 end return Cb..":"..(Db+1)end local function ab()local Bb=Y()return"<"..Bb.Type.." `"..Bb.Source.."`> at: "..Z(Bb)end local function bb()local Bb=Y()return Bb.Type=='Eof'or(Bb.Type=='Keyword'and u[Bb.Source])end local function cb()return v[Y().Source]or false end local function db()return w[Y().Source]or false end local function eb(Bb,Cb)local Db=Y()if Db.Type==Bb and(Cb==nil or Db.Source==Cb)then return X()else for Eb=-3,3 do print("Tokens["..Eb.."] = `"..Y(Eb).Source.."`")end if Cb then error(Z(Db)..": `"..Cb.."` expected.")else error(Z(Db)..": "..Bb.." expected.")end end end local function fb(Bb)local Cb=Bb.GetFirstToken local Db=Bb.GetLastToken function Bb:GetFirstToken()local Eb=Cb(self)assert(Eb)return Eb end function Bb:GetLastToken()local Eb=Db(self)assert(Eb)return Eb end return Bb end local gb local hb local function ib()local Bb={}local Cb={}table.insert(Bb,hb())while Y().Source==','do table.insert(Cb,X())table.insert(Bb,hb())end return Bb,Cb end local function jb()local Bb=Y()if Bb.Source=='('then local Cb=X()local Db=hb()local Eb=eb('Symbol',')')return fb{Type='ParenExpr';Expression=Db;Token_OpenParen=Cb;Token_CloseParen=Eb;GetFirstToken=function(Fb)return Fb.Token_OpenParen end;GetLastToken=function(Fb)return Fb.Token_CloseParen end;}elseif Bb.Type=='Ident'then return fb{Type='VariableExpr';Token=X();GetFirstToken=function(Cb)return Cb.Token end;GetLastToken=function(Cb)return Cb.Token end;}else print(ab())error(Z(Bb)..": Unexpected symbol")end end function g()local Bb=eb('Symbol','{')local Cb={}local Db={}while Y().Source~='}'do if Y().Source=='['then local Fb=X()local Gb=hb()local Hb=eb('Symbol',']')local Ib=eb('Symbol','=')local Jb=hb()table.insert(Cb,{EntryType='Index';Index=Gb;Value=Jb;Token_OpenBracket=Fb;Token_CloseBracket=Hb;Token_Equals=Ib;})elseif Y().Type=='Ident'and Y(1).Source=='='then local Fb=X()local Gb=X()local Hb=hb()table.insert(Cb,{EntryType='Field';Field=Fb;Value=Hb;Token_Equals=Gb;})else local Fb=hb()table.insert(Cb,{EntryType='Value';Value=Fb;})end if Y().Source==','or Y().Source==';'then table.insert(Db,X())else break end end local Eb=eb('Symbol','}')return fb{Type='TableLiteral';EntryList=Cb;Token_SeparatorList=Db;Token_OpenBrace=Bb;Token_CloseBrace=Eb;GetFirstToken=function(Fb)return Fb.Token_OpenBrace end;GetLastToken=function(Fb)return Fb.Token_CloseBrace end;}end local function kb()local Bb={}local Cb={}if Y().Type=='Ident'then table.insert(Bb,X())end while Y().Source==','do table.insert(Cb,X())local Db=eb('Ident')table.insert(Bb,Db)end return Bb,Cb end local function lb(Bb)local Cb=gb()local Db=Y()if Db.Type=='Keyword'and Db.Source==Bb then X()return Cb,Db else print(Db.Type,Db.Source)error(Z(Db)..": "..Bb.." expected.")end end local function mb(Bb)local Cb=X()local Db local Eb if not Bb then Db={}Eb={}table.insert(Db,eb('Ident'))while Y().Source=='.'do table.insert(Eb,X())table.insert(Db,eb('Ident'))end if Y().Source==':'then table.insert(Eb,X())table.insert(Db,eb('Ident'))end end local Fb=eb('Symbol','(')local Gb,Hb=kb()local Ib=eb('Symbol',')')local Jb,Kb=lb('end')return fb{Type=(Bb and'FunctionLiteral'or'FunctionStat');NameChain=Db;ArgList=Gb;Body=Jb;Token_Function=Cb;Token_NameChainSeparator=Eb;Token_OpenParen=Fb;Token_ArgCommaList=Hb;Token_CloseParen=Ib;Token_End=Kb;GetFirstToken=function(Lb)return Lb.Token_Function end;GetLastToken=function(Lb)return Lb.Token_End end;}end local function nb()local Bb=Y()if Bb.Source=='('then local Cb=X()local Db={}local Eb={}while Y().Source~=')'do table.insert(Db,hb())if Y().Source==','then table.insert(Eb,X())else break end end local Fb=eb('Symbol',')')return fb{CallType='ArgCall';ArgList=Db;Token_CommaList=Eb;Token_OpenParen=Cb;Token_CloseParen=Fb;GetFirstToken=function(Gb)return Gb.Token_OpenParen end;GetLastToken=function(Gb)return Gb.Token_CloseParen end;}elseif Bb.Source=='{'then return fb{CallType='TableCall';TableExpr=hb();GetFirstToken=function(Cb)return Cb.TableExpr:GetFirstToken()end;GetLastToken=function(Cb)return Cb.TableExpr:GetLastToken()end;}elseif Bb.Type=='String'then return fb{CallType='StringCall';Token=X();GetFirstToken=function(Cb)return Cb.Token end;GetLastToken=function(Cb)return Cb.Token end;}else error("Function arguments expected.")end end local function ob()local Bb=jb()assert(Bb,"nil prefixexpr")while true do local Cb=Y()if Cb.Source=='.'then local Db=X()local Eb=eb('Ident')Bb=fb{Type='FieldExpr';Base=Bb;Field=Eb;Token_Dot=Db;GetFirstToken=function(Fb)return Fb.Base:GetFirstToken()end;GetLastToken=function(Fb)return Fb.Field end;}elseif Cb.Source==':'then local Db=X()local Eb=eb('Ident')local Fb=nb()Bb=fb{Type='MethodExpr';Base=Bb;Method=Eb;FunctionArguments=Fb;Token_Colon=Db;GetFirstToken=function(Gb)return Gb.Base:GetFirstToken()end;GetLastToken=function(Gb)return Gb.FunctionArguments:GetLastToken()end;}elseif Cb.Source=='['then local Db=X()local Eb=hb()local Fb=eb('Symbol',']')Bb=fb{Type='IndexExpr';Base=Bb;Index=Eb;Token_OpenBracket=Db;Token_CloseBracket=Fb;GetFirstToken=function(Gb)return Gb.Base:GetFirstToken()end;GetLastToken=function(Gb)return Gb.Token_CloseBracket end;}elseif Cb.Source=='{'then Bb=fb{Type='CallExpr';Base=Bb;FunctionArguments=nb();GetFirstToken=function(Db)return Db.Base:GetFirstToken()end;GetLastToken=function(Db)return Db.FunctionArguments:GetLastToken()end;}elseif Cb.Source=='('then Bb=fb{Type='CallExpr';Base=Bb;FunctionArguments=nb();GetFirstToken=function(Db)return Db.Base:GetFirstToken()end;GetLastToken=function(Db)return Db.FunctionArguments:GetLastToken()end;}else return Bb end end end local function pb()local Bb=Y()if Bb.Type=='Number'then return fb{Type='NumberLiteral';Token=X();GetFirstToken=function(Cb)return Cb.Token end;GetLastToken=function(Cb)return Cb.Token end;}elseif Bb.Type=='String'then return fb{Type='StringLiteral';Token=X();GetFirstToken=function(Cb)return Cb.Token end;GetLastToken=function(Cb)return Cb.Token end;}elseif Bb.Source=='nil'then return fb{Type='NilLiteral';Token=X();GetFirstToken=function(Cb)return Cb.Token end;GetLastToken=function(Cb)return Cb.Token end;}elseif Bb.Source=='true'or Bb.Source=='false'then return fb{Type='BooleanLiteral';Token=X();GetFirstToken=function(Cb)return Cb.Token end;GetLastToken=function(Cb)return Cb.Token end;}elseif Bb.Source=='...'then return fb{Type='VargLiteral';Token=X();GetFirstToken=function(Cb)return Cb.Token end;GetLastToken=function(Cb)return Cb.Token end;}elseif Bb.Source=='{'then return g()elseif Bb.Source=='function'then return mb(true)else return ob()end end local function qb(Bb)local Cb if cb()then local Db=X()local Eb=qb(z)Cb=fb{Type='UnopExpr';Token_Op=Db;Rhs=Eb;GetFirstToken=function(Fb)return Fb.Token_Op end;GetLastToken=function(Fb)return Fb.Rhs:GetLastToken()end;}else Cb=pb()assert(Cb,"nil simpleexpr")end while db()and y[Y().Source][1]>Bb do local Db=X()local Eb=qb(y[Db.Source][2])assert(Eb,"RhsNeeded")Cb=fb{Type='BinopExpr';Lhs=Cb;Rhs=Eb;Token_Op=Db;GetFirstToken=function(Fb)return Fb.Lhs:GetFirstToken()end;GetLastToken=function(Fb)return Fb.Rhs:GetLastToken()end;}end return Cb end hb=function()return qb(0)end local function rb()local Bb=ob()if Bb.Type=='MethodExpr'or Bb.Type=='CallExpr'then return fb{Type='CallExprStat';Expression=Bb;GetFirstToken=function(Cb)return Cb.Expression:GetFirstToken()end;GetLastToken=function(Cb)return Cb.Expression:GetLastToken()end;}else local Cb={Bb}local Db={}while Y().Source==','do table.insert(Db,X())local Hb=ob()if Hb.Type=='MethodExpr'or Hb.Type=='CallExpr'then error("Bad left hand side of assignment")end table.insert(Cb,Hb)end local Eb=eb('Symbol','=')local Fb={hb()}local Gb={}while Y().Source==','do table.insert(Gb,X())table.insert(Fb,hb())end return fb{Type='AssignmentStat';Rhs=Fb;Lhs=Cb;Token_Equals=Eb;Token_LhsSeparatorList=Db;Token_RhsSeparatorList=Gb;GetFirstToken=function(Hb)return Hb.Lhs[1]:GetFirstToken()end;GetLastToken=function(Hb)return Hb.Rhs[#Hb.Rhs]:GetLastToken()end;}end end local function sb()local Bb=X()local Cb=hb()local Db=eb('Keyword','then')local Eb=gb()local Fb={}while Y().Source=='elseif'or Y().Source=='else'do local Hb=X()local Ib,Jb if Hb.Source=='elseif'then Ib=hb()Jb=eb('Keyword','then')end local Kb=gb()table.insert(Fb,{Condition=Ib;Body=Kb;ClauseType=Hb.Source;Token=Hb;Token_Then=Jb;})if Hb.Source=='else'then break end end local Gb=eb('Keyword','end')return fb{Type='IfStat';Condition=Cb;Body=Eb;ElseClauseList=Fb;Token_If=Bb;Token_Then=Db;Token_End=Gb;GetFirstToken=function(Hb)return Hb.Token_If end;GetLastToken=function(Hb)return Hb.Token_End end;}end local function tb()local Bb=X()local Cb,Db=lb('end')return fb{Type='DoStat';Body=Cb;Token_Do=Bb;Token_End=Db;GetFirstToken=function(Eb)return Eb.Token_Do end;GetLastToken=function(Eb)return Eb.Token_End end;}end local function ub()local Bb=X()local Cb=hb()local Db=eb('Keyword','do')local Eb,Fb=lb('end')return fb{Type='WhileStat';Condition=Cb;Body=Eb;Token_While=Bb;Token_Do=Db;Token_End=Fb;GetFirstToken=function(Gb)return Gb.Token_While end;GetLastToken=function(Gb)return Gb.Token_End end;}end local function vb()local Bb=X()local Cb,Db=kb()local Eb={}if Y().Source=='='then local Fb=X()local Gb,Hb=ib()if#Gb<2 or#Gb>3 then error("expected 2 or 3 values for range bounds")end local Ib=eb('Keyword','do')local Jb,Kb=lb('end')return fb{Type='NumericForStat';VarList=Cb;RangeList=Gb;Body=Jb;Token_For=Bb;Token_VarCommaList=Db;Token_Equals=Fb;Token_RangeCommaList=Hb;Token_Do=Ib;Token_End=Kb;GetFirstToken=function(Lb)return Lb.Token_For end;GetLastToken=function(Lb)return Lb.Token_End end;}elseif Y().Source=='in'then local Fb=X()local Gb,Hb=ib()local Ib=eb('Keyword','do')local Jb,Kb=lb('end')return fb{Type='GenericForStat';VarList=Cb;GeneratorList=Gb;Body=Jb;Token_For=Bb;Token_VarCommaList=Db;Token_In=Fb;Token_GeneratorCommaList=Hb;Token_Do=Ib;Token_End=Kb;GetFirstToken=function(Lb)return Lb.Token_For end;GetLastToken=function(Lb)return Lb.Token_End end;}else error("`=` or in expected")end end local function wb()local Bb=X()local Cb,Db=lb('until')local Eb=hb()return fb{Type='RepeatStat';Body=Cb;Condition=Eb;Token_Repeat=Bb;Token_Until=Db;GetFirstToken=function(Fb)return Fb.Token_Repeat end;GetLastToken=function(Fb)return Fb.Condition:GetLastToken()end;}end local function xb()local Bb=X()if Y().Source=='function'then local Cb=mb(false)if#Cb.NameChain>1 then error(Z(Cb.Token_NameChainSeparator[1])..": `(` expected.")end return fb{Type='LocalFunctionStat';FunctionStat=Cb;Token_Local=Bb;GetFirstToken=function(Db)return Db.Token_Local end;GetLastToken=function(Db)return Db.FunctionStat:GetLastToken()end;}elseif Y().Type=='Ident'then local Cb,Db=kb()local Eb,Fb={},{}local Gb if Y().Source=='='then Gb=X()Eb,Fb=ib()end return fb{Type='LocalVarStat';VarList=Cb;ExprList=Eb;Token_Local=Bb;Token_Equals=Gb;Token_VarCommaList=Db;Token_ExprCommaList=Fb;GetFirstToken=function(Hb)return Hb.Token_Local end;GetLastToken=function(Hb)if#Hb.ExprList>0 then return Hb.ExprList[#Hb.ExprList]:GetLastToken()else return Hb.VarList[#Hb.VarList]end end;}else error("`function` or ident expected")end end local function yb()local Bb=X()local Cb local Db if bb()or Y().Source==';'then Cb={}Db={}else Cb,Db=ib()end return{Type='ReturnStat';ExprList=Cb;Token_Return=Bb;Token_CommaList=Db;GetFirstToken=function(Eb)return Eb.Token_Return end;GetLastToken=function(Eb)if#Eb.ExprList>0 then return Eb.ExprList[#Eb.ExprList]:GetLastToken()else return Eb.Token_Return end end;}end local function zb()local Bb=X()return{Type='BreakStat';Token_Break=Bb;GetFirstToken=function(Cb)return Cb.Token_Break end;GetLastToken=function(Cb)return Cb.Token_Break end;}end local function Ab()local Bb=Y()if Bb.Source=='if'then return false,sb()elseif Bb.Source=='while'then return false,ub()elseif Bb.Source=='do'then return false,tb()elseif Bb.Source=='for'then return false,vb()elseif Bb.Source=='repeat'then return false,wb()elseif Bb.Source=='function'then return false,mb(false)elseif Bb.Source=='local'then return false,xb()elseif Bb.Source=='return'then return true,yb()elseif Bb.Source=='break'then return true,zb()else return false,rb()end end gb=function()local Bb={}local Cb={}local Db=false while not Db and not bb()do local Eb Db,Eb=Ab()table.insert(Bb,Eb)local Fb=Y()if Fb.Type=='Symbol'and Fb.Source==';'then Cb[#Bb]=X()end end return{Type='StatList';StatementList=Bb;SemicolonList=Cb;GetFirstToken=function(Eb)if#Eb.StatementList==0 then return nil else return Eb.StatementList[1]:GetFirstToken()end end;GetLastToken=function(Eb)if#Eb.StatementList==0 then return nil elseif Eb.SemicolonList[#Eb.StatementList]then return Eb.SemicolonList[#Eb.StatementList]else return Eb.StatementList[#Eb.StatementList]:GetLastToken()end end;}end return gb()end function h(U,V)local W=a{'BinopExpr';'UnopExpr';'NumberLiteral';'StringLiteral';'NilLiteral';'BooleanLiteral';'VargLiteral';'FieldExpr';'IndexExpr';'MethodExpr';'CallExpr';'FunctionLiteral';'VariableExpr';'ParenExpr';'TableLiteral';}local X=a{'StatList';'BreakStat';'ReturnStat';'LocalVarStat';'LocalFunctionStat';'FunctionStat';'RepeatStat';'GenericForStat';'NumericForStat';'WhileStat';'DoStat';'IfStat';'CallExprStat';'AssignmentStat';}for cb,db in pairs(V)do if not X[cb]and not W[cb]then error("Invalid visitor target: `"..cb.."`")end end local function Y(cb)local db=V[cb.Type]if type(db)=='function'then return db(cb)elseif db and db.Pre then return db.Pre(cb)end end local function Z(cb)local db=V[cb.Type]if db and type(db)=='table'and db.Post then return db.Post(cb)end end local ab,bb ab=function(cb)if Y(cb)then return end if cb.Type=='BinopExpr'then ab(cb.Lhs)ab(cb.Rhs)elseif cb.Type=='UnopExpr'then ab(cb.Rhs)elseif cb.Type=='NumberLiteral'or cb.Type=='StringLiteral'or cb.Type=='NilLiteral'or cb.Type=='BooleanLiteral'or cb.Type=='VargLiteral'then elseif cb.Type=='FieldExpr'then ab(cb.Base)elseif cb.Type=='IndexExpr'then ab(cb.Base)ab(cb.Index)elseif cb.Type=='MethodExpr'or cb.Type=='CallExpr'then ab(cb.Base)if cb.FunctionArguments.CallType=='ArgCall'then for db,eb in pairs(cb.FunctionArguments.ArgList)do ab(eb)end elseif cb.FunctionArguments.CallType=='TableCall'then ab(cb.FunctionArguments.TableExpr)end elseif cb.Type=='FunctionLiteral'then bb(cb.Body)elseif cb.Type=='VariableExpr'then elseif cb.Type=='ParenExpr'then ab(cb.Expression)elseif cb.Type=='TableLiteral'then for db,eb in pairs(cb.EntryList)do if eb.EntryType=='Field'then ab(eb.Value)elseif eb.EntryType=='Index'then ab(eb.Index)ab(eb.Value)elseif eb.EntryType=='Value'then ab(eb.Value)else assert(false,"unreachable")end end else assert(false,"unreachable, type: "..cb.Type..":"..d(cb))end Z(cb)end bb=function(cb)if Y(cb)then return end if cb.Type=='StatList'then for db,eb in pairs(cb.StatementList)do bb(eb)end elseif cb.Type=='BreakStat'then elseif cb.Type=='ReturnStat'then for db,eb in pairs(cb.ExprList)do ab(eb)end elseif cb.Type=='LocalVarStat'then if cb.Token_Equals then for db,eb in pairs(cb.ExprList)do ab(eb)end end elseif cb.Type=='LocalFunctionStat'then bb(cb.FunctionStat.Body)elseif cb.Type=='FunctionStat'then bb(cb.Body)elseif cb.Type=='RepeatStat'then bb(cb.Body)ab(cb.Condition)elseif cb.Type=='GenericForStat'then for db,eb in pairs(cb.GeneratorList)do ab(eb)end bb(cb.Body)elseif cb.Type=='NumericForStat'then for db,eb in pairs(cb.RangeList)do ab(eb)end bb(cb.Body)elseif cb.Type=='WhileStat'then ab(cb.Condition)bb(cb.Body)elseif cb.Type=='DoStat'then bb(cb.Body)elseif cb.Type=='IfStat'then ab(cb.Condition)bb(cb.Body)for db,eb in pairs(cb.ElseClauseList)do if eb.Condition then ab(eb.Condition)end bb(eb.Body)end elseif cb.Type=='CallExprStat'then ab(cb.Expression)elseif cb.Type=='AssignmentStat'then for db,eb in pairs(cb.Lhs)do ab(eb)end for db,eb in pairs(cb.Rhs)do ab(eb)end else assert(false,"unreachable")end Z(cb)end if X[U.Type]then bb(U)else ab(U)end end function i(U)local V={}local W=nil local X=0 local function Y()X=X+1 return X end local function Z()W={ParentScope=W;ChildScopeList={};VariableList={};BeginLocation=Y();}if W.ParentScope then W.Depth=W.ParentScope.Depth+1 table.insert(W.ParentScope.ChildScopeList,W)else W.Depth=1 end function W:GetVar(hb)for ib,jb in pairs(self.VariableList)do if jb.Name==hb then return jb end end if self.ParentScope then return self.ParentScope:GetVar(hb)else for ib,jb in pairs(V)do if jb.Name==hb then return jb end end end end end local function ab()local hb=W hb.EndLocation=Y()for ib,jb in pairs(hb.VariableList)do jb.ScopeEndLocation=hb.EndLocation end W=hb.ParentScope return hb end Z()local function bb(hb,ib,jb)assert(jb,"Misisng localInfo")assert(hb,"Missing local var name")local kb={Type='Local';Name=hb;RenameList={ib};AssignedTo=false;Info=jb;UseCount=0;Scope=W;BeginLocation=Y();EndLocation=Y();ReferenceLocationList={Y()};}function kb:Rename(lb)self.Name=lb for mb,nb in pairs(self.RenameList)do nb(lb)end end function kb:Reference()self.UseCount=self.UseCount+1 end table.insert(W.VariableList,kb)return kb end local function cb(hb)for jb,kb in pairs(V)do if kb.Name==hb then return kb end end local ib={Type='Global';Name=hb;RenameList={};AssignedTo=false;UseCount=0;Scope=nil;BeginLocation=Y();EndLocation=Y();ReferenceLocationList={};}function ib:Rename(jb)self.Name=jb for kb,lb in pairs(self.RenameList)do lb(jb)end end function ib:Reference()self.UseCount=self.UseCount+1 end table.insert(V,ib)return ib end local function db(hb,ib)assert(hb,"Missing var name")local jb=cb(hb)table.insert(jb.RenameList,ib)return jb end local function eb(hb,ib)for jb=#hb.VariableList,1,-1 do if hb.VariableList[jb].Name==ib then return hb.VariableList[jb]end end if hb.ParentScope then local jb=eb(hb.ParentScope,ib)if jb then return jb end end return nil end local function fb(hb,ib)assert(hb,"Missing var name")local jb=eb(W,hb)if jb then table.insert(jb.RenameList,ib)else jb=db(hb,ib)end local kb=Y()jb.EndLocation=kb table.insert(jb.ReferenceLocationList,jb.EndLocation)return jb end local gb={}gb.FunctionLiteral={Pre=function(hb)Z()for ib,jb in pairs(hb.ArgList)do local kb=bb(jb.Source,function(lb)jb.Source=lb end,{Type='Argument';Index=ib;})end end;Post=function(hb)ab()end;}gb.VariableExpr=function(hb)hb.Variable=fb(hb.Token.Source,function(ib)hb.Token.Source=ib end)end gb.StatList={Pre=function(hb)Z()end;Post=function(hb)ab()end;}gb.LocalVarStat={Post=function(hb)for ib,jb in pairs(hb.VarList)do bb(jb.Source,function(kb)hb.VarList[ib].Source=kb end,{Type='Local';})end end;}gb.LocalFunctionStat={Pre=function(hb)bb(hb.FunctionStat.NameChain[1].Source,function(ib)hb.FunctionStat.NameChain[1].Source=ib end,{Type='LocalFunction';})Z()for ib,jb in pairs(hb.FunctionStat.ArgList)do bb(jb.Source,function(kb)jb.Source=kb end,{Type='Argument';Index=ib;})end end;Post=function()ab()end;}gb.FunctionStat={Pre=function(hb)local ib=hb.NameChain local jb if#ib==1 then jb=db(ib[1].Source,function(kb)ib[1].Source=kb end)else jb=fb(ib[1].Source,function(kb)ib[1].Source=kb end)end jb.AssignedTo=true Z()for kb,lb in pairs(hb.ArgList)do bb(lb.Source,function(mb)lb.Source=mb end,{Type='Argument';Index=kb;})end end;Post=function()ab()end;}gb.GenericForStat={Pre=function(hb)for ib,jb in pairs(hb.GeneratorList)do h(jb,gb)end Z()for ib,jb in pairs(hb.VarList)do bb(jb.Source,function(kb)jb.Source=kb end,{Type='ForRange';Index=ib;})end h(hb.Body,gb)ab()return true end;}gb.NumericForStat={Pre=function(hb)for ib,jb in pairs(hb.RangeList)do h(jb,gb)end Z()for ib,jb in pairs(hb.VarList)do bb(jb.Source,function(kb)jb.Source=kb end,{Type='ForRange';Index=ib;})end h(hb.Body,gb)ab()return true end;}gb.AssignmentStat={Post=function(hb)for ib,jb in pairs(hb.Lhs)do if jb.Variable then jb.Variable.AssignedTo=true end end end;}h(U,gb)return V,ab()end function j(U)local V,W local function X(Y)if not Y.LeadingWhite or not Y.Source then error("Bad token: "..d(Y))end io.write(Y.LeadingWhite)io.write(Y.Source)end W=function(Y)if Y.Type=='BinopExpr'then W(Y.Lhs)X(Y.Token_Op)W(Y.Rhs)elseif Y.Type=='UnopExpr'then X(Y.Token_Op)W(Y.Rhs)elseif Y.Type=='NumberLiteral'or Y.Type=='StringLiteral'or Y.Type=='NilLiteral'or Y.Type=='BooleanLiteral'or Y.Type=='VargLiteral'then X(Y.Token)elseif Y.Type=='FieldExpr'then W(Y.Base)X(Y.Token_Dot)X(Y.Field)elseif Y.Type=='IndexExpr'then W(Y.Base)X(Y.Token_OpenBracket)W(Y.Index)X(Y.Token_CloseBracket)elseif Y.Type=='MethodExpr'or Y.Type=='CallExpr'then W(Y.Base)if Y.Type=='MethodExpr'then X(Y.Token_Colon)X(Y.Method)end if Y.FunctionArguments.CallType=='StringCall'then X(Y.FunctionArguments.Token)elseif Y.FunctionArguments.CallType=='ArgCall'then X(Y.FunctionArguments.Token_OpenParen)for Z,ab in pairs(Y.FunctionArguments.ArgList)do W(ab)local bb=Y.FunctionArguments.Token_CommaList[Z]if bb then X(bb)end end X(Y.FunctionArguments.Token_CloseParen)elseif Y.FunctionArguments.CallType=='TableCall'then W(Y.FunctionArguments.TableExpr)end elseif Y.Type=='FunctionLiteral'then X(Y.Token_Function)X(Y.Token_OpenParen)for Z,ab in pairs(Y.ArgList)do X(ab)local bb=Y.Token_ArgCommaList[Z]if bb then X(bb)end end X(Y.Token_CloseParen)V(Y.Body)X(Y.Token_End)elseif Y.Type=='VariableExpr'then X(Y.Token)elseif Y.Type=='ParenExpr'then X(Y.Token_OpenParen)W(Y.Expression)X(Y.Token_CloseParen)elseif Y.Type=='TableLiteral'then X(Y.Token_OpenBrace)for Z,ab in pairs(Y.EntryList)do if ab.EntryType=='Field'then X(ab.Field)X(ab.Token_Equals)W(ab.Value)elseif ab.EntryType=='Index'then X(ab.Token_OpenBracket)W(ab.Index)X(ab.Token_CloseBracket)X(ab.Token_Equals)W(ab.Value)elseif ab.EntryType=='Value'then W(ab.Value)else assert(false,"unreachable")end local bb=Y.Token_SeparatorList[Z]if bb then X(bb)end end X(Y.Token_CloseBrace)else assert(false,"unreachable, type: "..Y.Type..":"..d(Y))end end V=function(Y)if Y.Type=='StatList'then for Z,ab in pairs(Y.StatementList)do V(ab)if Y.SemicolonList[Z]then X(Y.SemicolonList[Z])end end elseif Y.Type=='BreakStat'then X(Y.Token_Break)elseif Y.Type=='ReturnStat'then X(Y.Token_Return)for Z,ab in pairs(Y.ExprList)do W(ab)if Y.Token_CommaList[Z]then X(Y.Token_CommaList[Z])end end elseif Y.Type=='LocalVarStat'then X(Y.Token_Local)for Z,ab in pairs(Y.VarList)do X(ab)local bb=Y.Token_VarCommaList[Z]if bb then X(bb)end end if Y.Token_Equals then X(Y.Token_Equals)for Z,ab in pairs(Y.ExprList)do W(ab)local bb=Y.Token_ExprCommaList[Z]if bb then X(bb)end end end elseif Y.Type=='LocalFunctionStat'then X(Y.Token_Local)X(Y.FunctionStat.Token_Function)X(Y.FunctionStat.NameChain[1])X(Y.FunctionStat.Token_OpenParen)for Z,ab in pairs(Y.FunctionStat.ArgList)do X(ab)local bb=Y.FunctionStat.Token_ArgCommaList[Z]if bb then X(bb)end end X(Y.FunctionStat.Token_CloseParen)V(Y.FunctionStat.Body)X(Y.FunctionStat.Token_End)elseif Y.Type=='FunctionStat'then X(Y.Token_Function)for Z,ab in pairs(Y.NameChain)do X(ab)local bb=Y.Token_NameChainSeparator[Z]if bb then X(bb)end end X(Y.Token_OpenParen)for Z,ab in pairs(Y.ArgList)do X(ab)local bb=Y.Token_ArgCommaList[Z]if bb then X(bb)end end X(Y.Token_CloseParen)V(Y.Body)X(Y.Token_End)elseif Y.Type=='RepeatStat'then X(Y.Token_Repeat)V(Y.Body)X(Y.Token_Until)W(Y.Condition)elseif Y.Type=='GenericForStat'then X(Y.Token_For)for Z,ab in pairs(Y.VarList)do X(ab)local bb=Y.Token_VarCommaList[Z]if bb then X(bb)end end X(Y.Token_In)for Z,ab in pairs(Y.GeneratorList)do W(ab)local bb=Y.Token_GeneratorCommaList[Z]if bb then X(bb)end end X(Y.Token_Do)V(Y.Body)X(Y.Token_End)elseif Y.Type=='NumericForStat'then X(Y.Token_For)for Z,ab in pairs(Y.VarList)do X(ab)local bb=Y.Token_VarCommaList[Z]if bb then X(bb)end end X(Y.Token_Equals)for Z,ab in pairs(Y.RangeList)do W(ab)local bb=Y.Token_RangeCommaList[Z]if bb then X(bb)end end X(Y.Token_Do)V(Y.Body)X(Y.Token_End)elseif Y.Type=='WhileStat'then X(Y.Token_While)W(Y.Condition)X(Y.Token_Do)V(Y.Body)X(Y.Token_End)elseif Y.Type=='DoStat'then X(Y.Token_Do)V(Y.Body)X(Y.Token_End)elseif Y.Type=='IfStat'then X(Y.Token_If)W(Y.Condition)X(Y.Token_Then)V(Y.Body)for Z,ab in pairs(Y.ElseClauseList)do X(ab.Token)if ab.Condition then W(ab.Condition)X(ab.Token_Then)end V(ab.Body)end X(Y.Token_End)elseif Y.Type=='CallExprStat'then W(Y.Expression)elseif Y.Type=='AssignmentStat'then for Z,ab in pairs(Y.Lhs)do W(ab)local bb=Y.Token_LhsSeparatorList[Z]if bb then X(bb)end end X(Y.Token_Equals)for Z,ab in pairs(Y.Rhs)do W(ab)local bb=Y.Token_RhsSeparatorList[Z]if bb then X(bb)end end else assert(false,"unreachable")end end V(U)end local function A(U)local V,W local X=0 local function Y(fb)local gb='\n'..('\t'):rep(X)if fb.LeadingWhite==''or(fb.LeadingWhite:sub(-#gb,-1)~=gb)then fb.LeadingWhite=fb.LeadingWhite:gsub("\n?[\t ]*$","")fb.LeadingWhite=fb.LeadingWhite..gb end end local function Z()X=X+1 end local function ab()X=X-1 assert(X>=0,"Undented too far")end local function bb(fb)if#fb.LeadingWhite>0 then return fb.LeadingWhite:sub(1,1)else return fb.Source:sub(1,1)end end local function cb(fb)if not k[bb(fb)]then fb.LeadingWhite=' '..fb.LeadingWhite end end local function db(fb)cb(fb:GetFirstToken())end local function eb(fb,gb,hb)Z()V(gb)ab()Y(hb)end W=function(fb)if fb.Type=='BinopExpr'then W(fb.Lhs)W(fb.Rhs)if fb.Token_Op.Source=='..'then else db(fb.Rhs)cb(fb.Token_Op)end elseif fb.Type=='UnopExpr'then W(fb.Rhs)elseif fb.Type=='NumberLiteral'or fb.Type=='StringLiteral'or fb.Type=='NilLiteral'or fb.Type=='BooleanLiteral'or fb.Type=='VargLiteral'then elseif fb.Type=='FieldExpr'then W(fb.Base)elseif fb.Type=='IndexExpr'then W(fb.Base)W(fb.Index)elseif fb.Type=='MethodExpr'or fb.Type=='CallExpr'then W(fb.Base)if fb.Type=='MethodExpr'then end if fb.FunctionArguments.CallType=='StringCall'then elseif fb.FunctionArguments.CallType=='ArgCall'then for gb,hb in pairs(fb.FunctionArguments.ArgList)do W(hb)if gb>1 then db(hb)end local ib=fb.FunctionArguments.Token_CommaList[gb]if ib then end end elseif fb.FunctionArguments.CallType=='TableCall'then W(fb.FunctionArguments.TableExpr)end elseif fb.Type=='FunctionLiteral'then for gb,hb in pairs(fb.ArgList)do if gb>1 then cb(hb)end local ib=fb.Token_ArgCommaList[gb]if ib then end end eb(fb.Token_CloseParen,fb.Body,fb.Token_End)elseif fb.Type=='VariableExpr'then elseif fb.Type=='ParenExpr'then W(fb.Expression)elseif fb.Type=='TableLiteral'then if#fb.EntryList==0 then else Z()for gb,hb in pairs(fb.EntryList)do if hb.EntryType=='Field'then Y(hb.Field)cb(hb.Token_Equals)W(hb.Value)db(hb.Value)elseif hb.EntryType=='Index'then Y(hb.Token_OpenBracket)W(hb.Index)cb(hb.Token_Equals)W(hb.Value)db(hb.Value)elseif hb.EntryType=='Value'then W(hb.Value)Y(hb.Value:GetFirstToken())else assert(false,"unreachable")end local ib=fb.Token_SeparatorList[gb]if ib then end end ab()Y(fb.Token_CloseBrace)end else assert(false,"unreachable, type: "..fb.Type..":"..d(fb))end end V=function(fb)if fb.Type=='StatList'then for gb,hb in pairs(fb.StatementList)do V(hb)Y(hb:GetFirstToken())end elseif fb.Type=='BreakStat'then elseif fb.Type=='ReturnStat'then for gb,hb in pairs(fb.ExprList)do W(hb)db(hb)if fb.Token_CommaList[gb]then end end elseif fb.Type=='LocalVarStat'then for gb,hb in pairs(fb.VarList)do cb(hb)local ib=fb.Token_VarCommaList[gb]if ib then end end if fb.Token_Equals then cb(fb.Token_Equals)for gb,hb in pairs(fb.ExprList)do W(hb)db(hb)local ib=fb.Token_ExprCommaList[gb]if ib then end end end elseif fb.Type=='LocalFunctionStat'then cb(fb.FunctionStat.Token_Function)cb(fb.FunctionStat.NameChain[1])for gb,hb in pairs(fb.FunctionStat.ArgList)do if gb>1 then cb(hb)end local ib=fb.FunctionStat.Token_ArgCommaList[gb]if ib then end end eb(fb.FunctionStat.Token_CloseParen,fb.FunctionStat.Body,fb.FunctionStat.Token_End)elseif fb.Type=='FunctionStat'then for gb,hb in pairs(fb.NameChain)do if gb==1 then cb(hb)end local ib=fb.Token_NameChainSeparator[gb]if ib then end end for gb,hb in pairs(fb.ArgList)do if gb>1 then cb(hb)end local ib=fb.Token_ArgCommaList[gb]if ib then end end eb(fb.Token_CloseParen,fb.Body,fb.Token_End)elseif fb.Type=='RepeatStat'then eb(fb.Token_Repeat,fb.Body,fb.Token_Until)W(fb.Condition)db(fb.Condition)elseif fb.Type=='GenericForStat'then for gb,hb in pairs(fb.VarList)do cb(hb)local ib=fb.Token_VarCommaList[gb]if ib then end end cb(fb.Token_In)for gb,hb in pairs(fb.GeneratorList)do W(hb)db(hb)local ib=fb.Token_GeneratorCommaList[gb]if ib then end end cb(fb.Token_Do)eb(fb.Token_Do,fb.Body,fb.Token_End)elseif fb.Type=='NumericForStat'then for gb,hb in pairs(fb.VarList)do cb(hb)local ib=fb.Token_VarCommaList[gb]if ib then end end cb(fb.Token_Equals)for gb,hb in pairs(fb.RangeList)do W(hb)db(hb)local ib=fb.Token_RangeCommaList[gb]if ib then end end cb(fb.Token_Do)eb(fb.Token_Do,fb.Body,fb.Token_End)elseif fb.Type=='WhileStat'then W(fb.Condition)db(fb.Condition)cb(fb.Token_Do)eb(fb.Token_Do,fb.Body,fb.Token_End)elseif fb.Type=='DoStat'then eb(fb.Token_Do,fb.Body,fb.Token_End)elseif fb.Type=='IfStat'then W(fb.Condition)db(fb.Condition)cb(fb.Token_Then)local gb=fb.Token_Then local hb=fb.Body for ib,jb in pairs(fb.ElseClauseList)do eb(gb,hb,jb.Token)gb=jb.Token if jb.Condition then W(jb.Condition)db(jb.Condition)cb(jb.Token_Then)gb=jb.Token_Then end hb=jb.Body end eb(gb,hb,fb.Token_End)elseif fb.Type=='CallExprStat'then W(fb.Expression)elseif fb.Type=='AssignmentStat'then for gb,hb in pairs(fb.Lhs)do W(hb)if gb>1 then db(hb)end local ib=fb.Token_LhsSeparatorList[gb]if ib then end end cb(fb.Token_Equals)for gb,hb in pairs(fb.Rhs)do W(hb)db(hb)local ib=fb.Token_RhsSeparatorList[gb]if ib then end end else assert(false,"unreachable")end end V(U)end local function B(U)local V,W local function X(ab)ab.LeadingWhite=''end local function Y(ab,bb)X(bb)local cb=ab.Source:sub(-1,-1)local db=bb.Source:sub(1,1)if(cb=='-'and db=='-')or(o[cb]and o[db])then bb.LeadingWhite=' 'else bb.LeadingWhite=''end end local function Z(ab,bb,cb)V(bb)X(cb)local db=bb:GetFirstToken()local eb=bb:GetLastToken()if db then Y(ab,db)Y(eb,cb)else Y(ab,cb)end end W=function(ab)if ab.Type=='BinopExpr'then W(ab.Lhs)X(ab.Token_Op)W(ab.Rhs)Y(ab.Token_Op,ab.Rhs:GetFirstToken())Y(ab.Lhs:GetLastToken(),ab.Token_Op)elseif ab.Type=='UnopExpr'then X(ab.Token_Op)W(ab.Rhs)Y(ab.Token_Op,ab.Rhs:GetFirstToken())elseif ab.Type=='NumberLiteral'or ab.Type=='StringLiteral'or ab.Type=='NilLiteral'or ab.Type=='BooleanLiteral'or ab.Type=='VargLiteral'then X(ab.Token)elseif ab.Type=='FieldExpr'then W(ab.Base)X(ab.Token_Dot)X(ab.Field)elseif ab.Type=='IndexExpr'then W(ab.Base)X(ab.Token_OpenBracket)W(ab.Index)X(ab.Token_CloseBracket)elseif ab.Type=='MethodExpr'or ab.Type=='CallExpr'then W(ab.Base)if ab.Type=='MethodExpr'then X(ab.Token_Colon)X(ab.Method)end if ab.FunctionArguments.CallType=='StringCall'then X(ab.FunctionArguments.Token)elseif ab.FunctionArguments.CallType=='ArgCall'then X(ab.FunctionArguments.Token_OpenParen)for bb,cb in pairs(ab.FunctionArguments.ArgList)do W(cb)local db=ab.FunctionArguments.Token_CommaList[bb]if db then X(db)end end X(ab.FunctionArguments.Token_CloseParen)elseif ab.FunctionArguments.CallType=='TableCall'then W(ab.FunctionArguments.TableExpr)end elseif ab.Type=='FunctionLiteral'then X(ab.Token_Function)X(ab.Token_OpenParen)for bb,cb in pairs(ab.ArgList)do X(cb)local db=ab.Token_ArgCommaList[bb]if db then X(db)end end X(ab.Token_CloseParen)Z(ab.Token_CloseParen,ab.Body,ab.Token_End)elseif ab.Type=='VariableExpr'then X(ab.Token)elseif ab.Type=='ParenExpr'then X(ab.Token_OpenParen)W(ab.Expression)X(ab.Token_CloseParen)elseif ab.Type=='TableLiteral'then X(ab.Token_OpenBrace)for bb,cb in pairs(ab.EntryList)do if cb.EntryType=='Field'then X(cb.Field)X(cb.Token_Equals)W(cb.Value)elseif cb.EntryType=='Index'then X(cb.Token_OpenBracket)W(cb.Index)X(cb.Token_CloseBracket)X(cb.Token_Equals)W(cb.Value)elseif cb.EntryType=='Value'then W(cb.Value)else assert(false,"unreachable")end local db=ab.Token_SeparatorList[bb]if db then X(db)end end X(ab.Token_CloseBrace)else assert(false,"unreachable, type: "..ab.Type..":"..d(ab))end end V=function(ab)if ab.Type=='StatList'then for bb=1,#ab.StatementList do local cb=ab.StatementList[bb]V(cb)X(cb:GetFirstToken())local db=ab.StatementList[bb-1]if db then if ab.SemicolonList[bb-1]and(db:GetLastToken().Source~=')'or cb:GetFirstToken().Source~=')')then ab.SemicolonList[bb-1]=nil end if not ab.SemicolonList[bb-1]then Y(db:GetLastToken(),cb:GetFirstToken())end end end ab.SemicolonList[#ab.StatementList]=nil if#ab.StatementList>0 then X(ab.StatementList[1]:GetFirstToken())end elseif ab.Type=='BreakStat'then X(ab.Token_Break)elseif ab.Type=='ReturnStat'then X(ab.Token_Return)for bb,cb in pairs(ab.ExprList)do W(cb)if ab.Token_CommaList[bb]then X(ab.Token_CommaList[bb])end end if#ab.ExprList>0 then Y(ab.Token_Return,ab.ExprList[1]:GetFirstToken())end elseif ab.Type=='LocalVarStat'then X(ab.Token_Local)for bb,cb in pairs(ab.VarList)do if bb==1 then Y(ab.Token_Local,cb)else X(cb)end local db=ab.Token_VarCommaList[bb]if db then X(db)end end if ab.Token_Equals then X(ab.Token_Equals)for bb,cb in pairs(ab.ExprList)do W(cb)local db=ab.Token_ExprCommaList[bb]if db then X(db)end end end elseif ab.Type=='LocalFunctionStat'then X(ab.Token_Local)Y(ab.Token_Local,ab.FunctionStat.Token_Function)Y(ab.FunctionStat.Token_Function,ab.FunctionStat.NameChain[1])Y(ab.FunctionStat.NameChain[1],ab.FunctionStat.Token_OpenParen)for bb,cb in pairs(ab.FunctionStat.ArgList)do X(cb)local db=ab.FunctionStat.Token_ArgCommaList[bb]if db then X(db)end end X(ab.FunctionStat.Token_CloseParen)Z(ab.FunctionStat.Token_CloseParen,ab.FunctionStat.Body,ab.FunctionStat.Token_End)elseif ab.Type=='FunctionStat'then X(ab.Token_Function)for bb,cb in pairs(ab.NameChain)do if bb==1 then Y(ab.Token_Function,cb)else X(cb)end local db=ab.Token_NameChainSeparator[bb]if db then X(db)end end X(ab.Token_OpenParen)for bb,cb in pairs(ab.ArgList)do X(cb)local db=ab.Token_ArgCommaList[bb]if db then X(db)end end X(ab.Token_CloseParen)Z(ab.Token_CloseParen,ab.Body,ab.Token_End)elseif ab.Type=='RepeatStat'then X(ab.Token_Repeat)Z(ab.Token_Repeat,ab.Body,ab.Token_Until)W(ab.Condition)Y(ab.Token_Until,ab.Condition:GetFirstToken())elseif ab.Type=='GenericForStat'then X(ab.Token_For)for bb,cb in pairs(ab.VarList)do if bb==1 then Y(ab.Token_For,cb)else X(cb)end local db=ab.Token_VarCommaList[bb]if db then X(db)end end Y(ab.VarList[#ab.VarList],ab.Token_In)for bb,cb in pairs(ab.GeneratorList)do W(cb)if bb==1 then Y(ab.Token_In,cb:GetFirstToken())end local db=ab.Token_GeneratorCommaList[bb]if db then X(db)end end Y(ab.GeneratorList[#ab.GeneratorList]:GetLastToken(),ab.Token_Do)Z(ab.Token_Do,ab.Body,ab.Token_End)elseif ab.Type=='NumericForStat'then X(ab.Token_For)for bb,cb in pairs(ab.VarList)do if bb==1 then Y(ab.Token_For,cb)else X(cb)end local db=ab.Token_VarCommaList[bb]if db then X(db)end end Y(ab.VarList[#ab.VarList],ab.Token_Equals)for bb,cb in pairs(ab.RangeList)do W(cb)if bb==1 then Y(ab.Token_Equals,cb:GetFirstToken())end local db=ab.Token_RangeCommaList[bb]if db then X(db)end end Y(ab.RangeList[#ab.RangeList]:GetLastToken(),ab.Token_Do)Z(ab.Token_Do,ab.Body,ab.Token_End)elseif ab.Type=='WhileStat'then X(ab.Token_While)W(ab.Condition)X(ab.Token_Do)Y(ab.Token_While,ab.Condition:GetFirstToken())Y(ab.Condition:GetLastToken(),ab.Token_Do)Z(ab.Token_Do,ab.Body,ab.Token_End)elseif ab.Type=='DoStat'then X(ab.Token_Do)X(ab.Token_End)Z(ab.Token_Do,ab.Body,ab.Token_End)elseif ab.Type=='IfStat'then X(ab.Token_If)W(ab.Condition)Y(ab.Token_If,ab.Condition:GetFirstToken())Y(ab.Condition:GetLastToken(),ab.Token_Then)local bb=ab.Token_Then local cb=ab.Body for db,eb in pairs(ab.ElseClauseList)do Z(bb,cb,eb.Token)bb=eb.Token if eb.Condition then W(eb.Condition)Y(eb.Token,eb.Condition:GetFirstToken())Y(eb.Condition:GetLastToken(),eb.Token_Then)bb=eb.Token_Then end V(eb.Body)cb=eb.Body end Z(bb,cb,ab.Token_End)elseif ab.Type=='CallExprStat'then W(ab.Expression)elseif ab.Type=='AssignmentStat'then for bb,cb in pairs(ab.Lhs)do W(cb)local db=ab.Token_LhsSeparatorList[bb]if db then X(db)end end X(ab.Token_Equals)for bb,cb in pairs(ab.Rhs)do W(cb)local db=ab.Token_RhsSeparatorList[bb]if db then X(db)end end else assert(false,"unreachable")end end V(U)end local C=0 local D={}for U=('a'):byte(),('z'):byte()do table.insert(D,string.char(U))end for U=('A'):byte(),('Z'):byte()do table.insert(D,string.char(U))end for U=('0'):byte(),('9'):byte()do table.insert(D,string.char(U))end table.insert(D,'_')local E={}for U=('a'):byte(),('z'):byte()do table.insert(E,string.char(U))end for U=('A'):byte(),('Z'):byte()do table.insert(E,string.char(U))end local function F(U)local V=''local W=U%#E U=(U-W)/#E V=V..E[W+1]while U>0 do local X=U%#D U=(U-X)/#D V=V..D[X+1]end return V end local function G()local U=C C=C+1 return F(U)end local function H()local U=''repeat U=G()until not t[U]return U end local function I(U,V)local W={}local X=0 for bb,cb in pairs(U)do if cb.AssignedTo then cb:Rename('_TMP_'..X..'_')X=X+1 else W[cb.Name]=true end end local function Y(bb)for cb,db in pairs(bb.VariableList)do db:Rename('_TMP_'..X..'_')X=X+1 end for cb,db in pairs(bb.ChildScopeList)do Y(db)end end local Z=0 for bb,cb in pairs(U)do if cb.AssignedTo then local db=''repeat db=F(Z)Z=Z+1 until not t[db]and not W[db]cb:Rename(db)end end V.FirstFreeName=Z local function ab(bb)for cb,db in pairs(bb.VariableList)do local eb=''repeat eb=F(bb.FirstFreeName)bb.FirstFreeName=bb.FirstFreeName+1 until not t[eb]and not W[eb]db:Rename(eb)end for cb,db in pairs(bb.ChildScopeList)do db.FirstFreeName=bb.FirstFreeName ab(db)end end ab(V)end local function J(U,V)local W={}for cb,db in pairs(t)do W[cb]=true end local X={}local Y={}do for db,eb in pairs(U)do if eb.AssignedTo then table.insert(X,eb)else W[eb.Name]=true end end local function cb(db)for eb,fb in pairs(db.VariableList)do table.insert(X,fb)table.insert(Y,fb)end for eb,fb in pairs(db.ChildScopeList)do cb(fb)end end cb(V)end for cb,db in pairs(X)do db.UsedNameArray={}end table.sort(X,function(cb,db)return#cb.RenameList<#db.RenameList end)local Z=0 local ab={}local function bb(cb)local db=ab[cb]if not db then repeat db=F(Z)Z=Z+1 until not W[db]ab[cb]=db end return db end for cb,db in pairs(X)do db.Renamed=true local eb=1 while db.UsedNameArray[eb]do eb=eb+1 end db:Rename(bb(eb))if db.Scope then for fb,gb in pairs(X)do if not gb.Renamed then if not gb.Scope or gb.Scope.Depth<db.Scope.Depth then for hb,ib in pairs(gb.ReferenceLocationList)do if ib>=db.BeginLocation and ib<=db.ScopeEndLocation then gb.UsedNameArray[eb]=true break end end elseif gb.Scope.Depth>db.Scope.Depth then for hb,ib in pairs(db.ReferenceLocationList)do if ib>=gb.BeginLocation and ib<=gb.ScopeEndLocation then gb.UsedNameArray[eb]=true break end end else if db.BeginLocation<gb.EndLocation and db.EndLocation>gb.BeginLocation then gb.UsedNameArray[eb]=true end end end end else for fb,gb in pairs(X)do if not gb.Renamed then if gb.Type=='Global'then gb.UsedNameArray[eb]=true elseif gb.Type=='Local'then for hb,ib in pairs(db.ReferenceLocationList)do if ib>=gb.BeginLocation and ib<=gb.ScopeEndLocation then gb.UsedNameArray[eb]=true break end end else assert(false,"unreachable")end end end end end end local function K(U,V)local W={}for bb,cb in pairs(U)do if not cb.AssignedTo then W[cb.Name]=true end end local X=1 local Y=1 local function Z(bb,cb)bb.Name=cb for db,eb in pairs(bb.RenameList)do eb(cb)end end for bb,cb in pairs(U)do if cb.AssignedTo then Z(cb,'G_'..Y)Y=Y+1 end end local function ab(bb)for cb,db in pairs(bb.VariableList)do local eb='L_'..X..'_'if db.Info.Type=='Argument'then eb=eb..'arg'..db.Info.Index elseif db.Info.Type=='LocalFunction'then eb=eb..'func'elseif db.Info.Type=='ForRange'then eb=eb..'forvar'..db.Info.Index end Z(db,eb)X=X+1 end for cb,db in pairs(bb.ChildScopeList)do ab(db)end end ab(V)end local function L()error("\nusage: minify <file> or unminify <file>\n".." The modified code will be printed to the stdout, pipe it to a file, the\n".." lua interpreter, or something else as desired EG:\n\n".." lua minify.lua minify input.lua > output.lua\n\n".." * minify will minify the code in the file.\n".." * unminify will beautify the code and replace the variable names with easily\n".." find-replacable ones to aide in reverse engineering minified code.\n",0)end local M={...}if#M~=2 then L()end local N=io.open(M[2],'r')if not N then error("Could not open the input file `"..M[2].."`",0)end local O=N:read('*all')local P=f(O)local Q,R=i(P)local function S(U,V,W)I(V,W)B(U)j(U)end local function T(U,V,W)K(V,W)A(U)j(U)end if M[1]=='minify'then S(P,Q,R)elseif M[1]=='unminify'then T(P,Q,R)else L()end |