aboutsummaryrefslogtreecommitdiff
path: root/compat53/module.lua
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 /compat53/module.lua
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.
Diffstat (limited to 'compat53/module.lua')
-rw-r--r--compat53/module.lua785
1 files changed, 751 insertions, 34 deletions
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 :