diff options
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | compat53.lua | 158 | ||||
| -rwxr-xr-x | tests/test.lua | 116 |
3 files changed, 265 insertions, 14 deletions
| @@ -66,6 +66,8 @@ your project: | |||
| 66 | * `math.maxinteger` and `math.mininteger`, `math.tointeger`, `math.type`, | 66 | * `math.maxinteger` and `math.mininteger`, `math.tointeger`, `math.type`, |
| 67 | and `math.ult` | 67 | and `math.ult` |
| 68 | * `ipairs` respects `__index` metamethod | 68 | * `ipairs` respects `__index` metamethod |
| 69 | * `table.move` | ||
| 70 | * `table` library (except `table.sort`) respects metamethods | ||
| 69 | 71 | ||
| 70 | ### C | 72 | ### C |
| 71 | 73 | ||
| @@ -112,9 +114,8 @@ For Lua 5.1 additionally: | |||
| 112 | 114 | ||
| 113 | * bit operators | 115 | * bit operators |
| 114 | * integer division operator | 116 | * integer division operator |
| 115 | * `table.move` | ||
| 116 | * `coroutine.isyieldable` | 117 | * `coroutine.isyieldable` |
| 117 | * `table` library doesn't respect metamethods yet | 118 | * `table.sort` doesn't respect metamethods yet |
| 118 | * Lua 5.1: `_ENV`, `goto`, labels, ephemeron tables, etc. See | 119 | * Lua 5.1: `_ENV`, `goto`, labels, ephemeron tables, etc. See |
| 119 | [`lua-compat-5.2`][2] for a detailed list. | 120 | [`lua-compat-5.2`][2] for a detailed list. |
| 120 | * the following C API functions/macros: | 121 | * the following C API functions/macros: |
diff --git a/compat53.lua b/compat53.lua index 8bf6bd8..761d554 100644 --- a/compat53.lua +++ b/compat53.lua | |||
| @@ -1,12 +1,31 @@ | |||
| 1 | local lua_version = _VERSION:sub(-3) | 1 | local lua_version = _VERSION:sub(-3) |
| 2 | 2 | ||
| 3 | if lua_version ~= "5.3" then | 3 | if lua_version < "5.3" then |
| 4 | local _type = type | 4 | -- local aliases for commonly used functions |
| 5 | local type, select, error = type, select, error | ||
| 6 | |||
| 5 | -- select the most powerful getmetatable function available | 7 | -- select the most powerful getmetatable function available |
| 6 | local gmt = _type(debug) == "table" and debug.getmetatable or | 8 | local gmt = type(debug) == "table" and debug.getmetatable or |
| 7 | getmetatable or function() return false end | 9 | getmetatable or function() return false end |
| 10 | |||
| 11 | -- type checking functions | ||
| 8 | local checkinteger -- forward declararation | 12 | local checkinteger -- forward declararation |
| 9 | 13 | ||
| 14 | local function argcheck(cond, i, f, extra) | ||
| 15 | if not cond then | ||
| 16 | error("bad argument #"..i.." to '"..f.."' ("..extra..")", 0) | ||
| 17 | end | ||
| 18 | end | ||
| 19 | |||
| 20 | local function checktype(x, t, i, f) | ||
| 21 | local xt = type(x) | ||
| 22 | if xt ~= t then | ||
| 23 | error("bad argument #"..i.." to '"..f.."' ("..t.. | ||
| 24 | " expected, got "..xt..")", 0) | ||
| 25 | end | ||
| 26 | end | ||
| 27 | |||
| 28 | |||
| 10 | -- load utf8 library | 29 | -- load utf8 library |
| 11 | local ok, utf8lib = pcall(require, "compat53.utf8") | 30 | local ok, utf8lib = pcall(require, "compat53.utf8") |
| 12 | if ok then | 31 | if ok then |
| @@ -17,6 +36,7 @@ if lua_version ~= "5.3" then | |||
| 17 | end | 36 | end |
| 18 | end | 37 | end |
| 19 | 38 | ||
| 39 | |||
| 20 | -- use Roberto's struct module for string packing/unpacking for now | 40 | -- use Roberto's struct module for string packing/unpacking for now |
| 21 | -- maybe we'll later extract the functions from the 5.3 string | 41 | -- maybe we'll later extract the functions from the 5.3 string |
| 22 | -- library for greater compatiblity, but it uses the 5.3 buffer API | 42 | -- library for greater compatiblity, but it uses the 5.3 buffer API |
| @@ -47,14 +67,14 @@ if lua_version ~= "5.3" then | |||
| 47 | math.mininteger = minint | 67 | math.mininteger = minint |
| 48 | 68 | ||
| 49 | function math.tointeger(n) | 69 | function math.tointeger(n) |
| 50 | if _type(n) == "number" and n <= maxint and n >= minint and n % 1 == 0 then | 70 | if type(n) == "number" and n <= maxint and n >= minint and n % 1 == 0 then |
| 51 | return n | 71 | return n |
| 52 | end | 72 | end |
| 53 | return nil | 73 | return nil |
| 54 | end | 74 | end |
| 55 | 75 | ||
| 56 | function math.type(n) | 76 | function math.type(n) |
| 57 | if _type(n) == "number" then | 77 | if type(n) == "number" then |
| 58 | if n <= maxint and n >= minint and n % 1 == 0 then | 78 | if n <= maxint and n >= minint and n % 1 == 0 then |
| 59 | return "integer" | 79 | return "integer" |
| 60 | else | 80 | else |
| @@ -65,15 +85,14 @@ if lua_version ~= "5.3" then | |||
| 65 | end | 85 | end |
| 66 | end | 86 | end |
| 67 | 87 | ||
| 68 | local _error = error | ||
| 69 | function checkinteger(x, i, f) | 88 | function checkinteger(x, i, f) |
| 70 | local t = _type(x) | 89 | local t = type(x) |
| 71 | if t ~= "number" then | 90 | if t ~= "number" then |
| 72 | _error("bad argument #"..i.." to '"..f.. | 91 | error("bad argument #"..i.." to '"..f.. |
| 73 | "' (number expected, got "..t..")", 0) | 92 | "' (number expected, got "..t..")", 0) |
| 74 | elseif x > maxint or x < minint or x % 1 ~= 0 then | 93 | elseif x > maxint or x < minint or x % 1 ~= 0 then |
| 75 | _error("bad argument #"..i.." to '"..f.. | 94 | error("bad argument #"..i.." to '"..f.. |
| 76 | "' (number has no integer representation)", 0) | 95 | "' (number has no integer representation)", 0) |
| 77 | else | 96 | else |
| 78 | return x | 97 | return x |
| 79 | end | 98 | end |
| @@ -112,6 +131,123 @@ if lua_version ~= "5.3" then | |||
| 112 | end | 131 | end |
| 113 | end | 132 | end |
| 114 | 133 | ||
| 134 | |||
| 135 | -- update table library | ||
| 136 | do | ||
| 137 | local table_concat = table.concat | ||
| 138 | function table.concat(list, sep, i, j) | ||
| 139 | local mt = gmt(list) | ||
| 140 | if type(mt) == "table" and type(mt.__len) == "function" then | ||
| 141 | local src = list | ||
| 142 | list, i, j = {}, i or 1, j or mt.__len(src) | ||
| 143 | for k = i, j do | ||
| 144 | list[k] = src[k] | ||
| 145 | end | ||
| 146 | end | ||
| 147 | return table_concat(list, sep, i, j) | ||
| 148 | end | ||
| 149 | |||
| 150 | local table_insert = table.insert | ||
| 151 | function table.insert(list, ...) | ||
| 152 | local mt = gmt(list) | ||
| 153 | local has_mt = type(mt) == "table" | ||
| 154 | local has_len = has_mt and type(mt.__len) == "function" | ||
| 155 | if has_mt and (has_len or mt.__index or mt.__newindex) then | ||
| 156 | local e = (has_len and mt.__len(list) or #list)+1 | ||
| 157 | local nargs, pos, value = select('#', ...), ... | ||
| 158 | if nargs == 1 then | ||
| 159 | pos, value = e, pos | ||
| 160 | elseif nargs == 2 then | ||
| 161 | pos = checkinteger(pos, "2", "table.insert") | ||
| 162 | argcheck(1 <= pos and pos <= e, "2", "table.insert", | ||
| 163 | "position out of bounds" ) | ||
| 164 | else | ||
| 165 | error("wrong number of arguments to 'insert'", 0) | ||
| 166 | end | ||
| 167 | for i = e-1, pos, -1 do | ||
| 168 | list[i+1] = list[i] | ||
| 169 | end | ||
| 170 | list[pos] = value | ||
| 171 | else | ||
| 172 | return table_insert(list, ...) | ||
| 173 | end | ||
| 174 | end | ||
| 175 | |||
| 176 | function table.move(a1, f, e, t, a2) | ||
| 177 | a2 = a2 or a1 | ||
| 178 | f = checkinteger(f, "2", "table.move") | ||
| 179 | argcheck(f > 0, "2", "table.move", | ||
| 180 | "initial position must be positive") | ||
| 181 | e = checkinteger(e, "3", "table.move") | ||
| 182 | t = checkinteger(t, "4", "table.move") | ||
| 183 | if e >= f then | ||
| 184 | local m, n, d = 0, e-f, 1 | ||
| 185 | if t > f then m, n, d = n, m, -1 end | ||
| 186 | for i = m, n, d do | ||
| 187 | a2[t+i] = a1[f+i] | ||
| 188 | end | ||
| 189 | end | ||
| 190 | return a2 | ||
| 191 | end | ||
| 192 | |||
| 193 | local table_remove = table.remove | ||
| 194 | function table.remove(list, pos) | ||
| 195 | local mt = gmt(list) | ||
| 196 | local has_mt = type(mt) == "table" | ||
| 197 | local has_len = has_mt and type(mt.__len) == "function" | ||
| 198 | if has_mt and (has_len or mt.__index or mt.__newindex) then | ||
| 199 | local e = (has_len and mt.__len(list) or #list) | ||
| 200 | pos = pos ~= nil and checkinteger(pos, "2", "table.remove") or e | ||
| 201 | if pos ~= e then | ||
| 202 | argcheck(1 <= pos and pos <= e+1, "2", "table.remove", | ||
| 203 | "position out of bounds" ) | ||
| 204 | end | ||
| 205 | local result = list[pos] | ||
| 206 | while pos < e do | ||
| 207 | list[pos] = list[pos+1] | ||
| 208 | pos = pos + 1 | ||
| 209 | end | ||
| 210 | list[pos] = nil | ||
| 211 | return result | ||
| 212 | else | ||
| 213 | return table_remove(list, pos) | ||
| 214 | end | ||
| 215 | end | ||
| 216 | |||
| 217 | -- TODO: table.sort | ||
| 218 | |||
| 219 | local table_unpack = lua_version == "5.1" and unpack or table.unpack | ||
| 220 | local function unpack_helper(list, i, j, ...) | ||
| 221 | if j < i then | ||
| 222 | return ... | ||
| 223 | else | ||
| 224 | return unpack_helper(list, i, j-1, list[j], ...) | ||
| 225 | end | ||
| 226 | end | ||
| 227 | function table.unpack(list, i, j) | ||
| 228 | local mt = gmt(list) | ||
| 229 | local has_mt = type(mt) == "table" | ||
| 230 | local has_len = has_mt and type(mt.__len) == "function" | ||
| 231 | if has_mt and (has_len or mt.__index) then | ||
| 232 | i, j = i or 1, j or (has_len and mt.__len(list)) or #list | ||
| 233 | return unpack_helper(list, i, j) | ||
| 234 | else | ||
| 235 | return table_unpack(list, i, j) | ||
| 236 | end | ||
| 237 | end | ||
| 238 | end | ||
| 239 | |||
| 240 | |||
| 241 | if lua_version == "5.1" then | ||
| 242 | -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag) | ||
| 243 | local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ" | ||
| 244 | local is_luajit52 = is_luajit and | ||
| 245 | #setmetatable({}, { __len = function() return 1 end }) == 1 | ||
| 246 | |||
| 247 | -- TODO: add functions from lua-compat-5.2 | ||
| 248 | |||
| 249 | end | ||
| 250 | |||
| 115 | end | 251 | end |
| 116 | 252 | ||
| 117 | -- vi: set expandtab softtabstop=3 shiftwidth=3 : | 253 | -- vi: set expandtab softtabstop=3 shiftwidth=3 : |
diff --git a/tests/test.lua b/tests/test.lua index ab25820..7e1c9da 100755 --- a/tests/test.lua +++ b/tests/test.lua | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | #!/usr/bin/env lua | 1 | #!/usr/bin/env lua |
| 2 | 2 | ||
| 3 | local F, ___ | 3 | local F, tproxy, ___ |
| 4 | do | 4 | do |
| 5 | local type, unpack = type, table.unpack or unpack | 5 | local type, unpack = type, table.unpack or unpack |
| 6 | function F(...) | 6 | function F(...) |
| @@ -13,6 +13,13 @@ do | |||
| 13 | end | 13 | end |
| 14 | return unpack(args, 1, n) | 14 | return unpack(args, 1, n) |
| 15 | end | 15 | end |
| 16 | function tproxy(t) | ||
| 17 | return setmetatable({}, { | ||
| 18 | __index = t, | ||
| 19 | __newindex = t, | ||
| 20 | __len = function() return #t end, | ||
| 21 | }), t | ||
| 22 | end | ||
| 16 | local sep = ("="):rep(70) | 23 | local sep = ("="):rep(70) |
| 17 | function ___() | 24 | function ___() |
| 18 | print(sep) | 25 | print(sep) |
| @@ -33,6 +40,113 @@ end | |||
| 33 | 40 | ||
| 34 | 41 | ||
| 35 | ___'' | 42 | ___'' |
| 43 | do | ||
| 44 | local p, t = tproxy{ "a", "b", "c" } | ||
| 45 | print("table.concat", table.concat(p)) | ||
| 46 | print("table.concat", table.concat(p, ",", 2)) | ||
| 47 | print("table.concat", table.concat(p, ".", 1, 2)) | ||
| 48 | print("table.concat", table.concat(t)) | ||
| 49 | print("table.concat", table.concat(t, ",", 2)) | ||
| 50 | print("table.concat", table.concat(t, ".", 1, 2)) | ||
| 51 | end | ||
| 52 | |||
| 53 | |||
| 54 | ___'' | ||
| 55 | do | ||
| 56 | local p, t = tproxy{ "a", "b", "c" } | ||
| 57 | table.insert(p, "d") | ||
| 58 | print("table.insert", next(p), t[4]) | ||
| 59 | table.insert(p, 1, "z") | ||
| 60 | print("table.insert", next(p), t[1], t[2]) | ||
| 61 | table.insert(p, 2, "y") | ||
| 62 | print("table.insert", next(p), t[1], t[2], p[3]) | ||
| 63 | t = { "a", "b", "c" } | ||
| 64 | table.insert(t, "d") | ||
| 65 | print("table.insert", t[1], t[2], t[3], t[4]) | ||
| 66 | table.insert(t, 1, "z") | ||
| 67 | print("table.insert", t[1], t[2], t[3], t[4], t[5]) | ||
| 68 | table.insert(t, 2, "y") | ||
| 69 | print("table.insert", t[1], t[2], t[3], t[4], t[5]) | ||
| 70 | end | ||
| 71 | |||
| 72 | |||
| 73 | ___'' | ||
| 74 | do | ||
| 75 | local ps, s = tproxy{ "a", "b", "c", "d" } | ||
| 76 | local pd, d = tproxy{ "A", "B", "C", "D" } | ||
| 77 | table.move(ps, 1, 4, 1, pd) | ||
| 78 | print("table.move", next(pd), d[1], d[2], d[3], d[4]) | ||
| 79 | pd, d = tproxy{ "A", "B", "C", "D" } | ||
| 80 | table.move(ps, 2, 4, 1, pd) | ||
| 81 | print("table.move", next(pd), d[1], d[2], d[3], d[4]) | ||
| 82 | pd, d = tproxy{ "A", "B", "C", "D" } | ||
| 83 | table.move(ps, 2, 3, 4, pd) | ||
| 84 | print("table.move", next(pd), d[1], d[2], d[3], d[4], d[5]) | ||
| 85 | table.move(ps, 2, 4, 1) | ||
| 86 | print("table.move", next(ps), s[1], s[2], s[3], s[4]) | ||
| 87 | ps, s = tproxy{ "a", "b", "c", "d" } | ||
| 88 | table.move(ps, 2, 3, 4) | ||
| 89 | print("table.move", next(ps), s[1], s[2], s[3], s[4], s[5]) | ||
| 90 | s = { "a", "b", "c", "d" } | ||
| 91 | d = { "A", "B", "C", "D" } | ||
| 92 | table.move(s, 1, 4, 1, d) | ||
| 93 | print("table.move", d[1], d[2], d[3], d[4]) | ||
| 94 | d = { "A", "B", "C", "D" } | ||
| 95 | table.move(s, 2, 4, 1, d) | ||
| 96 | print("table.move", d[1], d[2], d[3], d[4]) | ||
| 97 | d = { "A", "B", "C", "D" } | ||
| 98 | table.move(s, 2, 3, 4, d) | ||
| 99 | print("table.move", d[1], d[2], d[3], d[4], d[5]) | ||
| 100 | table.move(s, 2, 4, 1) | ||
| 101 | print("table.move", s[1], s[2], s[3], s[4]) | ||
| 102 | s = { "a", "b", "c", "d" } | ||
| 103 | table.move(s, 2, 3, 4) | ||
| 104 | print("table.move", s[1], s[2], s[3], s[4], s[5]) | ||
| 105 | end | ||
| 106 | |||
| 107 | |||
| 108 | ___'' | ||
| 109 | do | ||
| 110 | local p, t = tproxy{ "a", "b", "c", "d", "e" } | ||
| 111 | print("table.remove", table.remove(p)) | ||
| 112 | print("table.remove", next(p), t[1], t[2], t[3], t[4], t[5]) | ||
| 113 | print("table.remove", table.remove(p, 1)) | ||
| 114 | print("table.remove", next(p), t[1], t[2], t[3], t[4]) | ||
| 115 | print("table.remove", table.remove(p, 2)) | ||
| 116 | print("table.remove", next(p), t[1], t[2], t[3]) | ||
| 117 | print("table.remove", table.remove(p, 3)) | ||
| 118 | print("table.remove", next(p), t[1], t[2], t[3]) | ||
| 119 | p, t = tproxy{} | ||
| 120 | print("table.remove", table.remove(p)) | ||
| 121 | print("table.remove", next(p), next(t)) | ||
| 122 | t = { "a", "b", "c", "d", "e" } | ||
| 123 | print("table.remove", table.remove(t)) | ||
| 124 | print("table.remove", t[1], t[2], t[3], t[4], t[5]) | ||
| 125 | print("table.remove", table.remove(t, 1)) | ||
| 126 | print("table.remove", t[1], t[2], t[3], t[4]) | ||
| 127 | print("table.remove", table.remove(t, 2)) | ||
| 128 | print("table.remove", t[1], t[2], t[3]) | ||
| 129 | print("table.remove", table.remove(t, 3)) | ||
| 130 | print("table.remove", t[1], t[2], t[3]) | ||
| 131 | t = {} | ||
| 132 | print("table.remove", table.remove(t)) | ||
| 133 | print("table.remove", next(t)) | ||
| 134 | end | ||
| 135 | |||
| 136 | |||
| 137 | ___'' | ||
| 138 | do | ||
| 139 | local p, t = tproxy{ "a", "b", "c" } | ||
| 140 | print("table.unpack", table.unpack(p)) | ||
| 141 | print("table.unpack", table.unpack(p, 2)) | ||
| 142 | print("table.unpack", table.unpack(p, 1, 2)) | ||
| 143 | print("table.unpack", table.unpack(t)) | ||
| 144 | print("table.unpack", table.unpack(t, 2)) | ||
| 145 | print("table.unpack", table.unpack(t, 1, 2)) | ||
| 146 | end | ||
| 147 | |||
| 148 | |||
| 149 | ___'' | ||
| 36 | print("math.maxinteger", math.maxinteger+1 > math.maxinteger) | 150 | print("math.maxinteger", math.maxinteger+1 > math.maxinteger) |
| 37 | print("math.mininteger", math.mininteger-1 < math.mininteger) | 151 | print("math.mininteger", math.mininteger-1 < math.mininteger) |
| 38 | 152 | ||
