aboutsummaryrefslogtreecommitdiff
path: root/compat53
diff options
context:
space:
mode:
authorPhilipp Janda <siffiejoe@gmx.net>2015-04-11 17:09:37 +0200
committerPhilipp Janda <siffiejoe@gmx.net>2015-04-11 17:09:37 +0200
commitddeb3f7a1e25667694eaf3c8bf639b66d6809cb4 (patch)
treea02b62ce9ad9135dd8dc6aab13940e96ea87dcfe /compat53
parentba8686d6af119fc7ecf734a2d2b6e319c8d74df5 (diff)
downloadlua-compat-5.3-ddeb3f7a1e25667694eaf3c8bf639b66d6809cb4.tar.gz
lua-compat-5.3-ddeb3f7a1e25667694eaf3c8bf639b66d6809cb4.tar.bz2
lua-compat-5.3-ddeb3f7a1e25667694eaf3c8bf639b66d6809cb4.zip
split compat53 Lua module into separate modules that do/don't modify the global env
Diffstat (limited to 'compat53')
-rw-r--r--compat53/base.lua910
-rw-r--r--compat53/init.lua103
-rw-r--r--compat53/module.lua37
3 files changed, 1050 insertions, 0 deletions
diff --git a/compat53/base.lua b/compat53/base.lua
new file mode 100644
index 0000000..370909a
--- /dev/null
+++ b/compat53/base.lua
@@ -0,0 +1,910 @@
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 -- table that maps each running coroutine to the coroutine that resumed it
382 -- this is used to build complete tracebacks when "coroutine-friendly" pcall
383 -- is used.
384 local pcall_previous = {}
385 local pcall_callOf = {}
386 local xpcall_running = {}
387
388 -- handle debug functions
389 if type(debug) == "table" then
390 local debug_setfenv = debug.setfenv
391 local debug_getfenv = debug.getfenv
392 local debug_setmetatable = debug.setmetatable
393 local debug_getinfo = debug.getinfo
394 local debug_traceback = debug.traceback
395
396 M.debug = setmetatable({}, { __index = debug })
397
398 if not is_luajit52 then
399 function M.debug.setuservalue(obj, value)
400 if type(obj) ~= "userdata" then
401 error("bad argument #1 to 'setuservalue' (userdata expected, got "..
402 type(obj)..")", 2)
403 end
404 if value == nil then value = _G end
405 if type(value) ~= "table" then
406 error("bad argument #2 to 'setuservalue' (table expected, got "..
407 type(value)..")", 2)
408 end
409 return debug_setfenv(obj, value)
410 end
411
412 function M.debug.getuservalue(obj)
413 if type(obj) ~= "userdata" then
414 return nil
415 else
416 local v = debug_getfenv(obj)
417 if v == _G or v == package then
418 return nil
419 end
420 return v
421 end
422 end
423
424 function M.debug.setmetatable(value, tab)
425 debug_setmetatable(value, tab)
426 return value
427 end
428 end -- not luajit with compat52 enabled
429
430 if not is_luajit then
431 local function calculate_trace_level(co, level)
432 if level ~= nil then
433 for out = 1, 1/0 do
434 local info = (co==nil) and debug_getinfo(out, "") or debug_getinfo(co, out, "")
435 if info == nil then
436 local max = out-1
437 if level <= max then
438 return level
439 end
440 return nil, level-max
441 end
442 end
443 end
444 return 1
445 end
446
447 local stack_pattern = "\nstack traceback:"
448 local stack_replace = ""
449 function M.debug.traceback(co, msg, level)
450 local lvl
451 local nilmsg
452 if type(co) ~= "thread" then
453 co, msg, level = coroutine_running(), co, msg
454 end
455 if msg == nil then
456 msg = ""
457 nilmsg = true
458 elseif type(msg) ~= "string" then
459 return msg
460 end
461 if co == nil then
462 msg = debug_traceback(msg, level or 1)
463 else
464 local xpco = xpcall_running[co]
465 if xpco ~= nil then
466 lvl, level = calculate_trace_level(xpco, level)
467 if lvl then
468 msg = debug_traceback(xpco, msg, lvl)
469 else
470 msg = msg..stack_pattern
471 end
472 lvl, level = calculate_trace_level(co, level)
473 if lvl then
474 local trace = debug_traceback(co, "", lvl)
475 msg = msg..trace:gsub(stack_pattern, stack_replace)
476 end
477 else
478 co = pcall_callOf[co] or co
479 lvl, level = calculate_trace_level(co, level)
480 if lvl then
481 msg = debug_traceback(co, msg, lvl)
482 else
483 msg = msg..stack_pattern
484 end
485 end
486 co = pcall_previous[co]
487 while co ~= nil do
488 lvl, level = calculate_trace_level(co, level)
489 if lvl then
490 local trace = debug_traceback(co, "", lvl)
491 msg = msg..trace:gsub(stack_pattern, stack_replace)
492 end
493 co = pcall_previous[co]
494 end
495 end
496 if nilmsg then
497 msg = msg:gsub("^\n", "")
498 end
499 msg = msg:gsub("\n\t%(tail call%): %?", "\000")
500 msg = msg:gsub("\n\t%.%.%.\n", "\001\n")
501 msg = msg:gsub("\n\t%.%.%.$", "\001")
502 msg = msg:gsub("(%z+)\001(%z+)", function(some, other)
503 return "\n\t(..."..#some+#other.."+ tail call(s)...)"
504 end)
505 msg = msg:gsub("\001(%z+)", function(zeros)
506 return "\n\t(..."..#zeros.."+ tail call(s)...)"
507 end)
508 msg = msg:gsub("(%z+)\001", function(zeros)
509 return "\n\t(..."..#zeros.."+ tail call(s)...)"
510 end)
511 msg = msg:gsub("%z+", function(zeros)
512 return "\n\t(..."..#zeros.." tail call(s)...)"
513 end)
514 msg = msg:gsub("\001", function(zeros)
515 return "\n\t..."
516 end)
517 return msg
518 end
519 end -- is not luajit
520 end -- debug table available
521
522
523 if not is_luajit52 then
524 function M.pairs(t)
525 local mt = gmt(t)
526 if type(mt) == "table" and type(mt.__pairs) == "function" then
527 return mt.__pairs(t)
528 else
529 return pairs(t)
530 end
531 end
532 end
533
534
535 if not is_luajit then
536 local function check_mode(mode, prefix)
537 local has = { text = false, binary = false }
538 for i = 1,#mode do
539 local c = mode:sub(i, i)
540 if c == "t" then has.text = true end
541 if c == "b" then has.binary = true end
542 end
543 local t = prefix:sub(1, 1) == "\27" and "binary" or "text"
544 if not has[t] then
545 return "attempt to load a "..t.." chunk (mode is '"..mode.."')"
546 end
547 end
548
549 function M.load(ld, source, mode, env)
550 mode = mode or "bt"
551 local chunk, msg
552 if type( ld ) == "string" then
553 if mode ~= "bt" then
554 local merr = check_mode(mode, ld)
555 if merr then return nil, merr end
556 end
557 chunk, msg = loadstring(ld, source)
558 else
559 local ld_type = type(ld)
560 if ld_type ~= "function" then
561 error("bad argument #1 to 'load' (function expected, got "..
562 ld_type..")", 2)
563 end
564 if mode ~= "bt" then
565 local checked, merr = false, nil
566 local function checked_ld()
567 if checked then
568 return ld()
569 else
570 checked = true
571 local v = ld()
572 merr = check_mode(mode, v or "")
573 if merr then return nil end
574 return v
575 end
576 end
577 chunk, msg = load(checked_ld, source)
578 if merr then return nil, merr end
579 else
580 chunk, msg = load(ld, source)
581 end
582 end
583 if not chunk then
584 return chunk, msg
585 end
586 if env ~= nil then
587 setfenv(chunk, env)
588 end
589 return chunk
590 end
591
592 M.loadstring = load
593
594 function M.loadfile(file, mode, env)
595 mode = mode or "bt"
596 if mode ~= "bt" then
597 local f = io_open(file, "rb")
598 if f then
599 local prefix = f:read(1)
600 f:close()
601 if prefix then
602 local merr = check_mode(mode, prefix)
603 if merr then return nil, merr end
604 end
605 end
606 end
607 local chunk, msg = loadfile(file)
608 if not chunk then
609 return chunk, msg
610 end
611 if env ~= nil then
612 setfenv(chunk, env)
613 end
614 return chunk
615 end
616 end -- not luajit
617
618
619 if not is_luajit52 then
620 function M.rawlen(v)
621 local t = type(v)
622 if t ~= "string" and t ~= "table" then
623 error("bad argument #1 to 'rawlen' (table or string expected)", 2)
624 end
625 return #v
626 end
627 end
628
629
630 if not is_luajit52 then
631 function M.os.execute(cmd)
632 local code = os_execute(cmd)
633 -- Lua 5.1 does not report exit by signal.
634 if code == 0 then
635 return true, "exit", code
636 else
637 return nil, "exit", code/256 -- only correct on Linux!
638 end
639 end
640 end
641
642
643 if not table_ok and not is_luajit52 then
644 M.table.pack = function(...)
645 return { n = select('#', ...), ... }
646 end
647 end
648
649
650 local main_coroutine = coroutine_create(function() end)
651
652 function M.coroutine.create(func)
653 local success, result = pcall(coroutine_create, func)
654 if not success then
655 if type(func) ~= "function" then
656 error("bad argument #1 (function expected)", 0)
657 end
658 result = coroutine_create(function(...) return func(...) end)
659 end
660 return result
661 end
662
663 local pcall_mainOf = {}
664
665 if not is_luajit52 then
666 function M.coroutine.running()
667 local co = coroutine_running()
668 if co then
669 return pcall_mainOf[co] or co, false
670 else
671 return main_coroutine, true
672 end
673 end
674 end
675
676 function M.coroutine.yield(...)
677 local co, flag = coroutine_running()
678 if co and not flag then
679 return coroutine_yield(...)
680 else
681 error("attempt to yield from outside a coroutine", 0)
682 end
683 end
684
685 if not is_luajit then
686 function M.coroutine.resume(co, ...)
687 if co == main_coroutine then
688 return false, "cannot resume non-suspended coroutine"
689 else
690 return coroutine_resume(co, ...)
691 end
692 end
693
694 function M.coroutine.status(co)
695 local notmain = coroutine_running()
696 if co == main_coroutine then
697 return notmain and "normal" or "running"
698 else
699 return coroutine_status(co)
700 end
701 end
702
703 local function pcall_results(current, call, success, ...)
704 if coroutine_status(call) == "suspended" then
705 return pcall_results(current, call, coroutine_resume(call, coroutine_yield(...)))
706 end
707 if pcall_previous then
708 pcall_previous[call] = nil
709 local main = pcall_mainOf[call]
710 if main == current then current = nil end
711 pcall_callOf[main] = current
712 end
713 pcall_mainOf[call] = nil
714 return success, ...
715 end
716 local function pcall_exec(current, call, ...)
717 local main = pcall_mainOf[current] or current
718 pcall_mainOf[call] = main
719 if pcall_previous then
720 pcall_previous[call] = current
721 pcall_callOf[main] = call
722 end
723 return pcall_results(current, call, coroutine_resume(call, ...))
724 end
725 local coroutine_create52 = M.coroutine.create
726 local function pcall_coroutine(func)
727 if type(func) ~= "function" then
728 local callable = func
729 func = function (...) return callable(...) end
730 end
731 return coroutine_create52(func)
732 end
733 function M.pcall(func, ...)
734 local current = coroutine_running()
735 if not current then return pcall(func, ...) end
736 return pcall_exec(current, pcall_coroutine(func), ...)
737 end
738
739 local function xpcall_catch(current, call, msgh, success, ...)
740 if not success then
741 xpcall_running[current] = call
742 local ok, result = pcall(msgh, ...)
743 xpcall_running[current] = nil
744 if not ok then
745 return false, "error in error handling ("..tostring(result)..")"
746 end
747 return false, result
748 end
749 return true, ...
750 end
751 function M.xpcall(f, msgh, ...)
752 local current = coroutine_running()
753 if not current then
754 local args, n = { ... }, select('#', ...)
755 return xpcall(function() return f(unpack(args, 1, n)) end, msgh)
756 end
757 local call = pcall_coroutine(f)
758 return xpcall_catch(current, call, msgh, pcall_exec(current, call, ...))
759 end
760 end -- not luajit
761
762
763 if not is_luajit then
764 M.math.log = function(x, base)
765 if base ~= nil then
766 return math_log(x)/math_log(base)
767 else
768 return math_log(x)
769 end
770 end
771 end
772
773
774 if not is_luajit then
775 function M.package.searchpath(name, path, sep, rep)
776 sep = (sep or "."):gsub("(%p)", "%%%1")
777 rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1")
778 local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1")
779 local msg = {}
780 for subpath in path:gmatch("[^;]+") do
781 local fpath = subpath:gsub("%?", pname)
782 local f = io_open(fpath, "r")
783 if f then
784 f:close()
785 return fpath
786 end
787 msg[#msg+1] = "\n\tno file '" .. fpath .. "'"
788 end
789 return nil, table_concat(msg)
790 end
791 end
792
793
794 local function fix_pattern(pattern)
795 return (string_gsub(pattern, "%z", "%%z"))
796 end
797
798 function M.string.find(s, pattern, ...)
799 return string_find(s, fix_pattern(pattern), ...)
800 end
801
802 function M.string.gmatch(s, pattern)
803 return string_gmatch(s, fix_pattern(pattern))
804 end
805
806 function M.string.gsub(s, pattern, ...)
807 return string_gsub(s, fix_pattern(pattern), ...)
808 end
809
810 function M.string.match(s, pattern, ...)
811 return string_match(s, fix_pattern(pattern), ...)
812 end
813
814 if not is_luajit then
815 function M.string.rep(s, n, sep)
816 if sep ~= nil and sep ~= "" and n >= 2 then
817 return s .. string_rep(sep..s, n-1)
818 else
819 return string_rep(s, n)
820 end
821 end
822 end
823
824 if not is_luajit then
825 do
826 local addqt = {
827 ["\n"] = "\\\n",
828 ["\\"] = "\\\\",
829 ["\""] = "\\\""
830 }
831
832 local function addquoted(c)
833 return addqt[c] or string_format("\\%03d", c:byte())
834 end
835
836 function M.string.format(fmt, ...)
837 local args, n = { ... }, select('#', ...)
838 local i = 0
839 local function adjust_fmt(lead, mods, kind)
840 if #lead % 2 == 0 then
841 i = i + 1
842 if kind == "s" then
843 args[i] = _G.tostring(args[i])
844 elseif kind == "q" then
845 args[i] = '"'..string_gsub(args[i], "[%z%c\\\"\n]", addquoted)..'"'
846 return lead.."%"..mods.."s"
847 end
848 end
849 end
850 fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt)
851 return string_format(fmt, unpack(args, 1, n))
852 end
853 end
854 end
855
856
857 function M.io.write(...)
858 local res, msg, errno = io_write(...)
859 if res then
860 return io_output()
861 else
862 return nil, msg, errno
863 end
864 end
865
866 if not is_luajit then
867 local function helper(st, var_1, ...)
868 if var_1 == nil then
869 if st.doclose then st.f:close() end
870 if (...) ~= nil then
871 error((...), 2)
872 end
873 end
874 return var_1, ...
875 end
876
877 local function lines_iterator(st)
878 return helper(st, st.f:read(unpack(st, 1, st.n)))
879 end
880
881 local valid_format = { ["*l"] = true, ["*n"] = true, ["*a"] = true }
882
883 function M.io.lines(fname, ...)
884 local doclose, file, msg
885 if fname ~= nil then
886 doclose, file, msg = true, io_open(fname, "r")
887 if not file then error(msg, 2) end
888 else
889 doclose, file = false, io_input()
890 end
891 local st = { f=file, doclose=doclose, n=select('#', ...), ... }
892 for i = 1, st.n do
893 if type(st[i]) ~= "number" and not valid_format[st[i]] then
894 error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
895 end
896 end
897 return lines_iterator, st
898 end
899 end -- not luajit
900
901
902 end -- lua 5.1
903
904end -- lua < 5.3
905
906
907-- return module table
908return M
909
910-- vi: set expandtab softtabstop=3 shiftwidth=3 :
diff --git a/compat53/init.lua b/compat53/init.lua
new file mode 100644
index 0000000..cdd5f34
--- /dev/null
+++ b/compat53/init.lua
@@ -0,0 +1,103 @@
1local _G, _VERSION, type, pairs, require =
2 _G, _VERSION, type, pairs, require
3
4local M = require("compat53.base")
5local lua_version = _VERSION:sub(-3)
6
7
8-- apply other global effects
9if lua_version == "5.1" then
10
11 -- cache globals
12 local error, rawset, select, setmetatable, type, unpack =
13 error, rawset, select, setmetatable, type, unpack
14 local debug, io, package, string = debug, io, package, string
15 local io_type, io_stdout = io.type, io.stdout
16
17 -- select the most powerful getmetatable function available
18 local gmt = type(debug) == "table" and debug.getmetatable or
19 getmetatable or function() return false end
20
21 -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag)
22 local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ"
23
24
25 -- make package.searchers available as an alias for package.loaders
26 local p_index = { searchers = package.loaders }
27 setmetatable(package, {
28 __index = p_index,
29 __newindex = function(p, k, v)
30 if k == "searchers" then
31 rawset(p, "loaders", v)
32 p_index.searchers = v
33 else
34 rawset(p, k, v)
35 end
36 end
37 })
38
39
40 if not is_luajit then
41 local function helper(st, var_1, ...)
42 if var_1 == nil then
43 if (...) ~= nil then
44 error((...), 2)
45 end
46 end
47 return var_1, ...
48 end
49
50 local function lines_iterator(st)
51 return helper(st, st.f:read(unpack(st, 1, st.n)))
52 end
53
54 local valid_format = { ["*l"] = true, ["*n"] = true, ["*a"] = true }
55
56 local file_meta = gmt(io_stdout)
57 if type(file_meta) == "table" and type(file_meta.__index) == "table" then
58 local file_write = file_meta.__index.write
59 file_meta.__index.write = function(self, ...)
60 local res, msg, errno = file_write(self, ...)
61 if res then
62 return self
63 else
64 return nil, msg, errno
65 end
66 end
67
68 file_meta.__index.lines = function(self, ...)
69 if io_type(self) == "closed file" then
70 error("attempt to use a closed file", 2)
71 end
72 local st = { f=self, n=select('#', ...), ... }
73 for i = 1, st.n do
74 if type(st[i]) ~= "number" and not valid_format[st[i]] then
75 error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
76 end
77 end
78 return lines_iterator, st
79 end
80 end
81 end -- not luajit
82
83end -- lua == 5.1
84
85
86-- handle exporting to global scope
87local function extend_table(from, to)
88 for k,v in pairs(from) do
89 if type(v) == "table" and
90 type(to[k]) == "table" and
91 v ~= to[k] then
92 extend_table(v, to[k])
93 else
94 to[k] = v
95 end
96 end
97end
98
99extend_table(M, _G)
100
101return _G
102
103-- vi: set expandtab softtabstop=3 shiftwidth=3 :
diff --git a/compat53/module.lua b/compat53/module.lua
new file mode 100644
index 0000000..f6fd1f7
--- /dev/null
+++ b/compat53/module.lua
@@ -0,0 +1,37 @@
1local _G, _VERSION, debug, error, require, setfenv, setmetatable =
2 _G, _VERSION, debug, error, require, setfenv, setmetatable
3local lua_version = _VERSION:sub(-3)
4local M = require("compat53.base")
5
6local function findmain()
7 local i = 3
8 local info = debug.getinfo(i, "fS")
9 while info do
10 if info.what == "main" then
11 return info.func
12 end
13 i = i + 1
14 info = debug.getinfo(i, "fS")
15 end
16end
17
18local main = findmain()
19if not main then
20 error("must require 'compat53.module' from Lua")
21end
22local env = setmetatable({}, {
23 __index = M,
24 __newindex = _G,
25})
26if lua_version == "5.1" then
27 setfenv(main, env)
28elseif lua_version == "5.2" or lua_version == "5.3" then
29 debug.setupvalue(main, 1, env)
30else
31 error("unsupported Lua version")
32end
33
34-- return false to force reevaluation on next require
35return false
36
37-- vi: set expandtab softtabstop=3 shiftwidth=3 :