aboutsummaryrefslogtreecommitdiff
path: root/compat53/module.lua
diff options
context:
space:
mode:
authorPhilipp Janda <siffiejoe@gmx.net>2015-04-29 12:26:34 +0200
committerPhilipp Janda <siffiejoe@gmx.net>2015-04-29 12:26:34 +0200
commitc87038c1f3a6d3a21d14d6db1affbf879a25386a (patch)
tree0bbdb4925e1c47185b32f5e678a1265b80e69862 /compat53/module.lua
parentec5331cb94f9100fe4cf26d1ea215dd66e8e185c (diff)
parent17fdace5c04486db3470fe7022aee7eac8efc3af (diff)
downloadlua-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
Diffstat (limited to 'compat53/module.lua')
-rw-r--r--compat53/module.lua762
1 files changed, 762 insertions, 0 deletions
diff --git a/compat53/module.lua b/compat53/module.lua
new file mode 100644
index 0000000..0dd0317
--- /dev/null
+++ b/compat53/module.lua
@@ -0,0 +1,762 @@
1local _G, _VERSION = _G, _VERSION
2local lua_version = _VERSION:sub(-3)
3
4
5local M = _G
6
7if lua_version < "5.3" then
8
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
29
30 -- select the most powerful getmetatable function available
31 local gmt = type(debug) == "table" and debug.getmetatable or
32 getmetatable or function() return false end
33
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, unpack, xpcall =
356 load, loadfile, loadstring, setfenv, 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_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
540 if not is_luajit52 then
541 function M.os.execute(cmd)
542 local code = os_execute(cmd)
543 -- Lua 5.1 does not report exit by signal.
544 if code == 0 then
545 return true, "exit", code
546 else
547 return nil, "exit", code/256 -- only correct on Linux!
548 end
549 end
550 end
551
552
553 if not table_ok and not is_luajit52 then
554 M.table.pack = function(...)
555 return { n = select('#', ...), ... }
556 end
557 end
558
559
560 local main_coroutine = coroutine_create(function() end)
561
562 function M.coroutine.create(func)
563 local success, result = pcall(coroutine_create, func)
564 if not success then
565 if type(func) ~= "function" then
566 error("bad argument #1 (function expected)", 0)
567 end
568 result = coroutine_create(function(...) return func(...) end)
569 end
570 return result
571 end
572
573 if not is_luajit52 then
574 function M.coroutine.running()
575 local co = coroutine_running()
576 if co then
577 return co, false
578 else
579 return main_coroutine, true
580 end
581 end
582 end
583
584 function M.coroutine.yield(...)
585 local co, flag = coroutine_running()
586 if co and not flag then
587 return coroutine_yield(...)
588 else
589 error("attempt to yield from outside a coroutine", 0)
590 end
591 end
592
593 if not is_luajit then
594 function M.coroutine.resume(co, ...)
595 if co == main_coroutine then
596 return false, "cannot resume non-suspended coroutine"
597 else
598 return coroutine_resume(co, ...)
599 end
600 end
601
602 function M.coroutine.status(co)
603 local notmain = coroutine_running()
604 if co == main_coroutine then
605 return notmain and "normal" or "running"
606 else
607 return coroutine_status(co)
608 end
609 end
610 end -- not luajit
611
612
613 if not is_luajit then
614 M.math.log = function(x, base)
615 if base ~= nil then
616 return math_log(x)/math_log(base)
617 else
618 return math_log(x)
619 end
620 end
621 end
622
623
624 if not is_luajit then
625 function M.package.searchpath(name, path, sep, rep)
626 sep = (sep or "."):gsub("(%p)", "%%%1")
627 rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1")
628 local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1")
629 local msg = {}
630 for subpath in path:gmatch("[^;]+") do
631 local fpath = subpath:gsub("%?", pname)
632 local f = io_open(fpath, "r")
633 if f then
634 f:close()
635 return fpath
636 end
637 msg[#msg+1] = "\n\tno file '" .. fpath .. "'"
638 end
639 return nil, table_concat(msg)
640 end
641 end
642
643
644 local function fix_pattern(pattern)
645 return (string_gsub(pattern, "%z", "%%z"))
646 end
647
648 function M.string.find(s, pattern, ...)
649 return string_find(s, fix_pattern(pattern), ...)
650 end
651
652 function M.string.gmatch(s, pattern)
653 return string_gmatch(s, fix_pattern(pattern))
654 end
655
656 function M.string.gsub(s, pattern, ...)
657 return string_gsub(s, fix_pattern(pattern), ...)
658 end
659
660 function M.string.match(s, pattern, ...)
661 return string_match(s, fix_pattern(pattern), ...)
662 end
663
664 if not is_luajit then
665 function M.string.rep(s, n, sep)
666 if sep ~= nil and sep ~= "" and n >= 2 then
667 return s .. string_rep(sep..s, n-1)
668 else
669 return string_rep(s, n)
670 end
671 end
672 end
673
674 if not is_luajit then
675 do
676 local addqt = {
677 ["\n"] = "\\\n",
678 ["\\"] = "\\\\",
679 ["\""] = "\\\""
680 }
681
682 local function addquoted(c)
683 return addqt[c] or string_format("\\%03d", c:byte())
684 end
685
686 function M.string.format(fmt, ...)
687 local args, n = { ... }, select('#', ...)
688 local i = 0
689 local function adjust_fmt(lead, mods, kind)
690 if #lead % 2 == 0 then
691 i = i + 1
692 if kind == "s" then
693 args[i] = _G.tostring(args[i])
694 elseif kind == "q" then
695 args[i] = '"'..string_gsub(args[i], "[%z%c\\\"\n]", addquoted)..'"'
696 return lead.."%"..mods.."s"
697 end
698 end
699 end
700 fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt)
701 return string_format(fmt, unpack(args, 1, n))
702 end
703 end
704 end
705
706
707 function M.io.write(...)
708 local res, msg, errno = io_write(...)
709 if res then
710 return io_output()
711 else
712 return nil, msg, errno
713 end
714 end
715
716 if not is_luajit then
717 local function helper(st, var_1, ...)
718 if var_1 == nil then
719 if st.doclose then st.f:close() end
720 if (...) ~= nil then
721 error((...), 2)
722 end
723 end
724 return var_1, ...
725 end
726
727 local function lines_iterator(st)
728 return helper(st, st.f:read(unpack(st, 1, st.n)))
729 end
730
731 local valid_format = { ["*l"] = true, ["*n"] = true, ["*a"] = true }
732
733 function M.io.lines(fname, ...)
734 local doclose, file, msg
735 if fname ~= nil then
736 doclose, file, msg = true, io_open(fname, "r")
737 if not file then error(msg, 2) end
738 else
739 doclose, file = false, io_input()
740 end
741 local st = { f=file, doclose=doclose, n=select('#', ...), ... }
742 for i = 1, st.n do
743 if type(st[i]) ~= "number" and not valid_format[st[i]] then
744 error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
745 end
746 end
747 return lines_iterator, st
748 end
749 end -- not luajit
750
751 end -- lua 5.1
752
753 -- further write should be forwarded to _G
754 M_meta.__newindex = _G
755
756end -- lua < 5.3
757
758
759-- return module table
760return M
761
762-- vi: set expandtab softtabstop=3 shiftwidth=3 :