local anumber,bnumber = 111,23.45 local astring,bstring = "abc","def" local anumstr,bnumstr = tostring(anumber),tostring(bnumber) local aboolean,bboolean = false,true local afunction,bfunction = function() end, function() end local athread,bthead = coroutine.create(afunction),coroutine.create(bfunction) local atable,btable = {},{} local values = { anumber, aboolean, afunction, athread, atable } local groups local ts = tostring local tb,count = {},0 tostring = function(o) local t = type(o) if t~='thread' and t~='function' and t~='table' then return ts(o) end if not tb[o] then count = count + 1 tb[o] = t..'.'..count end return tb[o] end local buildop = function(name) return function(a,b) print( 'mt.__'..name..'()', a, b ) return '__'..name..'-result' end end local buildop3 = function(name) return function(a,b,c) print( 'mt.__'..name..'()', a, b, c ) return '__'..name..'-result' end end local buildop1 = function(name) return function(a) print( 'mt.__'..name..'()', a ) return '__'..name..'-result' end end local mt = { __call=buildop('call'), __add=buildop('add'), __sub=buildop('sub'), __mul=buildop('mul'), __div=buildop('div'), __pow=buildop('pow'), __mod=buildop('mod'), __unm=buildop1('unm'), __len=buildop1('len'), __lt=buildop('lt'), __le=buildop('le'), __index=buildop('index'), __newindex=buildop3('newindex'), __concat=buildop('concat'), } -- pcall a function and check for a pattern in the error string ecall = function(pattern, ...) local s,e = pcall(...) if not s then e = string.match(e,pattern) or e end return s,e end print( '---- __eq same types' ) local eqmt = { __eq=buildop('eq'), } groups = { {nil,nil}, {true,false}, {123,456}, {11,5.5}, {afunction,bfunction}, {athread,bthread}, {astring,bstring}, {anumber,anumstr} } for i=1,#groups do local a,b = groups[i][1], groups[i][2] local amt,bmt = debug.getmetatable(a),debug.getmetatable(b) print( type(a), type(b), 'before', pcall( function() return a==b end ) ) print( type(a), type(b), 'before', pcall( function() return a~=b end ) ) print( debug.setmetatable( a, eqmt ) ) print( debug.setmetatable( b, eqmt ) ) print( type(a), type(b), 'after', pcall( function() return a==b end ) ) print( type(a), type(b), 'after', pcall( function() return a~=b end ) ) print( debug.setmetatable( a, amt ) ) print( debug.setmetatable( b, bmt ) ) end print( '---- __eq, tables - should invoke metatag comparison' ) groups = { {atable,btable} } for i=1,#groups do local a,b = groups[i][1], groups[i][2] local amt,bmt = debug.getmetatable(a),debug.getmetatable(b) print( type(a), type(b), 'before', pcall( function() return a==b end ) ) print( type(a), type(b), 'before', pcall( function() return a~=b end ) ) print( debug.setmetatable( a, eqmt ) ) print( debug.setmetatable( b, eqmt ) ) print( type(a), type(b), 'after-a', pcall( function() return a==b end ) ) print( type(a), type(b), 'after-a', pcall( function() return a~=b end ) ) print( debug.setmetatable( a, amt ) ) print( debug.setmetatable( b, bmt ) ) end print( 'nilmt', debug.getmetatable(nil) ) print( 'boolmt', debug.getmetatable(true) ) print( 'number', debug.getmetatable(1) ) print( 'function', debug.getmetatable(afunction) ) print( 'thread', debug.getmetatable(athread) ) print( '---- __call' ) for i=1,#values do local a = values[i] local amt = debug.getmetatable(a) print( type(a), 'before', ecall( 'attempt to call', function() return a('a','b') end ) ) print( debug.setmetatable( a, mt ) ) print( type(a), 'after', pcall( function() return a() end ) ) print( type(a), 'after', pcall( function() return a('a') end ) ) print( type(a), 'after', pcall( function() return a('a','b') end ) ) print( type(a), 'after', pcall( function() return a('a','b','c') end ) ) print( type(a), 'after', pcall( function() return a('a','b','c','d') end ) ) print( debug.setmetatable( a, amt ) ) end print( '---- __add, __sub, __mul, __div, __pow, __mod' ) local groups = { {aboolean, aboolean}, {aboolean, athread}, {aboolean, afunction}, {aboolean, "abc"}, {aboolean, atable} } for i=1,#groups do local a,b = groups[i][1], groups[i][2] local amt,bmt = debug.getmetatable(a),debug.getmetatable(b) print( type(a), type(b), 'before', ecall( 'attempt to perform arithmetic', function() return a+b end ) ) print( type(a), type(b), 'before', ecall( 'attempt to perform arithmetic', function() return b+a end ) ) print( type(a), type(b), 'before', ecall( 'attempt to perform arithmetic', function() return a-b end ) ) print( type(a), type(b), 'before', ecall( 'attempt to perform arithmetic', function() return b-a end ) ) print( type(a), type(b), 'before', ecall( 'attempt to perform arithmetic', function() return a*b end ) ) print( type(a), type(b), 'before', ecall( 'attempt to perform arithmetic', function() return b*a end ) ) print( type(a), type(b), 'before', ecall( 'attempt to perform arithmetic', function() return a^b end ) ) print( type(a), type(b), 'before', ecall( 'attempt to perform arithmetic', function() return b^a end ) ) print( type(a), type(b), 'before', ecall( 'attempt to perform arithmetic', function() return a%b end ) ) print( type(a), type(b), 'before', ecall( 'attempt to perform arithmetic', function() return b%a end ) ) print( debug.setmetatable( a, mt ) ) print( type(a), type(b), 'after', pcall( function() return a+b end ) ) print( type(a), type(b), 'after', pcall( function() return b+a end ) ) print( type(a), type(b), 'after', pcall( function() return a-b end ) ) print( type(a), type(b), 'after', pcall( function() return b-a end ) ) print( type(a), type(b), 'after', pcall( function() return a*b end ) ) print( type(a), type(b), 'after', pcall( function() return b*a end ) ) print( type(a), type(b), 'after', pcall( function() return a^b end ) ) print( type(a), type(b), 'after', pcall( function() return b^a end ) ) print( type(a), type(b), 'after', pcall( function() return a%b end ) ) print( type(a), type(b), 'after', pcall( function() return b%a end ) ) print( debug.setmetatable( a, amt ) ) print( debug.setmetatable( b, bmt ) ) end print( '---- __len' ) values = { aboolean, afunction, athread, anumber } for i=1,#values do local a = values[i] local amt = debug.getmetatable(a) print( type(a), 'before', ecall( 'attempt to get length of ', function() return #a end ) ) print( debug.setmetatable( a, mt ) ) print( type(a), 'after', pcall( function() return #a end ) ) print( debug.setmetatable( a, amt ) ) end -- print( '---- __neg' ) values = { aboolean, afunction, athread, "abcd", atable, anumber } for i=1,#values do local a = values[i] local amt = debug.getmetatable(a) print( type(v), 'before', ecall( 'attempt to perform arithmetic ', function() return -a end ) ) print( debug.setmetatable( a, mt ) ) print( type(v), 'after', pcall( function() return -a end ) ) print( debug.setmetatable( a, amt ) ) end print( '---- __lt, __le, same types' ) local bfunction = function() end local bthread = coroutine.create( bfunction ) local btable = {} local groups groups = { {true, true}, {true, false}, {afunction, bfunction}, {athread, bthread}, {atable, atable}, {atable, btable} } for i=1,#groups do local a,b = groups[i][1], groups[i][2] local amt,bmt = debug.getmetatable(a),debug.getmetatable(b) print( type(a), type(b), 'before', ecall( 'attempt to compare', function() return ab end ) ) print( type(a), type(b), 'before', ecall( 'attempt to compare', function() return a>=b end ) ) print( debug.setmetatable( a, mt ) ) print( debug.setmetatable( b, mt ) ) print( type(a), type(b), 'after', pcall( function() return ab end ) ) print( type(a), type(b), 'after', pcall( function() return a>=b end ) ) print( debug.setmetatable( a, amt ) ) print( debug.setmetatable( b, bmt ) ) end print( '---- __lt, __le, different types' ) groups = { {aboolean, athread}, } for i=1,#groups do local a,b = groups[i][1], groups[i][2] local amt,bmt = debug.getmetatable(a),debug.getmetatable(b) print( type(a), type(b), 'before', ecall( 'attempt to compare', function() return ab end ) ) print( type(a), type(b), 'before', ecall( 'attempt to compare', function() return a>=b end ) ) print( debug.setmetatable( a, mt ) ) print( debug.setmetatable( b, mt ) ) print( type(a), type(b), 'after-a', ecall( 'attempt to compare', function() return ab end ) ) print( type(a), type(b), 'after-a', ecall( 'attempt to compare', function() return a>=b end ) ) print( debug.setmetatable( a, amt ) ) print( debug.setmetatable( b, bmt ) ) end print( '---- __tostring' ) values = { aboolean, afunction, athread, atable, "abc" } local strmt = { __tostring=function(a) return 'mt.__tostring('..type(a)..')' end, } for i=1,#values do local a = values[i] local amt = debug.getmetatable(a) print( debug.setmetatable( a, strmt ) ) print( type(a), 'after', pcall( function() return ts(a) end ) ) print( debug.setmetatable( a, amt ) ) end print( '---- __index, __newindex' ) values = { aboolean, anumber, afunction, athread } for i=1,#values do local a = values[i] local amt = debug.getmetatable(a) print( type(a), 'before', ecall( 'attempt to index', function() return a.foo end ) ) print( type(a), 'before', ecall( 'attempt to index', function() return a[123] end ) ) print( type(a), 'before', ecall( 'index', function() a.foo = 'bar' end ) ) print( type(a), 'before', ecall( 'index', function() a[123] = 'bar' end ) ) print( type(a), 'before', ecall( 'attempt to index', function() return a:foo() end ) ) print( debug.setmetatable( a, mt ) ) print( type(a), 'after', pcall( function() return a.foo end ) ) print( type(a), 'after', pcall( function() return a[123] end ) ) print( type(a), 'after', pcall( function() a.foo = 'bar' end ) ) print( type(a), 'after', pcall( function() a[123] = 'bar' end ) ) print( type(a), 'after', ecall( 'attempt to call', function() return a:foo() end ) ) print( debug.setmetatable( a, amt ) ) end print( '---- __concat' ) groups = { {atable, afunction}, {afunction, atable}, {123, nil}, {nil, 123} } local s,t,u = 'sss',777 local concatresult = setmetatable( { '__concat-result' }, { __tostring=function() return 'concat-string-result' end } ) local concatmt = { __concat=function(a,b) print( 'mt.__concat('..type(a)..','..type(b)..')', a, b ) return concatresult end } for i=1,#groups do local a,b = groups[i][1], groups[i][2] local amt,bmt = debug.getmetatable(a),debug.getmetatable(b) print( type(a), type(b), 'before', ecall( 'attempt to concatenate ', function() return a..b end ) ) print( type(a), type(b), 'before', ecall( 'attempt to concatenate ', function() return b..a end ) ) print( type(a), type(s), type(t), 'before', ecall( 'attempt to concatenate ', function() return a..s..t end ) ) print( type(s), type(a), type(t), 'before', ecall( 'attempt to concatenate ', function() return s..a..t end ) ) print( type(s), type(t), type(a), 'before', ecall( 'attempt to concatenate ', function() return s..t..a end ) ) print( debug.setmetatable( a, concatmt ) ) print( type(a), type(b), 'after', pcall( function() return a..b end ) ) print( type(a), type(b), 'after', pcall( function() return b..a end ) ) print( type(a), type(s), type(t), 'before', pcall( function() return a..s..t end ) ) print( type(s), type(a), type(t), 'before', ecall( 'attempt to concatenate ', function() return s..a..t end ) ) print( type(s), type(t), type(a), 'before', ecall( 'attempt to concatenate ', function() return s..t..a end ) ) print( debug.setmetatable( a, amt ) ) print( debug.setmetatable( b, bmt ) ) end print( '---- __metatable' ) values = { aboolean, afunction, athread, atable, "abc" } local mtmt = { __metatable={}, } for i=1,#values do local a = values[i] local amt = debug.getmetatable(a) print( type(a), 'before', pcall( function() return debug.getmetatable(a), getmetatable(a) end ) ) print( debug.setmetatable( a, mtmt ) ) print( type(a), 'after', pcall( function() return debug.getmetatable(a), getmetatable(a) end ) ) print( debug.setmetatable( a, amt ) ) end