aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Janda <siffiejoe@gmx.net>2015-01-18 18:36:21 +0100
committerPhilipp Janda <siffiejoe@gmx.net>2015-01-18 18:36:21 +0100
commit1806cdc571215fa82d6ffde3aa75204861aa6cb5 (patch)
tree0299bce22e04ad8a44142a6e2e455e85166b924d
parent4c062846ae003dd747dfcd3eca91493c7eee4f50 (diff)
downloadlua-compat-5.3-1806cdc571215fa82d6ffde3aa75204861aa6cb5.tar.gz
lua-compat-5.3-1806cdc571215fa82d6ffde3aa75204861aa6cb5.tar.bz2
lua-compat-5.3-1806cdc571215fa82d6ffde3aa75204861aa6cb5.zip
add table.sort, add code from lua-compat-5.2
-rw-r--r--compat53.lua662
-rwxr-xr-xtests/test.lua40
2 files changed, 698 insertions, 4 deletions
diff --git a/compat53.lua b/compat53.lua
index 761d554..03158f0 100644
--- a/compat53.lua
+++ b/compat53.lua
@@ -214,7 +214,70 @@ if lua_version < "5.3" then
214 end 214 end
215 end 215 end
216 216
217 -- TODO: table.sort 217 do
218 local function pivot(list, cmp, a, b)
219 local m = b - a
220 if m > 2 then
221 local c = a + (m-m%2)/2
222 local x, y, z = list[a], list[b], list[c]
223 if not cmp(x, y) then
224 x, y, a, b = y, x, b, a
225 end
226 if not cmp(y, z) then
227 y, z, b, c = z, y, c, b
228 end
229 if not cmp(x, y) then
230 x, y, a, b = y, x, b, a
231 end
232 return b, y
233 else
234 return b, list[b]
235 end
236 end
237
238 local function lt_cmp(a, b)
239 return a < b
240 end
241
242 local function qsort(list, cmp, b, e)
243 if b < e then
244 local i, j, k, val = b, e, pivot(list, cmp, b, e)
245 while i < j do
246 while i < j and cmp(list[i], val) do
247 i = i + 1
248 end
249 while i < j and not cmp(list[j], val) do
250 j = j - 1
251 end
252 if i < j then
253 list[i], list[j] = list[j], list[i]
254 if i == k then k = j end -- update pivot position
255 i, j = i+1, j-1
256 end
257 end
258 if i ~= k and not cmp(list[i], val) then
259 list[i], list[k] = val, list[i]
260 k = i -- update pivot position
261 end
262 qsort(list, cmp, b, i == k and i-1 or i)
263 return qsort(list, cmp, i+1, e)
264 end
265 end
266
267 local table_sort = table.sort
268 function table.sort(list, cmp)
269 local mt = gmt(list)
270 local has_mt = type(mt) == "table"
271 local has_len = has_mt and type(mt.__len) == "function"
272 if has_len then
273 cmp = cmp or lt_cmp
274 local len = mt.__len(list)
275 return qsort(list, cmp, 1, len)
276 else
277 return table_sort(list, cmp)
278 end
279 end
280 end
218 281
219 local table_unpack = lua_version == "5.1" and unpack or table.unpack 282 local table_unpack = lua_version == "5.1" and unpack or table.unpack
220 local function unpack_helper(list, i, j, ...) 283 local function unpack_helper(list, i, j, ...)
@@ -238,16 +301,607 @@ if lua_version < "5.3" then
238 end 301 end
239 302
240 303
304
305 -- bring Lua 5.1 (and LuaJIT) up to speed with Lua 5.2
241 if lua_version == "5.1" then 306 if lua_version == "5.1" then
242 -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag) 307 -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag)
243 local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ" 308 local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ"
244 local is_luajit52 = is_luajit and 309 local is_luajit52 = is_luajit and
245 #setmetatable({}, { __len = function() return 1 end }) == 1 310 #setmetatable({}, { __len = function() return 1 end }) == 1
246 311
247 -- TODO: add functions from lua-compat-5.2
248 312
249 end 313 -- table that maps each running coroutine to the coroutine that resumed it
314 -- this is used to build complete tracebacks when "coroutine-friendly" pcall
315 -- is used.
316 local pcall_previous = {}
317 local pcall_callOf = {}
318 local xpcall_running = {}
319 local coroutine_running = coroutine.running
320
321 -- handle debug functions
322 if type(debug) == "table" then
323
324 if not is_luajit52 then
325 local _G, package = _G, package
326 local debug_setfenv = debug.setfenv
327 function debug.setuservalue(obj, value)
328 if type(obj) ~= "userdata" then
329 error("bad argument #1 to 'setuservalue' (userdata expected, got "..
330 type(obj)..")", 0)
331 end
332 if value == nil then value = _G end
333 if type(value) ~= "table" then
334 error("bad argument #2 to 'setuservalue' (table expected, got "..
335 type(value)..")", 0)
336 end
337 return debug_setfenv(obj, value)
338 end
339
340 local debug_getfenv = debug.getfenv
341 function debug.getuservalue(obj)
342 if type(obj) ~= "userdata" then
343 return nil
344 else
345 local v = debug_getfenv(obj)
346 if v == _G or v == package then
347 return nil
348 end
349 return v
350 end
351 end
352
353 local debug_setmetatable = debug.setmetatable
354 function debug.setmetatable(value, tab)
355 debug_setmetatable(value, tab)
356 return value
357 end
358 end -- not luajit with compat52 enabled
359
360 local debug_getinfo = debug.getinfo
361 local function calculate_trace_level(co, level)
362 if level ~= nil then
363 for out = 1, 1/0 do
364 local info = (co==nil) and debug_getinfo(out, "") or debug_getinfo(co, out, "")
365 if info == nil then
366 local max = out-1
367 if level <= max then
368 return level
369 end
370 return nil, level-max
371 end
372 end
373 end
374 return 1
375 end
376
377 local stack_pattern = "\nstack traceback:"
378 local stack_replace = ""
379 local debug_traceback = debug.traceback
380 function debug.traceback(co, msg, level)
381 local lvl
382 local nilmsg
383 if type(co) ~= "thread" then
384 co, msg, level = coroutine_running(), co, msg
385 end
386 if msg == nil then
387 msg = ""
388 nilmsg = true
389 elseif type(msg) ~= "string" then
390 return msg
391 end
392 if co == nil then
393 msg = debug_traceback(msg, level or 1)
394 else
395 local xpco = xpcall_running[co]
396 if xpco ~= nil then
397 lvl, level = calculate_trace_level(xpco, level)
398 if lvl then
399 msg = debug_traceback(xpco, msg, lvl)
400 else
401 msg = msg..stack_pattern
402 end
403 lvl, level = calculate_trace_level(co, level)
404 if lvl then
405 local trace = debug_traceback(co, "", lvl)
406 msg = msg..trace:gsub(stack_pattern, stack_replace)
407 end
408 else
409 co = pcall_callOf[co] or co
410 lvl, level = calculate_trace_level(co, level)
411 if lvl then
412 msg = debug_traceback(co, msg, lvl)
413 else
414 msg = msg..stack_pattern
415 end
416 end
417 co = pcall_previous[co]
418 while co ~= nil do
419 lvl, level = calculate_trace_level(co, level)
420 if lvl then
421 local trace = debug_traceback(co, "", lvl)
422 msg = msg..trace:gsub(stack_pattern, stack_replace)
423 end
424 co = pcall_previous[co]
425 end
426 end
427 if nilmsg then
428 msg = msg:gsub("^\n", "")
429 end
430 msg = msg:gsub("\n\t%(tail call%): %?", "\000")
431 msg = msg:gsub("\n\t%.%.%.\n", "\001\n")
432 msg = msg:gsub("\n\t%.%.%.$", "\001")
433 msg = msg:gsub("(%z+)\001(%z+)", function(some, other)
434 return "\n\t(..."..#some+#other.."+ tail call(s)...)"
435 end)
436 msg = msg:gsub("\001(%z+)", function(zeros)
437 return "\n\t(..."..#zeros.."+ tail call(s)...)"
438 end)
439 msg = msg:gsub("(%z+)\001", function(zeros)
440 return "\n\t(..."..#zeros.."+ tail call(s)...)"
441 end)
442 msg = msg:gsub("%z+", function(zeros)
443 return "\n\t(..."..#zeros.." tail call(s)...)"
444 end)
445 msg = msg:gsub("\001", function(zeros)
446 return "\n\t..."
447 end)
448 return msg
449 end
450 end -- debug table available
451
452
453 if not is_luajit52 then
454 local _pairs = pairs
455 function pairs(t)
456 local mt = gmt(t)
457 if type(mt) == "table" and type(mt.__pairs) == "function" then
458 return mt.__pairs(t)
459 else
460 return _pairs(t)
461 end
462 end
463 end
464
465
466 if not is_luajit then
467 local function check_mode(mode, prefix)
468 local has = { text = false, binary = false }
469 for i = 1,#mode do
470 local c = mode:sub(i, i)
471 if c == "t" then has.text = true end
472 if c == "b" then has.binary = true end
473 end
474 local t = prefix:sub(1, 1) == "\27" and "binary" or "text"
475 if not has[t] then
476 return "attempt to load a "..t.." chunk (mode is '"..mode.."')"
477 end
478 end
479
480 local setfenv = setfenv
481 local _load, _loadstring = load, loadstring
482 function load(ld, source, mode, env)
483 mode = mode or "bt"
484 local chunk, msg
485 if type( ld ) == "string" then
486 if mode ~= "bt" then
487 local merr = check_mode(mode, ld)
488 if merr then return nil, merr end
489 end
490 chunk, msg = _loadstring(ld, source)
491 else
492 local ld_type = type(ld)
493 if ld_type ~= "function" then
494 error("bad argument #1 to 'load' (function expected, got "..
495 ld_type..")", 0)
496 end
497 if mode ~= "bt" then
498 local checked, merr = false, nil
499 local function checked_ld()
500 if checked then
501 return ld()
502 else
503 checked = true
504 local v = ld()
505 merr = check_mode(mode, v or "")
506 if merr then return nil end
507 return v
508 end
509 end
510 chunk, msg = _load(checked_ld, source)
511 if merr then return nil, merr end
512 else
513 chunk, msg = _load(ld, source)
514 end
515 end
516 if not chunk then
517 return chunk, msg
518 end
519 if env ~= nil then
520 setfenv(chunk, env)
521 end
522 return chunk
523 end
524
525 loadstring = load
526
527 local _loadfile = loadfile
528 local io_open = io.open
529 function loadfile(file, mode, env)
530 mode = mode or "bt"
531 if mode ~= "bt" then
532 local f = io_open(file, "rb")
533 if f then
534 local prefix = f:read(1)
535 f:close()
536 if prefix then
537 local merr = check_mode(mode, prefix)
538 if merr then return nil, merr end
539 end
540 end
541 end
542 local chunk, msg = _loadfile(file)
543 if not chunk then
544 return chunk, msg
545 end
546 if env ~= nil then
547 setfenv(chunk, env)
548 end
549 return chunk
550 end
551 end -- not luajit
552
553
554 if not is_luajit52 then
555 function rawlen(v)
556 local t = type(v)
557 if t ~= "string" and t ~= "table" then
558 error("bad argument #1 to 'rawlen' (table or string expected)", 0)
559 end
560 return #v
561 end
562 end
563
564
565 if not is_luajit52 then
566 local os_execute = os.execute
567 function os.execute(cmd)
568 local code = os_execute(cmd)
569 -- Lua 5.1 does not report exit by signal.
570 if code == 0 then
571 return true, "exit", code
572 else
573 return nil, "exit", code/256 -- only correct on POSIX!
574 end
575 end
576 end
577
578
579 if not is_luajit52 then
580 table.pack = function(...)
581 return { n = select('#', ...), ... }
582 end
583 end
584
585
586 local main_coroutine = coroutine.create(function() end)
587
588 local _pcall = pcall
589 local coroutine_create = coroutine.create
590 function coroutine.create(func)
591 local success, result = _pcall(coroutine_create, func)
592 if not success then
593 if type(func) ~= "function" then
594 error("bad argument #1 (function expected)", 0)
595 end
596 result = coroutine_create(function(...) return func(...) end)
597 end
598 return result
599 end
600
601 local pcall_mainOf = {}
602
603 if not is_luajit52 then
604 function coroutine.running()
605 local co = coroutine_running()
606 if co then
607 return pcall_mainOf[co] or co, false
608 else
609 return main_coroutine, true
610 end
611 end
612 end
613
614 local coroutine_yield = coroutine.yield
615 function coroutine.yield(...)
616 local co = coroutine_running()
617 if co then
618 return coroutine_yield(...)
619 else
620 error("attempt to yield from outside a coroutine", 0)
621 end
622 end
623
624 local coroutine_resume = coroutine.resume
625 function coroutine.resume(co, ...)
626 if co == main_coroutine then
627 return false, "cannot resume non-suspended coroutine"
628 else
629 return coroutine_resume(co, ...)
630 end
631 end
632
633 local coroutine_status = coroutine.status
634 function coroutine.status(co)
635 local notmain = coroutine_running()
636 if co == main_coroutine then
637 return notmain and "normal" or "running"
638 else
639 return coroutine_status(co)
640 end
641 end
642
643 local function pcall_results(current, call, success, ...)
644 if coroutine_status(call) == "suspended" then
645 return pcall_results(current, call, coroutine_resume(call, coroutine_yield(...)))
646 end
647 if pcall_previous then
648 pcall_previous[call] = nil
649 local main = pcall_mainOf[call]
650 if main == current then current = nil end
651 pcall_callOf[main] = current
652 end
653 pcall_mainOf[call] = nil
654 return success, ...
655 end
656 local function pcall_exec(current, call, ...)
657 local main = pcall_mainOf[current] or current
658 pcall_mainOf[call] = main
659 if pcall_previous then
660 pcall_previous[call] = current
661 pcall_callOf[main] = call
662 end
663 return pcall_results(current, call, coroutine_resume(call, ...))
664 end
665 local coroutine_create52 = coroutine.create
666 local function pcall_coroutine(func)
667 if type(func) ~= "function" then
668 local callable = func
669 func = function (...) return callable(...) end
670 end
671 return coroutine_create52(func)
672 end
673 function pcall(func, ...)
674 local current = coroutine_running()
675 if not current then return _pcall(func, ...) end
676 return pcall_exec(current, pcall_coroutine(func), ...)
677 end
678
679 local function xpcall_catch(current, call, msgh, success, ...)
680 if not success then
681 xpcall_running[current] = call
682 local ok, result = _pcall(msgh, ...)
683 xpcall_running[current] = nil
684 if not ok then
685 return false, "error in error handling ("..tostring(result)..")"
686 end
687 return false, result
688 end
689 return true, ...
690 end
691 local _xpcall = xpcall
692 local _unpack = unpack
693 function xpcall(f, msgh, ...)
694 local current = coroutine_running()
695 if not current then
696 local args, n = { ... }, select('#', ...)
697 return _xpcall(function() return f(_unpack(args, 1, n)) end, msgh)
698 end
699 local call = pcall_coroutine(f)
700 return xpcall_catch(current, call, msgh, pcall_exec(current, call, ...))
701 end
702
703
704 if not is_luajit then
705 local math_log = math.log
706 math.log = function(x, base)
707 if base ~= nil then
708 return math_log(x)/math_log(base)
709 else
710 return math_log(x)
711 end
712 end
713 end
714
715
716 if not is_luajit then
717 local io_open = io.open
718 local table_concat = table.concat
719 function package.searchpath(name, path, sep, rep)
720 sep = (sep or "."):gsub("(%p)", "%%%1")
721 rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1")
722 local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1")
723 local msg = {}
724 for subpath in path:gmatch("[^;]+") do
725 local fpath = subpath:gsub("%?", pname)
726 local f = io_open(fpath, "r")
727 if f then
728 f:close()
729 return fpath
730 end
731 msg[#msg+1] = "\n\tno file '" .. fpath .. "'"
732 end
733 return nil, table_concat(msg)
734 end
735 end
736
737 local p_index = { searchers = package.loaders }
738 local rawset = rawset
739 setmetatable(package, {
740 __index = p_index,
741 __newindex = function(p, k, v)
742 if k == "searchers" then
743 rawset(p, "loaders", v)
744 p_index.searchers = v
745 else
746 rawset(p, k, v)
747 end
748 end
749 })
750
751
752 local string_gsub = string.gsub
753 local function fix_pattern(pattern)
754 return (string_gsub(pattern, "%z", "%%z"))
755 end
756
757 local string_find = string.find
758 function string.find(s, pattern, ...)
759 return string_find(s, fix_pattern(pattern), ...)
760 end
761
762 local string_gmatch = string.gmatch
763 function string.gmatch(s, pattern)
764 return string_gmatch(s, fix_pattern(pattern))
765 end
766
767 function string.gsub(s, pattern, ...)
768 return string_gsub(s, fix_pattern(pattern), ...)
769 end
770
771 local string_match = string.match
772 function string.match(s, pattern, ...)
773 return string_match(s, fix_pattern(pattern), ...)
774 end
775
776 if not is_luajit then
777 local string_rep = string.rep
778 function string.rep(s, n, sep)
779 if sep ~= nil and sep ~= "" and n >= 2 then
780 return s .. string_rep(sep..s, n-1)
781 else
782 return string_rep(s, n)
783 end
784 end
785 end
786
787 if not is_luajit then
788 local string_format = string.format
789 do
790 local addqt = {
791 ["\n"] = "\\\n",
792 ["\\"] = "\\\\",
793 ["\""] = "\\\""
794 }
795
796 local function addquoted(c)
797 return addqt[c] or string_format("\\%03d", c:byte())
798 end
799
800 function string.format(fmt, ...)
801 local args, n = { ... }, select('#', ...)
802 local i = 0
803 local function adjust_fmt(lead, mods, kind)
804 if #lead % 2 == 0 then
805 i = i + 1
806 if kind == "s" then
807 args[i] = tostring(args[i])
808 elseif kind == "q" then
809 args[i] = '"'..string_gsub(args[i], "[%z%c\\\"\n]", addquoted)..'"'
810 return lead.."%"..mods.."s"
811 end
812 end
813 end
814 fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt)
815 return string_format(fmt, _unpack(args, 1, n))
816 end
817 end
818 end
819
820
821 local io_open = io.open
822 local io_write = io.write
823 local io_output = io.output
824 function io.write(...)
825 local res, msg, errno = io_write(...)
826 if res then
827 return io_output()
828 else
829 return nil, msg, errno
830 end
831 end
832
833 if not is_luajit then
834 local lines_iterator
835 do
836 local function helper( st, var_1, ... )
837 if var_1 == nil then
838 if st.doclose then st.f:close() end
839 if (...) ~= nil then
840 error((...), 0)
841 end
842 end
843 return var_1, ...
844 end
845
846 function lines_iterator(st)
847 return helper(st, st.f:read(_unpack(st, 1, st.n)))
848 end
849 end
850
851 local valid_format = { ["*l"] = true, ["*n"] = true, ["*a"] = true }
852
853 local io_input = io.input
854 function io.lines(fname, ...)
855 local doclose, file, msg
856 if fname ~= nil then
857 doclose, file, msg = true, io_open(fname, "r")
858 if not file then error(msg, 0) end
859 else
860 doclose, file = false, io_input()
861 end
862 local st = { f=file, doclose=doclose, n=select('#', ...), ... }
863 for i = 1, st.n do
864 if type(st[i]) ~= "number" and not valid_format[st[i]] then
865 error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 0)
866 end
867 end
868 return lines_iterator, st
869 end
870
871 do
872 local io_stdout = io.stdout
873 local io_type = io.type
874 local file_meta = gmt(io_stdout)
875 if type(file_meta) == "table" and type(file_meta.__index) == "table" then
876 local file_write = file_meta.__index.write
877 file_meta.__index.write = function(self, ...)
878 local res, msg, errno = file_write(self, ...)
879 if res then
880 return self
881 else
882 return nil, msg, errno
883 end
884 end
885
886 file_meta.__index.lines = function(self, ...)
887 if io_type(self) == "closed file" then
888 error("attempt to use a closed file", 0)
889 end
890 local st = { f=self, doclose=false, n=select('#', ...), ... }
891 for i = 1, st.n do
892 if type(st[i]) ~= "number" and not valid_format[st[i]] then
893 error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 0)
894 end
895 end
896 return lines_iterator, st
897 end
898 end
899 end
900 end -- not luajit
901
902
903 end -- lua 5.1
250 904
251end 905end -- lua < 5.3
252 906
253-- vi: set expandtab softtabstop=3 shiftwidth=3 : 907-- vi: set expandtab softtabstop=3 shiftwidth=3 :
diff --git a/tests/test.lua b/tests/test.lua
index 7e1c9da..b59fd94 100755
--- a/tests/test.lua
+++ b/tests/test.lua
@@ -133,6 +133,46 @@ do
133 print("table.remove", next(t)) 133 print("table.remove", next(t))
134end 134end
135 135
136___''
137do
138 local p, t = tproxy{ 3, 1, 5, 2, 8, 5, 2, 9, 7, 4 }
139 table.sort(p)
140 print("table.sort", next(p))
141 for i,v in ipairs(t) do
142 print("table.sort", i, v)
143 end
144 table.sort(p)
145 print("table.sort", next(p))
146 for i,v in ipairs(t) do
147 print("table.sort", i, v)
148 end
149 p, t = tproxy{ 9, 8, 7, 6, 5, 4, 3, 2, 1 }
150 table.sort(p)
151 print("table.sort", next(p))
152 for i,v in ipairs(t) do
153 print("table.sort", i, v)
154 end
155 table.sort(p, function(a, b) return a > b end)
156 print("table.sort", next(p))
157 for i,v in ipairs(t) do
158 print("table.sort", i, v)
159 end
160 p, t = tproxy{ 1, 1, 1, 1, 1 }
161 print("table.sort", next(p))
162 for i,v in ipairs(t) do
163 print("table.sort", i, v)
164 end
165 t = { 3, 1, 5, 2, 8, 5, 2, 9, 7, 4 }
166 table.sort(t)
167 for i,v in ipairs(t) do
168 print("table.sort", i, v)
169 end
170 table.sort(t, function(a, b) return a > b end)
171 for i,v in ipairs(t) do
172 print("table.sort", i, v)
173 end
174end
175
136 176
137___'' 177___''
138do 178do