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