1local tonumber, tointeger = tonumber, math.tointeger
 2local type, getmetatable, rawget, error = type, getmetatable, rawget, error
 3local strsub = string.sub
 4
 5local print = print
 6
 7_ENV = nil
 8
 9-- Try to convert a value to an integer, without assuming any coercion.
10local 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)
16end
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.
22local 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)
33end
34
35
36local 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
44end
45
46
47local smt = getmetatable("")
48
49smt.__band = function (x, y)
50  local x, y = checkargs(x, y, "__band")
51  return y and x & y or x
52end
53
54smt.__bor = function (x, y)
55  local x, y = checkargs(x, y, "__bor")
56  return y and x | y or x
57end
58
59smt.__bxor = function (x, y)
60  local x, y = checkargs(x, y, "__bxor")
61  return y and x ~ y or x
62end
63
64smt.__shl = function (x, y)
65  local x, y = checkargs(x, y, "__shl")
66  return y and x << y or x
67end
68
69smt.__shr = function (x, y)
70  local x, y = checkargs(x, y, "__shr")
71  return y and x >> y or x
72end
73
74smt.__bnot = function (x)
75  local x, y = checkargs(x, x, "__bnot")
76  return y and ~x or x
77end
78