diff options
author | Philipp Janda <siffiejoe@gmx.net> | 2015-01-17 20:36:32 +0100 |
---|---|---|
committer | Philipp Janda <siffiejoe@gmx.net> | 2015-01-17 20:36:32 +0100 |
commit | 4c062846ae003dd747dfcd3eca91493c7eee4f50 (patch) | |
tree | 583f36eab79954b8bb354d81f4f08cf1472be2b5 | |
parent | 64783408a4a108812f22268c12f71c75f5399d81 (diff) | |
download | lua-compat-5.3-4c062846ae003dd747dfcd3eca91493c7eee4f50.tar.gz lua-compat-5.3-4c062846ae003dd747dfcd3eca91493c7eee4f50.tar.bz2 lua-compat-5.3-4c062846ae003dd747dfcd3eca91493c7eee4f50.zip |
table library (except table.sort for now) respects metamethods
-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 | ||