-- test suite for dmlib.sourceoptimizer local SO = require "dmlib.sourceoptimizer" -- Removes unnecessary spaces in Lua code (actually a superset of Lua). -- Useful for comparing two code strings. -- warning: does not parse strings/comments specially -- warning: uses undocumented http://lua-users.org/wiki/FrontierPattern . local function normalize(code) code = code:gsub('^%s+', '') code = code:gsub('%s+$', '') code = code:gsub('%s+', ' ') code = code:gsub('%f[%a%d_ %- %.] ', '') -- non-alphanum (less '.'/'-'), any code = code:gsub('%f[^%a%d_] ([^%a%d_])', '%1') -- alphanum, non-alphanum -- avoid digraph/trigraph problems ('--', '..', '...') code = code:gsub('%f[- ] (-)', '%1') -- non '-', '-' code = code:gsub('%f[^-] ([^-])', '%1') -- '-', non '-' code = code:gsub('%f[%. ] (%.)', '%1') -- non '.', '.' code = code:gsub('%f[^%.] ([^%.])', '%1') -- '.', non '.' return code end local function nsimplify(code) return normalize(SO.inline_functions_code(code)) end --test( -- [[return (function() return math.modf(1,2) end)()]], -- [[return math.modf(1,2)]] --) local function asserteq(a,b) if a ~= b then error('failed match:\n' .. ' ['..tostring(a)..'] ==\n' .. ' ['..tostring(b)..']', 2) end end -- evaluate at no value position asserteq(nsimplify[[(function() end)()]], [[]]) asserteq(nsimplify[[(function() return end)()]], [[]]) asserteq(nsimplify[[(function() return f() end)()]], [[f()]]) asserteq(nsimplify[[(function() return f(),g() end)()]], [[f()g()]]) asserteq(nsimplify[[(function() return x end)()]], [[do local _=x end]]) asserteq(nsimplify[[(function() return 1 end)()]], [[do local _=1 end]]) --IMPROVE? asserteq(nsimplify[[(function() return f(),x,g(),y end)()]], [[f()g()do local _=x,y end]]) asserteq(nsimplify[[(function() e() return (function()f()end)(), (function()g()end)()+1 end)()]], [[e()f()g()do local _=nil,nil+1 end]]) --IMPROVE? -- evaluate at multiple value position asserteq(nsimplify[[x=(function() end)()]], [[x=nil]]) asserteq(nsimplify[[x=(function() f() return end)()]], [[f()x=nil]]) asserteq(nsimplify[[x=1,(function() f() end)()]], [[f()x=1]]) -- allow 0 values asserteq(nsimplify[[x=(function() return f() end)()]], [[x=f()]]) asserteq(nsimplify[[x=(function() return f(),g() end)()]], [[x=f(),g()]]) -- evaluate at single value position asserteq(nsimplify[[x=(function() end)(), 1]], [[x=nil,1]]) asserteq(nsimplify[[x=(function() return end)(), 1]], [[x=nil,1]]) asserteq(nsimplify[[x=(function() return f() end)(), 1]], [[x=f(),1]]) asserteq(nsimplify[[x=(function() return f(),g() end)(), 1]], [[g()x=f(),1]]) -- tables asserteq(nsimplify[[x={(function()f() return x,y end)()}]], [[f()x={x,y}]]) asserteq(nsimplify[[x={1,(function()f() end)()}]], [[f()x={1}]]) asserteq(nsimplify[[x={[1]=(function()f() return x,y end)()}]], [[f()do local _=y end x={[1]=x}]]) -- local asserteq(nsimplify[[local x=(function()f()end)()]], [[f()local x]]) asserteq(nsimplify[[local x=(function()f() return y,z end)()]], [[f()local x=y,z]]) -- return asserteq(nsimplify[[return (function()f()end)()]], [[f()return]]) asserteq(nsimplify[[return (function()f() return x,y end)()]], [[f()return x,y]]) -- 'or'/'and' short circuiting asserteq( nsimplify[[x = y or (function(x)z=1 return x*x end)(w)]], [[local __v2=y if not __v2 then local __v1x=w z=1 __v2=__v1x*__v1x end x=__v2]] ) asserteq( nsimplify[[x = y and (function(x)z=1 return x*x end)(w)]], [[local __v2=y if __v2 then local __v1x=w z=1 __v2=__v1x*__v1x end x=__v2]] ) -- while statements asserteq( nsimplify[[while (function()x=x+1 return x <= 5 end)() do print(x) end]], [[while 1 do x=x+1 if not(x<=5)then break end print(x)end]] ) -- repeat statements asserteq( nsimplify[[repeat print(x) until (function()x=x+1 return x <= 5 end)()]], [[repeat print(x)x=x+1 until x<=5]] ) -- if statements asserteq( nsimplify[[if (function()x=x+1 return x end)() then f() elseif (function()y=y+1 return y end)() then g() else h() end]], [[x=x+1 if x then f()else y=y+1 if y then g()else h()end end]] ) -- fornum statements asserteq( nsimplify[[for i=(function()return x,x2 end)(), (function()return y,y2 end)(), (function()return z,z2 end)() do f() end]], [[do local _=x2 end do local _=y2 end do local _=z2 end for i=x,y,z do f()end]] ) -- forin statements asserteq( nsimplify[[for i in (function()a()return x end)() do f() end]], [[a()for i in x do f()end]] ) asserteq( nsimplify[[for i in (function()a()return x end)(), (function()b()return y end)(), (function()c()return z end)() do f() end]], [[a()b()c()for i in x,y,z do f()end]] ) asserteq( nsimplify[[for i in (function()a()return x,xx end)(), (function()b()return y end)(), (function()c()return z,zz end)() do f() end]], [[a()do local _=xx end b()c()do local _=zz end for i in x,y,z do f()end]] ) asserteq( nsimplify[[for i in (function()a()return x,xx end)(), (function()b()return y,y2,y3 end)() do f() end ]], [[a()do local _=xx end b()do local _=y3 end for i in x,y,y2 do f()end]] ) -- non-anonymous function asserteq( nsimplify[[local function square(x) return x*x end; x=square(y)]], [[local __v1x=y x=__v1x*__v1x]] ) -- fully inlined asserteq( nsimplify[[local function square(x) return x*x end; x=square(y) z=square]], [[local function square(x)return x*x end local __v1x=y x=__v1x*__v1x z=square]] ) -- partly inlined asserteq( nsimplify[[local function square(x) return x*x end; square=math.sqrt x=square(y)]], [[local function square(x)return x*x end square=math.sqrt x=square(y)]] ) --not inlinable -- returns asserteq( nsimplify[[x = (function() if 1 then return end end)()]], [[x=(function()if 1 then return end end)()]] -- not inlinable ) asserteq( nsimplify[[x = (function() return (function() return 1 end) end)()]], [[x=(function()return 1 end)]] ) -- ignore nested asserteq( nsimplify[[x = (function() return (function() return 1 end)() end)()]], [[x=1]] ) -- inline nested asserteq( nsimplify[[x = (function() f() return (function() g() return 1 end)() end)()]], [[f()g()x=1]] ) -- inline nested local s = nsimplify[[x = (function(y) f() return (function(y) g() return y end)(y) end)(y)]] assert(s == [[local __v3y=y f()local __v2y=__v3y g()x=__v2y]] or s == [[local __v2y=y f()local __v3y=__v2y g()x=__v3y]],s ) --IMPROVE: why varnames non-deterministic? -- name collisions asserteq( nsimplify[[(function() local x=y end)()]], [[local __v1x=y]] ) asserteq( nsimplify[[local __v1x (function() local x=y end)()]], [[local __v1x local __vv1x=y]] ) asserteq( nsimplify[[__v1x=1; (function() local x=y end)()]], [[__v1x=1 local __vv1x=y]] ) -- dots --IMPROVE--NOT IMPL asserteq( -- nsimplify[[x=(function(...) return ... end)(a,b)]], -- [[x=a,b]] --) asserteq( nsimplify[[x=(function(...) return ... end)(a,b)]], [[x=(function(...)return...end)(a,b)]] ) -- examples asserteq( nsimplify[[x=(function(x) return x*x end)(y())]], [[local __v1x=y()x=__v1x*__v1x]] ) asserteq( nsimplify[[x=(function(x) return x*x end)(y)]], [[local __v1x=y x=__v1x*__v1x]] --IMPROVE? ) asserteq( nsimplify[[local function square(x) return x*x end; x=square(y)]], [[local __v1x=y x=__v1x*__v1x]] ) ----[=[ TEST: normalize asserteq(normalize[[]], '') asserteq(normalize[[ a = 1 b = _ ( - x . y)]], 'a=1 b=_(-x.y)') asserteq(normalize[[( 1 ) - - ( 1 )]], '(1)- -(1)') asserteq(normalize[[( ... .. ... )]], '(... .. ...)') asserteq(normalize[[" "]], '""') -- warning --]=] local s = nsimplify[[ local y=-1 local function square(x) return x*x end local function g(x) return x >= 0 and square(square(x))+1 or 1 end while (function() y = g(y)^(1/4) return y < 2 end)() do print(y) end ]] local t = {} local id=0 local s2 = normalize[[ local y = 1 while 1 do local __v12x = y local __v10 = 0 <= __v12x if __v10 then local __v11x = __v12x local __v14x = __v11x * __v11x __v10 = __v14x * __v14x + 1 end local __v13 = __v10 if not __v13 then __v13 = 1 end y = __v13 ^ (1 / 4) if not (y < 2) then break end print (y) end ]]:gsub('[^%w _]','%%%1'):gsub('__v%d+', function(s) if t[s] then return t[s] else id = id + 1 t[s] = '__v%' .. id return '__v(%d+)' end end) assert(s:match(s2), s) print 'DONE'