bwcoercion.lua (1791B)
1 local tonumber, tointeger = tonumber, math.tointeger 2 local type, getmetatable, rawget, error = type, getmetatable, rawget, error 3 local strsub = string.sub 4 5 local print = print 6 7 _ENV = nil 8 9 -- Try to convert a value to an integer, without assuming any coercion. 10 local function toint (x) 11 x = tonumber(x) -- handle numerical strings 12 if not x then 13 return false -- not coercible to a number 14 end 15 return tointeger(x) 16 end 17 18 19 -- If operation fails, maybe second operand has a metamethod that should 20 -- have been called if not for this string metamethod, so try to 21 -- call it. 22 local function trymt (x, y, mtname) 23 if type(y) ~= "string" then -- avoid recalling original metamethod 24 local mt = getmetatable(y) 25 local mm = mt and rawget(mt, mtname) 26 if mm then 27 return mm(x, y) 28 end 29 end 30 -- if any test fails, there is no other metamethod to be called 31 error("attempt to '" .. strsub(mtname, 3) .. 32 "' a " .. type(x) .. " with a " .. type(y), 4) 33 end 34 35 36 local function checkargs (x, y, mtname) 37 local xi = toint(x) 38 local yi = toint(y) 39 if xi and yi then 40 return xi, yi 41 else 42 return trymt(x, y, mtname), nil 43 end 44 end 45 46 47 local smt = getmetatable("") 48 49 smt.__band = function (x, y) 50 local x, y = checkargs(x, y, "__band") 51 return y and x & y or x 52 end 53 54 smt.__bor = function (x, y) 55 local x, y = checkargs(x, y, "__bor") 56 return y and x | y or x 57 end 58 59 smt.__bxor = function (x, y) 60 local x, y = checkargs(x, y, "__bxor") 61 return y and x ~ y or x 62 end 63 64 smt.__shl = function (x, y) 65 local x, y = checkargs(x, y, "__shl") 66 return y and x << y or x 67 end 68 69 smt.__shr = function (x, y) 70 local x, y = checkargs(x, y, "__shr") 71 return y and x >> y or x 72 end 73 74 smt.__bnot = function (x) 75 local x, y = checkargs(x, x, "__bnot") 76 return y and ~x or x 77 end 78