diff options
author | Philipp Janda <siffiejoe@gmx.net> | 2015-04-29 12:26:34 +0200 |
---|---|---|
committer | Philipp Janda <siffiejoe@gmx.net> | 2015-04-29 12:26:34 +0200 |
commit | c87038c1f3a6d3a21d14d6db1affbf879a25386a (patch) | |
tree | 0bbdb4925e1c47185b32f5e678a1265b80e69862 | |
parent | ec5331cb94f9100fe4cf26d1ea215dd66e8e185c (diff) | |
parent | 17fdace5c04486db3470fe7022aee7eac8efc3af (diff) | |
download | lua-compat-5.3-c87038c1f3a6d3a21d14d6db1affbf879a25386a.tar.gz lua-compat-5.3-c87038c1f3a6d3a21d14d6db1affbf879a25386a.tar.bz2 lua-compat-5.3-c87038c1f3a6d3a21d14d6db1affbf879a25386a.zip |
Merge branch 'isolated'
Conflicts:
README.md
-rw-r--r-- | README.md | 18 | ||||
-rw-r--r-- | compat53/init.lua | 324 | ||||
-rw-r--r-- | compat53/module.lua (renamed from compat53.lua) | 470 | ||||
-rwxr-xr-x | tests/test.lua | 130 |
4 files changed, 559 insertions, 383 deletions
@@ -42,6 +42,22 @@ string packing modules automatically. If unsuccessful, pure Lua | |||
42 | versions of the new `table` functions are used as a fallback, and | 42 | versions of the new `table` functions are used as a fallback, and |
43 | [Roberto's struct library][1] is tried for string packing. | 43 | [Roberto's struct library][1] is tried for string packing. |
44 | 44 | ||
45 | #### Lua submodules | ||
46 | |||
47 | ```lua | ||
48 | local _ENV = require("compat53.module") | ||
49 | if setfenv then setfenv(1, _ENV) end | ||
50 | ``` | ||
51 | |||
52 | The `compat53.module` module does not modify the global environment, | ||
53 | and so it is safe to use in modules without affecting other Lua files. | ||
54 | It is supposed to be set as the current environment (see above), i.e. | ||
55 | cherry picking individual functions from this module is expressly | ||
56 | *not* supported!). Not all features are available when using this | ||
57 | module (e.g. yieldable (x)pcall support, string/file methods, etc.), | ||
58 | so it is recommended to use plain `require("compat53")` whenever | ||
59 | possible. | ||
60 | |||
45 | ### C code | 61 | ### C code |
46 | 62 | ||
47 | There are two ways of adding the C API compatibility functions/macros to | 63 | There are two ways of adding the C API compatibility functions/macros to |
@@ -67,7 +83,7 @@ your project: | |||
67 | 5.3 sources or from the `struct` module. (`struct` is not 100% | 83 | 5.3 sources or from the `struct` module. (`struct` is not 100% |
68 | compatible to Lua 5.3's string packing!) (See [here][4]) | 84 | compatible to Lua 5.3's string packing!) (See [here][4]) |
69 | * `math.maxinteger` and `math.mininteger`, `math.tointeger`, `math.type`, | 85 | * `math.maxinteger` and `math.mininteger`, `math.tointeger`, `math.type`, |
70 | and `math.ult` (See [here][5]) | 86 | and `math.ult` (see [here][5]) |
71 | * `ipairs` respects `__index` metamethod | 87 | * `ipairs` respects `__index` metamethod |
72 | * `table.move` | 88 | * `table.move` |
73 | * `table` library respects metamethods | 89 | * `table` library respects metamethods |
diff --git a/compat53/init.lua b/compat53/init.lua new file mode 100644 index 0000000..31085be --- /dev/null +++ b/compat53/init.lua | |||
@@ -0,0 +1,324 @@ | |||
1 | local _G, _VERSION, type, pairs, require = | ||
2 | _G, _VERSION, type, pairs, require | ||
3 | |||
4 | local M = require("compat53.module") | ||
5 | local lua_version = _VERSION:sub(-3) | ||
6 | |||
7 | |||
8 | -- apply other global effects | ||
9 | if lua_version == "5.1" then | ||
10 | |||
11 | -- cache globals | ||
12 | local error, pcall, rawset, select, setmetatable, tostring, unpack, xpcall = | ||
13 | error, pcall, rawset, select, setmetatable, tostring, unpack, xpcall | ||
14 | local coroutine, debug, io, package, string = | ||
15 | coroutine, debug, io, package, string | ||
16 | local coroutine_create = coroutine.create | ||
17 | local coroutine_resume = coroutine.resume | ||
18 | local coroutine_running = coroutine.running | ||
19 | local coroutine_status = coroutine.status | ||
20 | local coroutine_yield = coroutine.yield | ||
21 | local io_type, io_stdout = io.type, io.stdout | ||
22 | |||
23 | -- select the most powerful getmetatable function available | ||
24 | local gmt = type(debug) == "table" and debug.getmetatable or | ||
25 | getmetatable or function() return false end | ||
26 | |||
27 | -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag) | ||
28 | local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ" | ||
29 | local is_luajit52 = is_luajit and | ||
30 | #setmetatable({}, { __len = function() return 1 end }) == 1 | ||
31 | |||
32 | |||
33 | -- make package.searchers available as an alias for package.loaders | ||
34 | local p_index = { searchers = package.loaders } | ||
35 | setmetatable(package, { | ||
36 | __index = p_index, | ||
37 | __newindex = function(p, k, v) | ||
38 | if k == "searchers" then | ||
39 | rawset(p, "loaders", v) | ||
40 | p_index.searchers = v | ||
41 | else | ||
42 | rawset(p, k, v) | ||
43 | end | ||
44 | end | ||
45 | }) | ||
46 | |||
47 | |||
48 | if not is_luajit then | ||
49 | local function helper(st, var_1, ...) | ||
50 | if var_1 == nil then | ||
51 | if (...) ~= nil then | ||
52 | error((...), 2) | ||
53 | end | ||
54 | end | ||
55 | return var_1, ... | ||
56 | end | ||
57 | |||
58 | local function lines_iterator(st) | ||
59 | return helper(st, st.f:read(unpack(st, 1, st.n))) | ||
60 | end | ||
61 | |||
62 | local valid_format = { ["*l"] = true, ["*n"] = true, ["*a"] = true } | ||
63 | |||
64 | local file_meta = gmt(io_stdout) | ||
65 | if type(file_meta) == "table" and type(file_meta.__index) == "table" then | ||
66 | local file_write = file_meta.__index.write | ||
67 | file_meta.__index.write = function(self, ...) | ||
68 | local res, msg, errno = file_write(self, ...) | ||
69 | if res then | ||
70 | return self | ||
71 | else | ||
72 | return nil, msg, errno | ||
73 | end | ||
74 | end | ||
75 | |||
76 | file_meta.__index.lines = function(self, ...) | ||
77 | if io_type(self) == "closed file" then | ||
78 | error("attempt to use a closed file", 2) | ||
79 | end | ||
80 | local st = { f=self, n=select('#', ...), ... } | ||
81 | for i = 1, st.n do | ||
82 | if type(st[i]) ~= "number" and not valid_format[st[i]] then | ||
83 | error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2) | ||
84 | end | ||
85 | end | ||
86 | return lines_iterator, st | ||
87 | end | ||
88 | end | ||
89 | end -- not luajit | ||
90 | |||
91 | |||
92 | -- the (x)pcall implementations start a new coroutine internally | ||
93 | -- to allow yielding even in Lua 5.1. to allow for accurate | ||
94 | -- stack traces we keep track of the nested coroutine activations | ||
95 | -- in the weak tables below: | ||
96 | local weak_meta = { __mode = "kv" } | ||
97 | -- maps the internal pcall coroutines to the user coroutine that | ||
98 | -- *should* be running if pcall didn't use coroutines internally | ||
99 | local pcall_mainOf = setmetatable({}, weak_meta) | ||
100 | -- table that maps each running coroutine started by pcall to | ||
101 | -- the coroutine that resumed it (user coroutine *or* pcall | ||
102 | -- coroutine!) | ||
103 | local pcall_previous = setmetatable({}, weak_meta) | ||
104 | -- reverse of `pcall_mainOf`. maps a user coroutine to the | ||
105 | -- currently active pcall coroutine started within it | ||
106 | local pcall_callOf = setmetatable({}, weak_meta) | ||
107 | -- similar to `pcall_mainOf` but is used only while executing | ||
108 | -- the error handler of xpcall (thus no nesting is necessary!) | ||
109 | local xpcall_running = setmetatable({}, weak_meta) | ||
110 | |||
111 | -- handle debug functions | ||
112 | if type(debug) == "table" then | ||
113 | local debug_getinfo = debug.getinfo | ||
114 | local debug_traceback = debug.traceback | ||
115 | |||
116 | if not is_luajit then | ||
117 | local function calculate_trace_level(co, level) | ||
118 | if level ~= nil then | ||
119 | for out = 1, 1/0 do | ||
120 | local info = (co==nil) and debug_getinfo(out, "") or debug_getinfo(co, out, "") | ||
121 | if info == nil then | ||
122 | local max = out-1 | ||
123 | if level <= max then | ||
124 | return level | ||
125 | end | ||
126 | return nil, level-max | ||
127 | end | ||
128 | end | ||
129 | end | ||
130 | return 1 | ||
131 | end | ||
132 | |||
133 | local stack_pattern = "\nstack traceback:" | ||
134 | local stack_replace = "" | ||
135 | function debug.traceback(co, msg, level) | ||
136 | local lvl | ||
137 | local nilmsg | ||
138 | if type(co) ~= "thread" then | ||
139 | co, msg, level = coroutine_running(), co, msg | ||
140 | end | ||
141 | if msg == nil then | ||
142 | msg = "" | ||
143 | nilmsg = true | ||
144 | elseif type(msg) ~= "string" then | ||
145 | return msg | ||
146 | end | ||
147 | if co == nil then | ||
148 | msg = debug_traceback(msg, level or 1) | ||
149 | else | ||
150 | local xpco = xpcall_running[co] | ||
151 | if xpco ~= nil then | ||
152 | lvl, level = calculate_trace_level(xpco, level) | ||
153 | if lvl then | ||
154 | msg = debug_traceback(xpco, msg, lvl) | ||
155 | else | ||
156 | msg = msg..stack_pattern | ||
157 | end | ||
158 | lvl, level = calculate_trace_level(co, level) | ||
159 | if lvl then | ||
160 | local trace = debug_traceback(co, "", lvl) | ||
161 | msg = msg..trace:gsub(stack_pattern, stack_replace) | ||
162 | end | ||
163 | else | ||
164 | co = pcall_callOf[co] or co | ||
165 | lvl, level = calculate_trace_level(co, level) | ||
166 | if lvl then | ||
167 | msg = debug_traceback(co, msg, lvl) | ||
168 | else | ||
169 | msg = msg..stack_pattern | ||
170 | end | ||
171 | end | ||
172 | co = pcall_previous[co] | ||
173 | while co ~= nil do | ||
174 | lvl, level = calculate_trace_level(co, level) | ||
175 | if lvl then | ||
176 | local trace = debug_traceback(co, "", lvl) | ||
177 | msg = msg..trace:gsub(stack_pattern, stack_replace) | ||
178 | end | ||
179 | co = pcall_previous[co] | ||
180 | end | ||
181 | end | ||
182 | if nilmsg then | ||
183 | msg = msg:gsub("^\n", "") | ||
184 | end | ||
185 | msg = msg:gsub("\n\t%(tail call%): %?", "\000") | ||
186 | msg = msg:gsub("\n\t%.%.%.\n", "\001\n") | ||
187 | msg = msg:gsub("\n\t%.%.%.$", "\001") | ||
188 | msg = msg:gsub("(%z+)\001(%z+)", function(some, other) | ||
189 | return "\n\t(..."..#some+#other.."+ tail call(s)...)" | ||
190 | end) | ||
191 | msg = msg:gsub("\001(%z+)", function(zeros) | ||
192 | return "\n\t(..."..#zeros.."+ tail call(s)...)" | ||
193 | end) | ||
194 | msg = msg:gsub("(%z+)\001", function(zeros) | ||
195 | return "\n\t(..."..#zeros.."+ tail call(s)...)" | ||
196 | end) | ||
197 | msg = msg:gsub("%z+", function(zeros) | ||
198 | return "\n\t(..."..#zeros.." tail call(s)...)" | ||
199 | end) | ||
200 | msg = msg:gsub("\001", function(zeros) | ||
201 | return "\n\t..." | ||
202 | end) | ||
203 | return msg | ||
204 | end | ||
205 | end -- is not luajit | ||
206 | end -- debug table available | ||
207 | |||
208 | |||
209 | local main_coroutine = coroutine_create(function() end) | ||
210 | |||
211 | if not is_luajit52 then | ||
212 | function M.coroutine.running() | ||
213 | local co = coroutine_running() | ||
214 | if co then | ||
215 | return pcall_mainOf[co] or co, false | ||
216 | else | ||
217 | return main_coroutine, true | ||
218 | end | ||
219 | end | ||
220 | end | ||
221 | |||
222 | if not is_luajit then | ||
223 | function M.coroutine.resume(co, ...) | ||
224 | if co == main_coroutine then | ||
225 | return false, "cannot resume non-suspended coroutine" | ||
226 | else | ||
227 | return coroutine_resume(co, ...) | ||
228 | end | ||
229 | end | ||
230 | |||
231 | function M.coroutine.status(co) | ||
232 | local notmain = coroutine_running() | ||
233 | if co == main_coroutine then | ||
234 | return notmain and "normal" or "running" | ||
235 | else | ||
236 | return coroutine_status(co) | ||
237 | end | ||
238 | end | ||
239 | |||
240 | local function pcall_results(current, call, success, ...) | ||
241 | if coroutine_status(call) == "suspended" then | ||
242 | return pcall_results(current, call, coroutine_resume(call, coroutine_yield(...))) | ||
243 | end | ||
244 | if pcall_previous then | ||
245 | pcall_previous[call] = nil | ||
246 | local main = pcall_mainOf[call] | ||
247 | if main == current then current = nil end | ||
248 | pcall_callOf[main] = current | ||
249 | end | ||
250 | pcall_mainOf[call] = nil | ||
251 | return success, ... | ||
252 | end | ||
253 | |||
254 | local function pcall_exec(current, call, ...) | ||
255 | local main = pcall_mainOf[current] or current | ||
256 | pcall_mainOf[call] = main | ||
257 | if pcall_previous then | ||
258 | pcall_previous[call] = current | ||
259 | pcall_callOf[main] = call | ||
260 | end | ||
261 | return pcall_results(current, call, coroutine_resume(call, ...)) | ||
262 | end | ||
263 | |||
264 | local coroutine_create52 = M.coroutine.create | ||
265 | |||
266 | local function pcall_coroutine(func) | ||
267 | if type(func) ~= "function" then | ||
268 | local callable = func | ||
269 | func = function (...) return callable(...) end | ||
270 | end | ||
271 | return coroutine_create52(func) | ||
272 | end | ||
273 | |||
274 | function M.pcall(func, ...) | ||
275 | local current = coroutine_running() | ||
276 | if not current then return pcall(func, ...) end | ||
277 | return pcall_exec(current, pcall_coroutine(func), ...) | ||
278 | end | ||
279 | |||
280 | local function xpcall_catch(current, call, msgh, success, ...) | ||
281 | if not success then | ||
282 | xpcall_running[current] = call | ||
283 | local ok, result = pcall(msgh, ...) | ||
284 | xpcall_running[current] = nil | ||
285 | if not ok then | ||
286 | return false, "error in error handling ("..tostring(result)..")" | ||
287 | end | ||
288 | return false, result | ||
289 | end | ||
290 | return true, ... | ||
291 | end | ||
292 | |||
293 | function M.xpcall(f, msgh, ...) | ||
294 | local current = coroutine_running() | ||
295 | if not current then | ||
296 | local args, n = { ... }, select('#', ...) | ||
297 | return xpcall(function() return f(unpack(args, 1, n)) end, msgh) | ||
298 | end | ||
299 | local call = pcall_coroutine(f) | ||
300 | return xpcall_catch(current, call, msgh, pcall_exec(current, call, ...)) | ||
301 | end | ||
302 | end -- not luajit | ||
303 | |||
304 | end -- lua == 5.1 | ||
305 | |||
306 | |||
307 | -- handle exporting to global scope | ||
308 | local function extend_table(from, to) | ||
309 | if from ~= to then | ||
310 | for k,v in pairs(from) do | ||
311 | if type(v) == "table" and | ||
312 | type(to[k]) == "table" and | ||
313 | v ~= to[k] then | ||
314 | extend_table(v, to[k]) | ||
315 | else | ||
316 | to[k] = v | ||
317 | end | ||
318 | end | ||
319 | end | ||
320 | end | ||
321 | |||
322 | extend_table(M, _G) | ||
323 | |||
324 | -- vi: set expandtab softtabstop=3 shiftwidth=3 : | ||
diff --git a/compat53.lua b/compat53/module.lua index 919ab33..0dd0317 100644 --- a/compat53.lua +++ b/compat53/module.lua | |||
@@ -1,8 +1,31 @@ | |||
1 | local _G, _VERSION = _G, _VERSION | ||
1 | local lua_version = _VERSION:sub(-3) | 2 | local lua_version = _VERSION:sub(-3) |
2 | 3 | ||
4 | |||
5 | local M = _G | ||
6 | |||
3 | if lua_version < "5.3" then | 7 | if lua_version < "5.3" then |
4 | -- local aliases for commonly used functions | 8 | |
5 | local type, select, error = type, select, error | 9 | -- cache globals in upvalues |
10 | local error, ipairs, pairs, pcall, require, select, setmetatable, type = | ||
11 | error, ipairs, pairs, pcall, require, select, setmetatable, type | ||
12 | local debug, math, package, string, table = | ||
13 | debug, math, package, string, table | ||
14 | |||
15 | -- create module table | ||
16 | M = {} | ||
17 | local M_meta = { | ||
18 | __index = _G, | ||
19 | -- __newindex is set at the end | ||
20 | } | ||
21 | setmetatable(M, M_meta) | ||
22 | |||
23 | -- create subtables | ||
24 | M.math = setmetatable({}, { __index = math }) | ||
25 | M.string = setmetatable({}, { __index = string }) | ||
26 | M.table = setmetatable({}, { __index = table }) | ||
27 | M.utf8 = {} | ||
28 | |||
6 | 29 | ||
7 | -- select the most powerful getmetatable function available | 30 | -- select the most powerful getmetatable function available |
8 | local gmt = type(debug) == "table" and debug.getmetatable or | 31 | local gmt = type(debug) == "table" and debug.getmetatable or |
@@ -29,11 +52,13 @@ if lua_version < "5.3" then | |||
29 | -- load utf8 library | 52 | -- load utf8 library |
30 | local utf8_ok, utf8lib = pcall(require, "compat53.utf8") | 53 | local utf8_ok, utf8lib = pcall(require, "compat53.utf8") |
31 | if utf8_ok then | 54 | if utf8_ok then |
32 | utf8 = utf8lib | ||
33 | package.loaded["utf8"] = utf8lib | ||
34 | if lua_version == "5.1" then | 55 | if lua_version == "5.1" then |
35 | utf8lib.charpattern = "[%z\1-\127\194-\244][\128-\191]*" | 56 | utf8lib.charpattern = "[%z\1-\127\194-\244][\128-\191]*" |
36 | end | 57 | end |
58 | for k,v in pairs(utf8lib) do | ||
59 | M.utf8[k] = v | ||
60 | end | ||
61 | package.loaded["utf8"] = M.utf8 | ||
37 | end | 62 | end |
38 | 63 | ||
39 | 64 | ||
@@ -41,7 +66,7 @@ if lua_version < "5.3" then | |||
41 | local table_ok, tablib = pcall(require, "compat53.table") | 66 | local table_ok, tablib = pcall(require, "compat53.table") |
42 | if table_ok then | 67 | if table_ok then |
43 | for k,v in pairs(tablib) do | 68 | for k,v in pairs(tablib) do |
44 | table[k] = v | 69 | M.table[k] = v |
45 | end | 70 | end |
46 | end | 71 | end |
47 | 72 | ||
@@ -50,7 +75,7 @@ if lua_version < "5.3" then | |||
50 | local str_ok, strlib = pcall(require, "compat53.string") | 75 | local str_ok, strlib = pcall(require, "compat53.string") |
51 | if str_ok then | 76 | if str_ok then |
52 | for k,v in pairs(strlib) do | 77 | for k,v in pairs(strlib) do |
53 | string[k] = v | 78 | M.string[k] = v |
54 | end | 79 | end |
55 | end | 80 | end |
56 | 81 | ||
@@ -60,9 +85,9 @@ if lua_version < "5.3" then | |||
60 | if not str_ok then | 85 | if not str_ok then |
61 | local struct_ok, struct = pcall(require, "struct") | 86 | local struct_ok, struct = pcall(require, "struct") |
62 | if struct_ok then | 87 | if struct_ok then |
63 | string.pack = struct.pack | 88 | M.string.pack = struct.pack |
64 | string.packsize = struct.size | 89 | M.string.packsize = struct.size |
65 | string.unpack = struct.unpack | 90 | M.string.unpack = struct.unpack |
66 | end | 91 | end |
67 | end | 92 | end |
68 | 93 | ||
@@ -81,17 +106,17 @@ if lua_version < "5.3" then | |||
81 | maxint = maxint | 106 | maxint = maxint |
82 | minint = -maxint | 107 | minint = -maxint |
83 | end | 108 | end |
84 | math.maxinteger = maxint | 109 | M.math.maxinteger = maxint |
85 | math.mininteger = minint | 110 | M.math.mininteger = minint |
86 | 111 | ||
87 | function math.tointeger(n) | 112 | function M.math.tointeger(n) |
88 | if type(n) == "number" and n <= maxint and n >= minint and n % 1 == 0 then | 113 | if type(n) == "number" and n <= maxint and n >= minint and n % 1 == 0 then |
89 | return n | 114 | return n |
90 | end | 115 | end |
91 | return nil | 116 | return nil |
92 | end | 117 | end |
93 | 118 | ||
94 | function math.type(n) | 119 | function M.math.type(n) |
95 | if type(n) == "number" then | 120 | if type(n) == "number" then |
96 | if n <= maxint and n >= minint and n % 1 == 0 then | 121 | if n <= maxint and n >= minint and n % 1 == 0 then |
97 | return "integer" | 122 | return "integer" |
@@ -116,7 +141,7 @@ if lua_version < "5.3" then | |||
116 | end | 141 | end |
117 | end | 142 | end |
118 | 143 | ||
119 | function math.ult(m, n) | 144 | function M.math.ult(m, n) |
120 | m = checkinteger(m, "1", "math.ult") | 145 | m = checkinteger(m, "1", "math.ult") |
121 | n = checkinteger(n, "2", "math.ult") | 146 | n = checkinteger(n, "2", "math.ult") |
122 | if m >= 0 and n < 0 then | 147 | if m >= 0 and n < 0 then |
@@ -132,7 +157,6 @@ if lua_version < "5.3" then | |||
132 | 157 | ||
133 | -- ipairs should respect __index metamethod | 158 | -- ipairs should respect __index metamethod |
134 | do | 159 | do |
135 | local _ipairs = ipairs | ||
136 | local function ipairs_iterator(st, var) | 160 | local function ipairs_iterator(st, var) |
137 | var = var + 1 | 161 | var = var + 1 |
138 | local val = st[var] | 162 | local val = st[var] |
@@ -140,11 +164,11 @@ if lua_version < "5.3" then | |||
140 | return var, st[var] | 164 | return var, st[var] |
141 | end | 165 | end |
142 | end | 166 | end |
143 | function ipairs(t) | 167 | function M.ipairs(t) |
144 | if gmt(t) ~= nil then -- t has metatable | 168 | if gmt(t) ~= nil then -- t has metatable |
145 | return ipairs_iterator, t, 0 | 169 | return ipairs_iterator, t, 0 |
146 | else | 170 | else |
147 | return _ipairs(t) | 171 | return ipairs(t) |
148 | end | 172 | end |
149 | end | 173 | end |
150 | end | 174 | end |
@@ -153,7 +177,12 @@ if lua_version < "5.3" then | |||
153 | -- update table library (if C module not available) | 177 | -- update table library (if C module not available) |
154 | if not table_ok then | 178 | if not table_ok then |
155 | local table_concat = table.concat | 179 | local table_concat = table.concat |
156 | function table.concat(list, sep, i, j) | 180 | local table_insert = table.insert |
181 | local table_remove = table.remove | ||
182 | local table_sort = table.sort | ||
183 | local table_unpack = lua_version == "5.1" and unpack or table.unpack | ||
184 | |||
185 | function M.table.concat(list, sep, i, j) | ||
157 | local mt = gmt(list) | 186 | local mt = gmt(list) |
158 | if type(mt) == "table" and type(mt.__len) == "function" then | 187 | if type(mt) == "table" and type(mt.__len) == "function" then |
159 | local src = list | 188 | local src = list |
@@ -165,8 +194,7 @@ if lua_version < "5.3" then | |||
165 | return table_concat(list, sep, i, j) | 194 | return table_concat(list, sep, i, j) |
166 | end | 195 | end |
167 | 196 | ||
168 | local table_insert = table.insert | 197 | function M.table.insert(list, ...) |
169 | function table.insert(list, ...) | ||
170 | local mt = gmt(list) | 198 | local mt = gmt(list) |
171 | local has_mt = type(mt) == "table" | 199 | local has_mt = type(mt) == "table" |
172 | local has_len = has_mt and type(mt.__len) == "function" | 200 | local has_len = has_mt and type(mt.__len) == "function" |
@@ -191,7 +219,7 @@ if lua_version < "5.3" then | |||
191 | end | 219 | end |
192 | end | 220 | end |
193 | 221 | ||
194 | function table.move(a1, f, e, t, a2) | 222 | function M.table.move(a1, f, e, t, a2) |
195 | a2 = a2 or a1 | 223 | a2 = a2 or a1 |
196 | f = checkinteger(f, "2", "table.move") | 224 | f = checkinteger(f, "2", "table.move") |
197 | argcheck(f > 0, "2", "table.move", | 225 | argcheck(f > 0, "2", "table.move", |
@@ -208,8 +236,7 @@ if lua_version < "5.3" then | |||
208 | return a2 | 236 | return a2 |
209 | end | 237 | end |
210 | 238 | ||
211 | local table_remove = table.remove | 239 | function M.table.remove(list, pos) |
212 | function table.remove(list, pos) | ||
213 | local mt = gmt(list) | 240 | local mt = gmt(list) |
214 | local has_mt = type(mt) == "table" | 241 | local has_mt = type(mt) == "table" |
215 | local has_len = has_mt and type(mt.__len) == "function" | 242 | local has_len = has_mt and type(mt.__len) == "function" |
@@ -282,8 +309,7 @@ if lua_version < "5.3" then | |||
282 | end | 309 | end |
283 | end | 310 | end |
284 | 311 | ||
285 | local table_sort = table.sort | 312 | function M.table.sort(list, cmp) |
286 | function table.sort(list, cmp) | ||
287 | local mt = gmt(list) | 313 | local mt = gmt(list) |
288 | local has_mt = type(mt) == "table" | 314 | local has_mt = type(mt) == "table" |
289 | local has_len = has_mt and type(mt.__len) == "function" | 315 | local has_len = has_mt and type(mt.__len) == "function" |
@@ -297,7 +323,6 @@ if lua_version < "5.3" then | |||
297 | end | 323 | end |
298 | end | 324 | end |
299 | 325 | ||
300 | local table_unpack = lua_version == "5.1" and unpack or table.unpack | ||
301 | local function unpack_helper(list, i, j, ...) | 326 | local function unpack_helper(list, i, j, ...) |
302 | if j < i then | 327 | if j < i then |
303 | return ... | 328 | return ... |
@@ -305,7 +330,7 @@ if lua_version < "5.3" then | |||
305 | return unpack_helper(list, i, j-1, list[j], ...) | 330 | return unpack_helper(list, i, j-1, list[j], ...) |
306 | end | 331 | end |
307 | end | 332 | end |
308 | function table.unpack(list, i, j) | 333 | function M.table.unpack(list, i, j) |
309 | local mt = gmt(list) | 334 | local mt = gmt(list) |
310 | local has_mt = type(mt) == "table" | 335 | local has_mt = type(mt) == "table" |
311 | local has_len = has_mt and type(mt.__len) == "function" | 336 | local has_len = has_mt and type(mt.__len) == "function" |
@@ -319,7 +344,6 @@ if lua_version < "5.3" then | |||
319 | end -- update table library | 344 | end -- update table library |
320 | 345 | ||
321 | 346 | ||
322 | |||
323 | -- bring Lua 5.1 (and LuaJIT) up to speed with Lua 5.2 | 347 | -- bring Lua 5.1 (and LuaJIT) up to speed with Lua 5.2 |
324 | if lua_version == "5.1" then | 348 | if lua_version == "5.1" then |
325 | -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag) | 349 | -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag) |
@@ -327,31 +351,45 @@ if lua_version < "5.3" then | |||
327 | local is_luajit52 = is_luajit and | 351 | local is_luajit52 = is_luajit and |
328 | #setmetatable({}, { __len = function() return 1 end }) == 1 | 352 | #setmetatable({}, { __len = function() return 1 end }) == 1 |
329 | 353 | ||
330 | 354 | -- cache globals in upvalues | |
331 | -- the (x)pcall implementations start a new coroutine internally | 355 | local load, loadfile, loadstring, setfenv, unpack, xpcall = |
332 | -- to allow yielding even in Lua 5.1. to allow for accurate | 356 | load, loadfile, loadstring, setfenv, unpack, xpcall |
333 | -- stack traces we keep track of the nested coroutine activations | 357 | local coroutine, io, os = coroutine, io, os |
334 | -- in the weak tables below: | 358 | local coroutine_create = coroutine.create |
335 | local weak_meta = { __mode = "kv" } | 359 | local coroutine_resume = coroutine.resume |
336 | -- table that maps each running coroutine started by pcall to | ||
337 | -- the coroutine that resumed it (user coroutine *or* pcall | ||
338 | -- coroutine!) | ||
339 | local pcall_previous = setmetatable({}, weak_meta) | ||
340 | -- reverse of `pcall_mainOf`. maps a user coroutine to the | ||
341 | -- currently active pcall coroutine started within it | ||
342 | local pcall_callOf = setmetatable({}, weak_meta) | ||
343 | -- similar to `pcall_mainOf` but is used only while executing | ||
344 | -- the error handler of xpcall (thus no nesting is necessary!) | ||
345 | local xpcall_running = setmetatable({}, weak_meta) | ||
346 | local coroutine_running = coroutine.running | 360 | local coroutine_running = coroutine.running |
361 | local coroutine_status = coroutine.status | ||
362 | local coroutine_yield = coroutine.yield | ||
363 | local io_input = io.input | ||
364 | local io_open = io.open | ||
365 | local io_output = io.output | ||
366 | local io_write = io.write | ||
367 | local math_log = math.log | ||
368 | local os_execute = os.execute | ||
369 | local string_find = string.find | ||
370 | local string_format = string.format | ||
371 | local string_gmatch = string.gmatch | ||
372 | local string_gsub = string.gsub | ||
373 | local string_match = string.match | ||
374 | local string_rep = string.rep | ||
375 | local table_concat = table.concat | ||
376 | |||
377 | -- create subtables | ||
378 | M.coroutine = setmetatable({}, { __index = coroutine }) | ||
379 | M.io = setmetatable({}, { __index = io }) | ||
380 | M.os = setmetatable({}, { __index = os }) | ||
381 | M.package = setmetatable({}, { __index = package }) | ||
347 | 382 | ||
348 | -- handle debug functions | 383 | -- handle debug functions |
349 | if type(debug) == "table" then | 384 | if type(debug) == "table" then |
385 | local debug_setfenv = debug.setfenv | ||
386 | local debug_getfenv = debug.getfenv | ||
387 | local debug_setmetatable = debug.setmetatable | ||
388 | |||
389 | M.debug = setmetatable({}, { __index = debug }) | ||
350 | 390 | ||
351 | if not is_luajit52 then | 391 | if not is_luajit52 then |
352 | local _G, package = _G, package | 392 | function M.debug.setuservalue(obj, value) |
353 | local debug_setfenv = debug.setfenv | ||
354 | function debug.setuservalue(obj, value) | ||
355 | if type(obj) ~= "userdata" then | 393 | if type(obj) ~= "userdata" then |
356 | error("bad argument #1 to 'setuservalue' (userdata expected, got ".. | 394 | error("bad argument #1 to 'setuservalue' (userdata expected, got ".. |
357 | type(obj)..")", 2) | 395 | type(obj)..")", 2) |
@@ -364,8 +402,7 @@ if lua_version < "5.3" then | |||
364 | return debug_setfenv(obj, value) | 402 | return debug_setfenv(obj, value) |
365 | end | 403 | end |
366 | 404 | ||
367 | local debug_getfenv = debug.getfenv | 405 | function M.debug.getuservalue(obj) |
368 | function debug.getuservalue(obj) | ||
369 | if type(obj) ~= "userdata" then | 406 | if type(obj) ~= "userdata" then |
370 | return nil | 407 | return nil |
371 | else | 408 | else |
@@ -377,116 +414,21 @@ if lua_version < "5.3" then | |||
377 | end | 414 | end |
378 | end | 415 | end |
379 | 416 | ||
380 | local debug_setmetatable = debug.setmetatable | 417 | function M.debug.setmetatable(value, tab) |
381 | function debug.setmetatable(value, tab) | ||
382 | debug_setmetatable(value, tab) | 418 | debug_setmetatable(value, tab) |
383 | return value | 419 | return value |
384 | end | 420 | end |
385 | end -- not luajit with compat52 enabled | 421 | end -- not luajit with compat52 enabled |
386 | |||
387 | if not is_luajit then | ||
388 | local debug_getinfo = debug.getinfo | ||
389 | local function calculate_trace_level(co, level) | ||
390 | if level ~= nil then | ||
391 | for out = 1, 1/0 do | ||
392 | local info = (co==nil) and debug_getinfo(out, "") or debug_getinfo(co, out, "") | ||
393 | if info == nil then | ||
394 | local max = out-1 | ||
395 | if level <= max then | ||
396 | return level | ||
397 | end | ||
398 | return nil, level-max | ||
399 | end | ||
400 | end | ||
401 | end | ||
402 | return 1 | ||
403 | end | ||
404 | |||
405 | local stack_pattern = "\nstack traceback:" | ||
406 | local stack_replace = "" | ||
407 | local debug_traceback = debug.traceback | ||
408 | function debug.traceback(co, msg, level) | ||
409 | local lvl | ||
410 | local nilmsg | ||
411 | if type(co) ~= "thread" then | ||
412 | co, msg, level = coroutine_running(), co, msg | ||
413 | end | ||
414 | if msg == nil then | ||
415 | msg = "" | ||
416 | nilmsg = true | ||
417 | elseif type(msg) ~= "string" then | ||
418 | return msg | ||
419 | end | ||
420 | if co == nil then | ||
421 | msg = debug_traceback(msg, level or 1) | ||
422 | else | ||
423 | local xpco = xpcall_running[co] | ||
424 | if xpco ~= nil then | ||
425 | lvl, level = calculate_trace_level(xpco, level) | ||
426 | if lvl then | ||
427 | msg = debug_traceback(xpco, msg, lvl) | ||
428 | else | ||
429 | msg = msg..stack_pattern | ||
430 | end | ||
431 | lvl, level = calculate_trace_level(co, level) | ||
432 | if lvl then | ||
433 | local trace = debug_traceback(co, "", lvl) | ||
434 | msg = msg..trace:gsub(stack_pattern, stack_replace) | ||
435 | end | ||
436 | else | ||
437 | co = pcall_callOf[co] or co | ||
438 | lvl, level = calculate_trace_level(co, level) | ||
439 | if lvl then | ||
440 | msg = debug_traceback(co, msg, lvl) | ||
441 | else | ||
442 | msg = msg..stack_pattern | ||
443 | end | ||
444 | end | ||
445 | co = pcall_previous[co] | ||
446 | while co ~= nil do | ||
447 | lvl, level = calculate_trace_level(co, level) | ||
448 | if lvl then | ||
449 | local trace = debug_traceback(co, "", lvl) | ||
450 | msg = msg..trace:gsub(stack_pattern, stack_replace) | ||
451 | end | ||
452 | co = pcall_previous[co] | ||
453 | end | ||
454 | end | ||
455 | if nilmsg then | ||
456 | msg = msg:gsub("^\n", "") | ||
457 | end | ||
458 | msg = msg:gsub("\n\t%(tail call%): %?", "\000") | ||
459 | msg = msg:gsub("\n\t%.%.%.\n", "\001\n") | ||
460 | msg = msg:gsub("\n\t%.%.%.$", "\001") | ||
461 | msg = msg:gsub("(%z+)\001(%z+)", function(some, other) | ||
462 | return "\n\t(..."..#some+#other.."+ tail call(s)...)" | ||
463 | end) | ||
464 | msg = msg:gsub("\001(%z+)", function(zeros) | ||
465 | return "\n\t(..."..#zeros.."+ tail call(s)...)" | ||
466 | end) | ||
467 | msg = msg:gsub("(%z+)\001", function(zeros) | ||
468 | return "\n\t(..."..#zeros.."+ tail call(s)...)" | ||
469 | end) | ||
470 | msg = msg:gsub("%z+", function(zeros) | ||
471 | return "\n\t(..."..#zeros.." tail call(s)...)" | ||
472 | end) | ||
473 | msg = msg:gsub("\001", function(zeros) | ||
474 | return "\n\t..." | ||
475 | end) | ||
476 | return msg | ||
477 | end | ||
478 | end -- is not luajit | ||
479 | end -- debug table available | 422 | end -- debug table available |
480 | 423 | ||
481 | 424 | ||
482 | if not is_luajit52 then | 425 | if not is_luajit52 then |
483 | local _pairs = pairs | 426 | function M.pairs(t) |
484 | function pairs(t) | ||
485 | local mt = gmt(t) | 427 | local mt = gmt(t) |
486 | if type(mt) == "table" and type(mt.__pairs) == "function" then | 428 | if type(mt) == "table" and type(mt.__pairs) == "function" then |
487 | return mt.__pairs(t) | 429 | return mt.__pairs(t) |
488 | else | 430 | else |
489 | return _pairs(t) | 431 | return pairs(t) |
490 | end | 432 | end |
491 | end | 433 | end |
492 | end | 434 | end |
@@ -506,9 +448,7 @@ if lua_version < "5.3" then | |||
506 | end | 448 | end |
507 | end | 449 | end |
508 | 450 | ||
509 | local setfenv = setfenv | 451 | function M.load(ld, source, mode, env) |
510 | local _load, _loadstring = load, loadstring | ||
511 | function load(ld, source, mode, env) | ||
512 | mode = mode or "bt" | 452 | mode = mode or "bt" |
513 | local chunk, msg | 453 | local chunk, msg |
514 | if type( ld ) == "string" then | 454 | if type( ld ) == "string" then |
@@ -516,7 +456,7 @@ if lua_version < "5.3" then | |||
516 | local merr = check_mode(mode, ld) | 456 | local merr = check_mode(mode, ld) |
517 | if merr then return nil, merr end | 457 | if merr then return nil, merr end |
518 | end | 458 | end |
519 | chunk, msg = _loadstring(ld, source) | 459 | chunk, msg = loadstring(ld, source) |
520 | else | 460 | else |
521 | local ld_type = type(ld) | 461 | local ld_type = type(ld) |
522 | if ld_type ~= "function" then | 462 | if ld_type ~= "function" then |
@@ -536,10 +476,10 @@ if lua_version < "5.3" then | |||
536 | return v | 476 | return v |
537 | end | 477 | end |
538 | end | 478 | end |
539 | chunk, msg = _load(checked_ld, source) | 479 | chunk, msg = load(checked_ld, source) |
540 | if merr then return nil, merr end | 480 | if merr then return nil, merr end |
541 | else | 481 | else |
542 | chunk, msg = _load(ld, source) | 482 | chunk, msg = load(ld, source) |
543 | end | 483 | end |
544 | end | 484 | end |
545 | if not chunk then | 485 | if not chunk then |
@@ -551,11 +491,9 @@ if lua_version < "5.3" then | |||
551 | return chunk | 491 | return chunk |
552 | end | 492 | end |
553 | 493 | ||
554 | loadstring = load | 494 | M.loadstring = load |
555 | 495 | ||
556 | local _loadfile = loadfile | 496 | function M.loadfile(file, mode, env) |
557 | local io_open = io.open | ||
558 | function loadfile(file, mode, env) | ||
559 | mode = mode or "bt" | 497 | mode = mode or "bt" |
560 | if mode ~= "bt" then | 498 | if mode ~= "bt" then |
561 | local f = io_open(file, "rb") | 499 | local f = io_open(file, "rb") |
@@ -568,7 +506,7 @@ if lua_version < "5.3" then | |||
568 | end | 506 | end |
569 | end | 507 | end |
570 | end | 508 | end |
571 | local chunk, msg = _loadfile(file) | 509 | local chunk, msg = loadfile(file) |
572 | if not chunk then | 510 | if not chunk then |
573 | return chunk, msg | 511 | return chunk, msg |
574 | end | 512 | end |
@@ -581,7 +519,7 @@ if lua_version < "5.3" then | |||
581 | 519 | ||
582 | 520 | ||
583 | if not is_luajit52 then | 521 | if not is_luajit52 then |
584 | function rawlen(v) | 522 | function M.rawlen(v) |
585 | local t = type(v) | 523 | local t = type(v) |
586 | if t ~= "string" and t ~= "table" then | 524 | if t ~= "string" and t ~= "table" then |
587 | error("bad argument #1 to 'rawlen' (table or string expected)", 2) | 525 | error("bad argument #1 to 'rawlen' (table or string expected)", 2) |
@@ -591,33 +529,38 @@ if lua_version < "5.3" then | |||
591 | end | 529 | end |
592 | 530 | ||
593 | 531 | ||
532 | if not is_luajit then | ||
533 | function M.xpcall(f, msgh, ...) | ||
534 | local args, n = { ... }, select('#', ...) | ||
535 | return xpcall(function() return f(unpack(args, 1, n)) end, msgh) | ||
536 | end | ||
537 | end | ||
538 | |||
539 | |||
594 | if not is_luajit52 then | 540 | if not is_luajit52 then |
595 | local os_execute = os.execute | 541 | function M.os.execute(cmd) |
596 | function os.execute(cmd) | ||
597 | local code = os_execute(cmd) | 542 | local code = os_execute(cmd) |
598 | -- Lua 5.1 does not report exit by signal. | 543 | -- Lua 5.1 does not report exit by signal. |
599 | if code == 0 then | 544 | if code == 0 then |
600 | return true, "exit", code | 545 | return true, "exit", code |
601 | else | 546 | else |
602 | return nil, "exit", code/256 -- only correct on POSIX! | 547 | return nil, "exit", code/256 -- only correct on Linux! |
603 | end | 548 | end |
604 | end | 549 | end |
605 | end | 550 | end |
606 | 551 | ||
607 | 552 | ||
608 | if not table_ok and not is_luajit52 then | 553 | if not table_ok and not is_luajit52 then |
609 | table.pack = function(...) | 554 | M.table.pack = function(...) |
610 | return { n = select('#', ...), ... } | 555 | return { n = select('#', ...), ... } |
611 | end | 556 | end |
612 | end | 557 | end |
613 | 558 | ||
614 | 559 | ||
615 | local main_coroutine = coroutine.create(function() end) | 560 | local main_coroutine = coroutine_create(function() end) |
616 | 561 | ||
617 | local _pcall = pcall | 562 | function M.coroutine.create(func) |
618 | local coroutine_create = coroutine.create | 563 | local success, result = pcall(coroutine_create, func) |
619 | function coroutine.create(func) | ||
620 | local success, result = _pcall(coroutine_create, func) | ||
621 | if not success then | 564 | if not success then |
622 | if type(func) ~= "function" then | 565 | if type(func) ~= "function" then |
623 | error("bad argument #1 (function expected)", 0) | 566 | error("bad argument #1 (function expected)", 0) |
@@ -627,23 +570,18 @@ if lua_version < "5.3" then | |||
627 | return result | 570 | return result |
628 | end | 571 | end |
629 | 572 | ||
630 | -- maps the internal pcall coroutines to the user coroutine that | ||
631 | -- *should* be running if pcall didn't use coroutines internally | ||
632 | local pcall_mainOf = setmetatable({}, weak_meta) | ||
633 | |||
634 | if not is_luajit52 then | 573 | if not is_luajit52 then |
635 | function coroutine.running() | 574 | function M.coroutine.running() |
636 | local co = coroutine_running() | 575 | local co = coroutine_running() |
637 | if co then | 576 | if co then |
638 | return pcall_mainOf[co] or co, false | 577 | return co, false |
639 | else | 578 | else |
640 | return main_coroutine, true | 579 | return main_coroutine, true |
641 | end | 580 | end |
642 | end | 581 | end |
643 | end | 582 | end |
644 | 583 | ||
645 | local coroutine_yield = coroutine.yield | 584 | function M.coroutine.yield(...) |
646 | function coroutine.yield(...) | ||
647 | local co, flag = coroutine_running() | 585 | local co, flag = coroutine_running() |
648 | if co and not flag then | 586 | if co and not flag then |
649 | return coroutine_yield(...) | 587 | return coroutine_yield(...) |
@@ -653,8 +591,7 @@ if lua_version < "5.3" then | |||
653 | end | 591 | end |
654 | 592 | ||
655 | if not is_luajit then | 593 | if not is_luajit then |
656 | local coroutine_resume = coroutine.resume | 594 | function M.coroutine.resume(co, ...) |
657 | function coroutine.resume(co, ...) | ||
658 | if co == main_coroutine then | 595 | if co == main_coroutine then |
659 | return false, "cannot resume non-suspended coroutine" | 596 | return false, "cannot resume non-suspended coroutine" |
660 | else | 597 | else |
@@ -662,8 +599,7 @@ if lua_version < "5.3" then | |||
662 | end | 599 | end |
663 | end | 600 | end |
664 | 601 | ||
665 | local coroutine_status = coroutine.status | 602 | function M.coroutine.status(co) |
666 | function coroutine.status(co) | ||
667 | local notmain = coroutine_running() | 603 | local notmain = coroutine_running() |
668 | if co == main_coroutine then | 604 | if co == main_coroutine then |
669 | return notmain and "normal" or "running" | 605 | return notmain and "normal" or "running" |
@@ -671,73 +607,11 @@ if lua_version < "5.3" then | |||
671 | return coroutine_status(co) | 607 | return coroutine_status(co) |
672 | end | 608 | end |
673 | end | 609 | end |
674 | |||
675 | local function pcall_results(current, call, success, ...) | ||
676 | if coroutine_status(call) == "suspended" then | ||
677 | return pcall_results(current, call, coroutine_resume(call, coroutine_yield(...))) | ||
678 | end | ||
679 | if pcall_previous then | ||
680 | pcall_previous[call] = nil | ||
681 | local main = pcall_mainOf[call] | ||
682 | if main == current then current = nil end | ||
683 | pcall_callOf[main] = current | ||
684 | end | ||
685 | pcall_mainOf[call] = nil | ||
686 | return success, ... | ||
687 | end | ||
688 | local function pcall_exec(current, call, ...) | ||
689 | local main = pcall_mainOf[current] or current | ||
690 | pcall_mainOf[call] = main | ||
691 | if pcall_previous then | ||
692 | pcall_previous[call] = current | ||
693 | pcall_callOf[main] = call | ||
694 | end | ||
695 | return pcall_results(current, call, coroutine_resume(call, ...)) | ||
696 | end | ||
697 | local coroutine_create52 = coroutine.create | ||
698 | local function pcall_coroutine(func) | ||
699 | if type(func) ~= "function" then | ||
700 | local callable = func | ||
701 | func = function (...) return callable(...) end | ||
702 | end | ||
703 | return coroutine_create52(func) | ||
704 | end | ||
705 | function pcall(func, ...) | ||
706 | local current = coroutine_running() | ||
707 | if not current then return _pcall(func, ...) end | ||
708 | return pcall_exec(current, pcall_coroutine(func), ...) | ||
709 | end | ||
710 | |||
711 | local _tostring = tostring | ||
712 | local function xpcall_catch(current, call, msgh, success, ...) | ||
713 | if not success then | ||
714 | xpcall_running[current] = call | ||
715 | local ok, result = _pcall(msgh, ...) | ||
716 | xpcall_running[current] = nil | ||
717 | if not ok then | ||
718 | return false, "error in error handling (".._tostring(result)..")" | ||
719 | end | ||
720 | return false, result | ||
721 | end | ||
722 | return true, ... | ||
723 | end | ||
724 | local _xpcall = xpcall | ||
725 | local _unpack = unpack | ||
726 | function xpcall(f, msgh, ...) | ||
727 | local current = coroutine_running() | ||
728 | if not current then | ||
729 | local args, n = { ... }, select('#', ...) | ||
730 | return _xpcall(function() return f(_unpack(args, 1, n)) end, msgh) | ||
731 | end | ||
732 | local call = pcall_coroutine(f) | ||
733 | return xpcall_catch(current, call, msgh, pcall_exec(current, call, ...)) | ||
734 | end | ||
735 | end -- not luajit | 610 | end -- not luajit |
736 | 611 | ||
737 | 612 | ||
738 | if not is_luajit then | 613 | if not is_luajit then |
739 | local math_log = math.log | 614 | M.math.log = function(x, base) |
740 | math.log = function(x, base) | ||
741 | if base ~= nil then | 615 | if base ~= nil then |
742 | return math_log(x)/math_log(base) | 616 | return math_log(x)/math_log(base) |
743 | else | 617 | else |
@@ -747,11 +621,8 @@ if lua_version < "5.3" then | |||
747 | end | 621 | end |
748 | 622 | ||
749 | 623 | ||
750 | local package = package | ||
751 | if not is_luajit then | 624 | if not is_luajit then |
752 | local io_open = io.open | 625 | function M.package.searchpath(name, path, sep, rep) |
753 | local table_concat = table.concat | ||
754 | function package.searchpath(name, path, sep, rep) | ||
755 | sep = (sep or "."):gsub("(%p)", "%%%1") | 626 | sep = (sep or "."):gsub("(%p)", "%%%1") |
756 | rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1") | 627 | rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1") |
757 | local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1") | 628 | local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1") |
@@ -769,48 +640,29 @@ if lua_version < "5.3" then | |||
769 | end | 640 | end |
770 | end | 641 | end |
771 | 642 | ||
772 | local p_index = { searchers = package.loaders } | ||
773 | local rawset = rawset | ||
774 | setmetatable(package, { | ||
775 | __index = p_index, | ||
776 | __newindex = function(p, k, v) | ||
777 | if k == "searchers" then | ||
778 | rawset(p, "loaders", v) | ||
779 | p_index.searchers = v | ||
780 | else | ||
781 | rawset(p, k, v) | ||
782 | end | ||
783 | end | ||
784 | }) | ||
785 | |||
786 | 643 | ||
787 | local string_gsub = string.gsub | ||
788 | local function fix_pattern(pattern) | 644 | local function fix_pattern(pattern) |
789 | return (string_gsub(pattern, "%z", "%%z")) | 645 | return (string_gsub(pattern, "%z", "%%z")) |
790 | end | 646 | end |
791 | 647 | ||
792 | local string_find = string.find | 648 | function M.string.find(s, pattern, ...) |
793 | function string.find(s, pattern, ...) | ||
794 | return string_find(s, fix_pattern(pattern), ...) | 649 | return string_find(s, fix_pattern(pattern), ...) |
795 | end | 650 | end |
796 | 651 | ||
797 | local string_gmatch = string.gmatch | 652 | function M.string.gmatch(s, pattern) |
798 | function string.gmatch(s, pattern) | ||
799 | return string_gmatch(s, fix_pattern(pattern)) | 653 | return string_gmatch(s, fix_pattern(pattern)) |
800 | end | 654 | end |
801 | 655 | ||
802 | function string.gsub(s, pattern, ...) | 656 | function M.string.gsub(s, pattern, ...) |
803 | return string_gsub(s, fix_pattern(pattern), ...) | 657 | return string_gsub(s, fix_pattern(pattern), ...) |
804 | end | 658 | end |
805 | 659 | ||
806 | local string_match = string.match | 660 | function M.string.match(s, pattern, ...) |
807 | function string.match(s, pattern, ...) | ||
808 | return string_match(s, fix_pattern(pattern), ...) | 661 | return string_match(s, fix_pattern(pattern), ...) |
809 | end | 662 | end |
810 | 663 | ||
811 | if not is_luajit then | 664 | if not is_luajit then |
812 | local string_rep = string.rep | 665 | function M.string.rep(s, n, sep) |
813 | function string.rep(s, n, sep) | ||
814 | if sep ~= nil and sep ~= "" and n >= 2 then | 666 | if sep ~= nil and sep ~= "" and n >= 2 then |
815 | return s .. string_rep(sep..s, n-1) | 667 | return s .. string_rep(sep..s, n-1) |
816 | else | 668 | else |
@@ -820,7 +672,6 @@ if lua_version < "5.3" then | |||
820 | end | 672 | end |
821 | 673 | ||
822 | if not is_luajit then | 674 | if not is_luajit then |
823 | local string_format = string.format | ||
824 | do | 675 | do |
825 | local addqt = { | 676 | local addqt = { |
826 | ["\n"] = "\\\n", | 677 | ["\n"] = "\\\n", |
@@ -832,15 +683,14 @@ if lua_version < "5.3" then | |||
832 | return addqt[c] or string_format("\\%03d", c:byte()) | 683 | return addqt[c] or string_format("\\%03d", c:byte()) |
833 | end | 684 | end |
834 | 685 | ||
835 | local _unpack = unpack | 686 | function M.string.format(fmt, ...) |
836 | function string.format(fmt, ...) | ||
837 | local args, n = { ... }, select('#', ...) | 687 | local args, n = { ... }, select('#', ...) |
838 | local i = 0 | 688 | local i = 0 |
839 | local function adjust_fmt(lead, mods, kind) | 689 | local function adjust_fmt(lead, mods, kind) |
840 | if #lead % 2 == 0 then | 690 | if #lead % 2 == 0 then |
841 | i = i + 1 | 691 | i = i + 1 |
842 | if kind == "s" then | 692 | if kind == "s" then |
843 | args[i] = tostring(args[i]) | 693 | args[i] = _G.tostring(args[i]) |
844 | elseif kind == "q" then | 694 | elseif kind == "q" then |
845 | args[i] = '"'..string_gsub(args[i], "[%z%c\\\"\n]", addquoted)..'"' | 695 | args[i] = '"'..string_gsub(args[i], "[%z%c\\\"\n]", addquoted)..'"' |
846 | return lead.."%"..mods.."s" | 696 | return lead.."%"..mods.."s" |
@@ -848,16 +698,13 @@ if lua_version < "5.3" then | |||
848 | end | 698 | end |
849 | end | 699 | end |
850 | fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt) | 700 | fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt) |
851 | return string_format(fmt, _unpack(args, 1, n)) | 701 | return string_format(fmt, unpack(args, 1, n)) |
852 | end | 702 | end |
853 | end | 703 | end |
854 | end | 704 | end |
855 | 705 | ||
856 | 706 | ||
857 | local io_open = io.open | 707 | function M.io.write(...) |
858 | local io_write = io.write | ||
859 | local io_output = io.output | ||
860 | function io.write(...) | ||
861 | local res, msg, errno = io_write(...) | 708 | local res, msg, errno = io_write(...) |
862 | if res then | 709 | if res then |
863 | return io_output() | 710 | return io_output() |
@@ -867,28 +714,23 @@ if lua_version < "5.3" then | |||
867 | end | 714 | end |
868 | 715 | ||
869 | if not is_luajit then | 716 | if not is_luajit then |
870 | local lines_iterator | 717 | local function helper(st, var_1, ...) |
871 | do | 718 | if var_1 == nil then |
872 | local function helper( st, var_1, ... ) | 719 | if st.doclose then st.f:close() end |
873 | if var_1 == nil then | 720 | if (...) ~= nil then |
874 | if st.doclose then st.f:close() end | 721 | error((...), 2) |
875 | if (...) ~= nil then | ||
876 | error((...), 2) | ||
877 | end | ||
878 | end | 722 | end |
879 | return var_1, ... | ||
880 | end | 723 | end |
724 | return var_1, ... | ||
725 | end | ||
881 | 726 | ||
882 | local _unpack = unpack | 727 | local function lines_iterator(st) |
883 | function lines_iterator(st) | 728 | return helper(st, st.f:read(unpack(st, 1, st.n))) |
884 | return helper(st, st.f:read(_unpack(st, 1, st.n))) | ||
885 | end | ||
886 | end | 729 | end |
887 | 730 | ||
888 | local valid_format = { ["*l"] = true, ["*n"] = true, ["*a"] = true } | 731 | local valid_format = { ["*l"] = true, ["*n"] = true, ["*a"] = true } |
889 | 732 | ||
890 | local io_input = io.input | 733 | function M.io.lines(fname, ...) |
891 | function io.lines(fname, ...) | ||
892 | local doclose, file, msg | 734 | local doclose, file, msg |
893 | if fname ~= nil then | 735 | if fname ~= nil then |
894 | doclose, file, msg = true, io_open(fname, "r") | 736 | doclose, file, msg = true, io_open(fname, "r") |
@@ -904,41 +746,17 @@ if lua_version < "5.3" then | |||
904 | end | 746 | end |
905 | return lines_iterator, st | 747 | return lines_iterator, st |
906 | end | 748 | end |
907 | |||
908 | do | ||
909 | local io_stdout = io.stdout | ||
910 | local io_type = io.type | ||
911 | local file_meta = gmt(io_stdout) | ||
912 | if type(file_meta) == "table" and type(file_meta.__index) == "table" then | ||
913 | local file_write = file_meta.__index.write | ||
914 | file_meta.__index.write = function(self, ...) | ||
915 | local res, msg, errno = file_write(self, ...) | ||
916 | if res then | ||
917 | return self | ||
918 | else | ||
919 | return nil, msg, errno | ||
920 | end | ||
921 | end | ||
922 | |||
923 | file_meta.__index.lines = function(self, ...) | ||
924 | if io_type(self) == "closed file" then | ||
925 | error("attempt to use a closed file", 2) | ||
926 | end | ||
927 | local st = { f=self, doclose=false, n=select('#', ...), ... } | ||
928 | for i = 1, st.n do | ||
929 | if type(st[i]) ~= "number" and not valid_format[st[i]] then | ||
930 | error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2) | ||
931 | end | ||
932 | end | ||
933 | return lines_iterator, st | ||
934 | end | ||
935 | end | ||
936 | end | ||
937 | end -- not luajit | 749 | end -- not luajit |
938 | 750 | ||
939 | |||
940 | end -- lua 5.1 | 751 | end -- lua 5.1 |
941 | 752 | ||
753 | -- further write should be forwarded to _G | ||
754 | M_meta.__newindex = _G | ||
755 | |||
942 | end -- lua < 5.3 | 756 | end -- lua < 5.3 |
943 | 757 | ||
758 | |||
759 | -- return module table | ||
760 | return M | ||
761 | |||
944 | -- vi: set expandtab softtabstop=3 shiftwidth=3 : | 762 | -- vi: set expandtab softtabstop=3 shiftwidth=3 : |
diff --git a/tests/test.lua b/tests/test.lua index 83e174e..856230b 100755 --- a/tests/test.lua +++ b/tests/test.lua | |||
@@ -36,14 +36,26 @@ end | |||
36 | local V = _VERSION:gsub("^.*(%d+)%.(%d+)$", "%1%2") | 36 | local V = _VERSION:gsub("^.*(%d+)%.(%d+)$", "%1%2") |
37 | if jit then V = "jit" end | 37 | if jit then V = "jit" end |
38 | 38 | ||
39 | print( "testing Lua API ..." ) | 39 | local mode = "global" |
40 | package.path = "../?.lua;"..package.path | 40 | if arg[1] == "module" then |
41 | mode = "module" | ||
42 | end | ||
43 | |||
44 | |||
45 | package.path = "../?.lua;../?/init.lua;"..package.path | ||
41 | package.cpath = "./?-"..V..".so;./?-"..V..".dll;./?.so;./?.dll" | 46 | package.cpath = "./?-"..V..".so;./?-"..V..".dll;./?.so;./?.dll" |
42 | require("compat53") | 47 | if mode == "module" then |
48 | print( "testing Lua API using `compat53.module` ..." ) | ||
49 | _ENV = require("compat53.module") | ||
50 | if setfenv then setfenv(1, _ENV) end | ||
51 | else | ||
52 | print( "testing Lua API using `compat53` ..." ) | ||
53 | require("compat53") | ||
54 | end | ||
43 | 55 | ||
44 | ___'' | 56 | ___'' |
45 | do | 57 | do |
46 | local t = setmetatable( {}, { __index = { 1, false, "three" } } ) | 58 | local t = setmetatable({}, { __index = { 1, false, "three" } }) |
47 | for i,v in ipairs(t) do | 59 | for i,v in ipairs(t) do |
48 | print("ipairs", i, v) | 60 | print("ipairs", i, v) |
49 | end | 61 | end |
@@ -338,22 +350,24 @@ do | |||
338 | print("xpcall()", xpcall(func, debug.traceback, false)) | 350 | print("xpcall()", xpcall(func, debug.traceback, false)) |
339 | print("xpcall()", xpcall(func, debug.traceback, true)) | 351 | print("xpcall()", xpcall(func, debug.traceback, true)) |
340 | print("xpcall()", xpcall(func, tb, true)) | 352 | print("xpcall()", xpcall(func, tb, true)) |
341 | local function func2(cb) | 353 | if mode ~= "module" then |
342 | print("xpcall()", xpcall(cb, debug.traceback, "str")) | 354 | local function func2(cb) |
343 | end | 355 | print("xpcall()", xpcall(cb, debug.traceback, "str")) |
344 | local function func3(cb) | 356 | end |
345 | print("pcall()", pcall(cb, "str")) | 357 | local function func3(cb) |
358 | print("pcall()", pcall(cb, "str")) | ||
359 | end | ||
360 | local function cb(arg) | ||
361 | coroutine.yield(2) | ||
362 | return arg | ||
363 | end | ||
364 | local c = coroutine.wrap(func2) | ||
365 | print("xpcall()", c(cb)) | ||
366 | print("xpcall()", c()) | ||
367 | local c = coroutine.wrap(func3) | ||
368 | print("pcall()", c(cb)) | ||
369 | print("pcall()", c()) | ||
346 | end | 370 | end |
347 | local function cb(arg) | ||
348 | coroutine.yield(2) | ||
349 | return arg | ||
350 | end | ||
351 | local c = coroutine.wrap(func2) | ||
352 | print("xpcall()", c(cb)) | ||
353 | print("xpcall()", c()) | ||
354 | local c = coroutine.wrap(func3) | ||
355 | print("pcall()", c(cb)) | ||
356 | print("pcall()", c()) | ||
357 | end | 371 | end |
358 | 372 | ||
359 | 373 | ||
@@ -387,9 +401,11 @@ do | |||
387 | print("coroutine.running()", F(coroutine.running())) | 401 | print("coroutine.running()", F(coroutine.running())) |
388 | local main_co, co1, co2 = coroutine.running() | 402 | local main_co, co1, co2 = coroutine.running() |
389 | -- coroutine.yield | 403 | -- coroutine.yield |
390 | print("coroutine.yield()", pcall(function() | 404 | if mode ~= "module" then |
391 | coroutine.yield(1, 2, 3) | 405 | print("coroutine.yield()", pcall(function() |
392 | end)) | 406 | coroutine.yield(1, 2, 3) |
407 | end)) | ||
408 | end | ||
393 | print("coroutine.yield()", coroutine.wrap(function() | 409 | print("coroutine.yield()", coroutine.wrap(function() |
394 | coroutine.yield(1, 2, 3) | 410 | coroutine.yield(1, 2, 3) |
395 | end)()) | 411 | end)()) |
@@ -428,13 +444,13 @@ do | |||
428 | local path, prefix = "./?.lua;?/init.lua;../?.lua", "package.searchpath()" | 444 | local path, prefix = "./?.lua;?/init.lua;../?.lua", "package.searchpath()" |
429 | print(prefix, package.searchpath("no.such.module", path)) | 445 | print(prefix, package.searchpath("no.such.module", path)) |
430 | print(prefix, package.searchpath("no.such.module", "")) | 446 | print(prefix, package.searchpath("no.such.module", "")) |
431 | print(prefix, package.searchpath("compat52", path)) | 447 | print(prefix, package.searchpath("compat53", path)) |
432 | print(prefix, package.searchpath("no:such:module", path, ":", "|")) | 448 | print(prefix, package.searchpath("no:such:module", path, ":", "|")) |
433 | end | 449 | end |
434 | 450 | ||
435 | 451 | ||
436 | ___'' | 452 | ___'' |
437 | do | 453 | if mode ~= "module" then |
438 | local function mod_func() return {} end | 454 | local function mod_func() return {} end |
439 | local function my_searcher(name) | 455 | local function my_searcher(name) |
440 | if name == "my.module" then | 456 | if name == "my.module" then |
@@ -462,19 +478,19 @@ end | |||
462 | 478 | ||
463 | ___'' | 479 | ___'' |
464 | do | 480 | do |
465 | print("string.find()", ("abc\0abc\0abc"):find("[^a\0]+")) | 481 | print("string.find()", string.find("abc\0abc\0abc", "[^a\0]+")) |
466 | print("string.find()", ("abc\0abc\0abc"):find("%w+\0", 5)) | 482 | print("string.find()", string.find("abc\0abc\0abc", "%w+\0", 5)) |
467 | for x in ("abc\0def\0ghi"):gmatch("[^\0]+") do | 483 | for x in string.gmatch("abc\0def\0ghi", "[^\0]+") do |
468 | print("string.gmatch()", x) | 484 | print("string.gmatch()", x) |
469 | end | 485 | end |
470 | for x in ("abc\0def\0ghi"):gmatch("%w*\0") do | 486 | for x in string.gmatch("abc\0def\0ghi", "%w*\0") do |
471 | print("string.gmatch()", #x) | 487 | print("string.gmatch()", #x) |
472 | end | 488 | end |
473 | print("string.gsub()", ("abc\0def\0ghi"):gsub("[\0]", "X")) | 489 | print("string.gsub()", string.gsub("abc\0def\0ghi", "[\0]", "X")) |
474 | print("string.gsub()", ("abc\0def\0ghi"):gsub("%w*\0", "X")) | 490 | print("string.gsub()", string.gsub("abc\0def\0ghi", "%w*\0", "X")) |
475 | print("string.gsub()", ("abc\0def\0ghi"):gsub("%A", "X")) | 491 | print("string.gsub()", string.gsub("abc\0def\0ghi", "%A", "X")) |
476 | print("string.match()", ("abc\0abc\0abc"):match("([^\0a]+)")) | 492 | print("string.match()", string.match("abc\0abc\0abc", "([^\0a]+)")) |
477 | print("string.match()", #("abc\0abc\0abc"):match(".*\0")) | 493 | print("string.match()", #string.match("abc\0abc\0abc", ".*\0")) |
478 | print("string.rep()", string.rep("a", 0)) | 494 | print("string.rep()", string.rep("a", 0)) |
479 | print("string.rep()", string.rep("b", 1)) | 495 | print("string.rep()", string.rep("b", 1)) |
480 | print("string.rep()", string.rep("c", 4)) | 496 | print("string.rep()", string.rep("c", 4)) |
@@ -533,30 +549,32 @@ do | |||
533 | print("io.lines()", pcall(function() | 549 | print("io.lines()", pcall(function() |
534 | for l in io.lines("no_such_file.txt") do print(l) end | 550 | for l in io.lines("no_such_file.txt") do print(l) end |
535 | end)) | 551 | end)) |
536 | local f = assert(io.open("test.lua", "r")) | 552 | if mode ~= "module" then |
537 | for a,b in f:lines(2, "*l") do | 553 | local f = assert(io.open("test.lua", "r")) |
538 | print("file:lines()", a, b) | 554 | for a,b in f:lines(2, "*l") do |
539 | break | 555 | print("file:lines()", a, b) |
556 | break | ||
557 | end | ||
558 | f:close() | ||
559 | f = assert(io.open("data.txt", "r")) | ||
560 | for n1,n2,rest in f:lines("*n", "*n", "*a") do | ||
561 | print("file:lines()", n1, n2, rest) | ||
562 | end | ||
563 | f:close() | ||
564 | f = assert(io.open("data.txt", "r")) | ||
565 | for l in f:lines() do | ||
566 | print("file:lines()", l) | ||
567 | end | ||
568 | f:close() | ||
569 | print("file:lines()", pcall(function() | ||
570 | for l in f:lines() do print(l) end | ||
571 | end)) | ||
572 | print("file:lines()", pcall(function() | ||
573 | local f = assert(io.open("data.txt", "r")) | ||
574 | for l in f:lines("*l", "*x") do print(l) end | ||
575 | f:close() | ||
576 | end)) | ||
540 | end | 577 | end |
541 | f:close() | ||
542 | f = assert(io.open("data.txt", "r")) | ||
543 | for n1,n2,rest in f:lines("*n", "*n", "*a") do | ||
544 | print("file:lines()", n1, n2, rest) | ||
545 | end | ||
546 | f:close() | ||
547 | f = assert(io.open("data.txt", "r")) | ||
548 | for l in f:lines() do | ||
549 | print("file:lines()", l) | ||
550 | end | ||
551 | f:close() | ||
552 | print("file:lines()", pcall(function() | ||
553 | for l in f:lines() do print(l) end | ||
554 | end)) | ||
555 | print("file:lines()", pcall(function() | ||
556 | local f = assert(io.open("data.txt", "r")) | ||
557 | for l in f:lines("*l", "*x") do print(l) end | ||
558 | f:close() | ||
559 | end)) | ||
560 | os.remove("data.txt") | 578 | os.remove("data.txt") |
561 | end | 579 | end |
562 | ___'' | 580 | ___'' |