local tonumber, tointeger = tonumber, math.tointeger local type, getmetatable, rawget, error = type, getmetatable, rawget, error local strsub = string.sub local print = print _ENV = nil -- Try to convert a value to an integer, without assuming any coercion. local function toint (x) x = tonumber(x) -- handle numerical strings if not x then return false -- not coercible to a number end return tointeger(x) end -- If operation fails, maybe second operand has a metamethod that should -- have been called if not for this string metamethod, so try to -- call it. local function trymt (x, y, mtname) if type(y) ~= "string" then -- avoid recalling original metamethod local mt = getmetatable(y) local mm = mt and rawget(mt, mtname) if mm then return mm(x, y) end end -- if any test fails, there is no other metamethod to be called error("attempt to '" .. strsub(mtname, 3) .. "' a " .. type(x) .. " with a " .. type(y), 4) end local function checkargs (x, y, mtname) local xi = toint(x) local yi = toint(y) if xi and yi then return xi, yi else return trymt(x, y, mtname), nil end end local smt = getmetatable("") smt.__band = function (x, y) local x, y = checkargs(x, y, "__band") return y and x & y or x end smt.__bor = function (x, y) local x, y = checkargs(x, y, "__bor") return y and x | y or x end smt.__bxor = function (x, y) local x, y = checkargs(x, y, "__bxor") return y and x ~ y or x end smt.__shl = function (x, y) local x, y = checkargs(x, y, "__shl") return y and x << y or x end smt.__shr = function (x, y) local x, y = checkargs(x, y, "__shr") return y and x >> y or x end smt.__bnot = function (x) local x, y = checkargs(x, x, "__bnot") return y and ~x or x end