aboutsummaryrefslogtreecommitdiffstats
path: root/compat53/module.lua
diff options
context:
space:
mode:
Diffstat (limited to 'compat53/module.lua')
-rw-r--r--compat53/module.lua827
1 files changed, 827 insertions, 0 deletions
diff --git a/compat53/module.lua b/compat53/module.lua
new file mode 100644
index 0000000..30be6b5
--- /dev/null
+++ b/compat53/module.lua
@@ -0,0 +1,827 @@
+local _G, _VERSION = _G, _VERSION
+local lua_version = _VERSION:sub(-3)
+
+
+local M = _G
+
+if lua_version < "5.3" then
+
+ -- cache globals in upvalues
+ local error, ipairs, pairs, pcall, require, select, setmetatable, type =
+ error, ipairs, pairs, pcall, require, select, setmetatable, type
+ local debug, io, math, package, string, table =
+ debug, io, math, package, string, table
+ local io_lines = io.lines
+ local io_read = io.read
+ local unpack = lua_version == "5.1" and unpack or table.unpack
+
+ -- create module table
+ M = {}
+ local M_meta = {
+ __index = _G,
+ -- __newindex is set at the end
+ }
+ setmetatable(M, M_meta)
+
+ -- create subtables
+ M.io = setmetatable({}, { __index = io })
+ M.math = setmetatable({}, { __index = math })
+ M.string = setmetatable({}, { __index = string })
+ M.table = setmetatable({}, { __index = table })
+ M.utf8 = {}
+
+
+ -- select the most powerful getmetatable function available
+ local gmt = type(debug) == "table" and debug.getmetatable or
+ getmetatable or function() return false end
+
+ -- type checking functions
+ local checkinteger -- forward declararation
+
+ local function argcheck(cond, i, f, extra)
+ if not cond then
+ error("bad argument #"..i.." to '"..f.."' ("..extra..")", 0)
+ end
+ end
+
+
+ -- load utf8 library
+ local utf8_ok, utf8lib = pcall(require, "compat53.utf8")
+ if utf8_ok then
+ if lua_version == "5.1" then
+ utf8lib.charpattern = "[%z\1-\127\194-\244][\128-\191]*"
+ end
+ for k,v in pairs(utf8lib) do
+ M.utf8[k] = v
+ end
+ package.loaded["utf8"] = M.utf8
+ end
+
+
+ -- load table library
+ local table_ok, tablib = pcall(require, "compat53.table")
+ if table_ok then
+ for k,v in pairs(tablib) do
+ M.table[k] = v
+ end
+ end
+
+
+ -- load string packing functions
+ local str_ok, strlib = pcall(require, "compat53.string")
+ if str_ok then
+ for k,v in pairs(strlib) do
+ M.string[k] = v
+ end
+ end
+
+
+ -- try Roberto's struct module for string packing/unpacking if
+ -- compat53.string is unavailable
+ if not str_ok then
+ local struct_ok, struct = pcall(require, "struct")
+ if struct_ok then
+ M.string.pack = struct.pack
+ M.string.packsize = struct.size
+ M.string.unpack = struct.unpack
+ end
+ end
+
+
+ -- update math library
+ do
+ local maxint, minint = 1
+
+ while maxint+1 > maxint and 2*maxint > maxint do
+ maxint = maxint * 2
+ end
+ if 2*maxint <= maxint then
+ maxint = 2*maxint-1
+ minint = -maxint-1
+ else
+ maxint = maxint
+ minint = -maxint
+ end
+ M.math.maxinteger = maxint
+ M.math.mininteger = minint
+
+ function M.math.tointeger(n)
+ if type(n) == "number" and n <= maxint and n >= minint and n % 1 == 0 then
+ return n
+ end
+ return nil
+ end
+
+ function M.math.type(n)
+ if type(n) == "number" then
+ if n <= maxint and n >= minint and n % 1 == 0 then
+ return "integer"
+ else
+ return "float"
+ end
+ else
+ return nil
+ end
+ end
+
+ function checkinteger(x, i, f)
+ local t = type(x)
+ if t ~= "number" then
+ error("bad argument #"..i.." to '"..f..
+ "' (number expected, got "..t..")", 0)
+ elseif x > maxint or x < minint or x % 1 ~= 0 then
+ error("bad argument #"..i.." to '"..f..
+ "' (number has no integer representation)", 0)
+ else
+ return x
+ end
+ end
+
+ function M.math.ult(m, n)
+ m = checkinteger(m, "1", "math.ult")
+ n = checkinteger(n, "2", "math.ult")
+ if m >= 0 and n < 0 then
+ return true
+ elseif m < 0 and n >= 0 then
+ return false
+ else
+ return m < n
+ end
+ end
+ end
+
+
+ -- assert should allow non-string error objects
+ function M.assert(cond, ...)
+ if cond then
+ return cond, ...
+ elseif select('#', ...) > 0 then
+ error((...), 0)
+ else
+ error("assertion failed!", 0)
+ end
+ end
+
+
+ -- ipairs should respect __index metamethod
+ do
+ local function ipairs_iterator(st, var)
+ var = var + 1
+ local val = st[var]
+ if val ~= nil then
+ return var, st[var]
+ end
+ end
+ function M.ipairs(t)
+ if gmt(t) ~= nil then -- t has metatable
+ return ipairs_iterator, t, 0
+ else
+ return ipairs(t)
+ end
+ end
+ end
+
+
+ -- make '*' optional for io.read and io.lines
+ do
+ local function addasterisk(fmt)
+ if type(fmt) == "string" and fmt:sub(1, 1) ~= "*" then
+ return "*"..fmt
+ else
+ return fmt
+ end
+ end
+
+ function M.io.read(...)
+ local n = select('#', ...)
+ for i = 1, n do
+ local a = select(i, ...)
+ local b = addasterisk(a)
+ -- as an optimization we only allocate a table for the
+ -- modified format arguments when we have a '*' somewhere.
+ if a ~= b then
+ local args = { ... }
+ args[i] = b
+ for j = i+1, n do
+ args[j] = addasterisk(args[j])
+ end
+ return io_read(unpack(args, 1, n))
+ end
+ end
+ return io_read(...)
+ end
+
+ -- PUC-Rio Lua 5.1 uses a different implementation for io.lines!
+ function M.io.lines(...)
+ local n = select('#', ...)
+ for i = 2, n do
+ local a = select(i, ...)
+ local b = addasterisk(a)
+ -- as an optimization we only allocate a table for the
+ -- modified format arguments when we have a '*' somewhere.
+ if a ~= b then
+ local args = { ... }
+ args[i] = b
+ for j = i+1, n do
+ args[j] = addasterisk(args[j])
+ end
+ return io_lines(unpack(args, 1, n))
+ end
+ end
+ return io_lines(...)
+ end
+ end
+
+
+ -- update table library (if C module not available)
+ if not table_ok then
+ local table_concat = table.concat
+ local table_insert = table.insert
+ local table_remove = table.remove
+ local table_sort = table.sort
+
+ function M.table.concat(list, sep, i, j)
+ local mt = gmt(list)
+ if type(mt) == "table" and type(mt.__len) == "function" then
+ local src = list
+ list, i, j = {}, i or 1, j or mt.__len(src)
+ for k = i, j do
+ list[k] = src[k]
+ end
+ end
+ return table_concat(list, sep, i, j)
+ end
+
+ function M.table.insert(list, ...)
+ local mt = gmt(list)
+ local has_mt = type(mt) == "table"
+ local has_len = has_mt and type(mt.__len) == "function"
+ if has_mt and (has_len or mt.__index or mt.__newindex) then
+ local e = (has_len and mt.__len(list) or #list)+1
+ local nargs, pos, value = select('#', ...), ...
+ if nargs == 1 then
+ pos, value = e, pos
+ elseif nargs == 2 then
+ pos = checkinteger(pos, "2", "table.insert")
+ argcheck(1 <= pos and pos <= e, "2", "table.insert",
+ "position out of bounds" )
+ else
+ error("wrong number of arguments to 'insert'", 0)
+ end
+ for i = e-1, pos, -1 do
+ list[i+1] = list[i]
+ end
+ list[pos] = value
+ else
+ return table_insert(list, ...)
+ end
+ end
+
+ function M.table.move(a1, f, e, t, a2)
+ a2 = a2 or a1
+ f = checkinteger(f, "2", "table.move")
+ argcheck(f > 0, "2", "table.move",
+ "initial position must be positive")
+ e = checkinteger(e, "3", "table.move")
+ t = checkinteger(t, "4", "table.move")
+ if e >= f then
+ local m, n, d = 0, e-f, 1
+ if t > f then m, n, d = n, m, -1 end
+ for i = m, n, d do
+ a2[t+i] = a1[f+i]
+ end
+ end
+ return a2
+ end
+
+ function M.table.remove(list, pos)
+ local mt = gmt(list)
+ local has_mt = type(mt) == "table"
+ local has_len = has_mt and type(mt.__len) == "function"
+ if has_mt and (has_len or mt.__index or mt.__newindex) then
+ local e = (has_len and mt.__len(list) or #list)
+ pos = pos ~= nil and checkinteger(pos, "2", "table.remove") or e
+ if pos ~= e then
+ argcheck(1 <= pos and pos <= e+1, "2", "table.remove",
+ "position out of bounds" )
+ end
+ local result = list[pos]
+ while pos < e do
+ list[pos] = list[pos+1]
+ pos = pos + 1
+ end
+ list[pos] = nil
+ return result
+ else
+ return table_remove(list, pos)
+ end
+ end
+
+ do
+ local function pivot(list, cmp, a, b)
+ local m = b - a
+ if m > 2 then
+ local c = a + (m-m%2)/2
+ local x, y, z = list[a], list[b], list[c]
+ if not cmp(x, y) then
+ x, y, a, b = y, x, b, a
+ end
+ if not cmp(y, z) then
+ y, b = z, c
+ end
+ if not cmp(x, y) then
+ y, b = x, a
+ end
+ return b, y
+ else
+ return b, list[b]
+ end
+ end
+
+ local function lt_cmp(a, b)
+ return a < b
+ end
+
+ local function qsort(list, cmp, b, e)
+ if b < e then
+ local i, j, k, val = b, e, pivot(list, cmp, b, e)
+ while i < j do
+ while i < j and cmp(list[i], val) do
+ i = i + 1
+ end
+ while i < j and not cmp(list[j], val) do
+ j = j - 1
+ end
+ if i < j then
+ list[i], list[j] = list[j], list[i]
+ if i == k then k = j end -- update pivot position
+ i, j = i+1, j-1
+ end
+ end
+ if i ~= k and not cmp(list[i], val) then
+ list[i], list[k] = val, list[i]
+ k = i -- update pivot position
+ end
+ qsort(list, cmp, b, i == k and i-1 or i)
+ return qsort(list, cmp, i+1, e)
+ end
+ end
+
+ function M.table.sort(list, cmp)
+ local mt = gmt(list)
+ local has_mt = type(mt) == "table"
+ local has_len = has_mt and type(mt.__len) == "function"
+ if has_len then
+ cmp = cmp or lt_cmp
+ local len = mt.__len(list)
+ return qsort(list, cmp, 1, len)
+ else
+ return table_sort(list, cmp)
+ end
+ end
+ end
+
+ local function unpack_helper(list, i, j, ...)
+ if j < i then
+ return ...
+ else
+ return unpack_helper(list, i, j-1, list[j], ...)
+ end
+ end
+ function M.table.unpack(list, i, j)
+ local mt = gmt(list)
+ local has_mt = type(mt) == "table"
+ local has_len = has_mt and type(mt.__len) == "function"
+ if has_mt and (has_len or mt.__index) then
+ i, j = i or 1, j or (has_len and mt.__len(list)) or #list
+ return unpack_helper(list, i, j)
+ else
+ return unpack(list, i, j)
+ end
+ end
+ end -- update table library
+
+
+ -- bring Lua 5.1 (and LuaJIT) up to speed with Lua 5.2
+ if lua_version == "5.1" then
+ -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag)
+ local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ"
+ local is_luajit52 = is_luajit and
+ #setmetatable({}, { __len = function() return 1 end }) == 1
+
+ -- cache globals in upvalues
+ local load, loadfile, loadstring, setfenv, xpcall =
+ load, loadfile, loadstring, setfenv, xpcall
+ local coroutine, os = coroutine, os
+ local coroutine_create = coroutine.create
+ local coroutine_resume = coroutine.resume
+ local coroutine_running = coroutine.running
+ local coroutine_status = coroutine.status
+ local coroutine_yield = coroutine.yield
+ local io_input = io.input
+ local io_open = io.open
+ local io_output = io.output
+ local io_write = io.write
+ local math_log = math.log
+ local os_execute = os.execute
+ local string_find = string.find
+ local string_format = string.format
+ local string_gmatch = string.gmatch
+ local string_gsub = string.gsub
+ local string_match = string.match
+ local string_rep = string.rep
+ local table_concat = table.concat
+
+ -- create subtables
+ M.coroutine = setmetatable({}, { __index = coroutine })
+ M.os = setmetatable({}, { __index = os })
+ M.package = setmetatable({}, { __index = package })
+
+ -- handle debug functions
+ if type(debug) == "table" then
+ local debug_setfenv = debug.setfenv
+ local debug_getfenv = debug.getfenv
+ local debug_setmetatable = debug.setmetatable
+
+ M.debug = setmetatable({}, { __index = debug })
+
+ if not is_luajit52 then
+ function M.debug.setuservalue(obj, value)
+ if type(obj) ~= "userdata" then
+ error("bad argument #1 to 'setuservalue' (userdata expected, got "..
+ type(obj)..")", 2)
+ end
+ if value == nil then value = _G end
+ if type(value) ~= "table" then
+ error("bad argument #2 to 'setuservalue' (table expected, got "..
+ type(value)..")", 2)
+ end
+ return debug_setfenv(obj, value)
+ end
+
+ function M.debug.getuservalue(obj)
+ if type(obj) ~= "userdata" then
+ return nil
+ else
+ local v = debug_getfenv(obj)
+ if v == _G or v == package then
+ return nil
+ end
+ return v
+ end
+ end
+
+ function M.debug.setmetatable(value, tab)
+ debug_setmetatable(value, tab)
+ return value
+ end
+ end -- not luajit with compat52 enabled
+ end -- debug table available
+
+
+ if not is_luajit52 then
+ function M.pairs(t)
+ local mt = gmt(t)
+ if type(mt) == "table" and type(mt.__pairs) == "function" then
+ return mt.__pairs(t)
+ else
+ return pairs(t)
+ end
+ end
+ end
+
+
+ if not is_luajit then
+ local function check_mode(mode, prefix)
+ local has = { text = false, binary = false }
+ for i = 1,#mode do
+ local c = mode:sub(i, i)
+ if c == "t" then has.text = true end
+ if c == "b" then has.binary = true end
+ end
+ local t = prefix:sub(1, 1) == "\27" and "binary" or "text"
+ if not has[t] then
+ return "attempt to load a "..t.." chunk (mode is '"..mode.."')"
+ end
+ end
+
+ function M.load(ld, source, mode, env)
+ mode = mode or "bt"
+ local chunk, msg
+ if type( ld ) == "string" then
+ if mode ~= "bt" then
+ local merr = check_mode(mode, ld)
+ if merr then return nil, merr end
+ end
+ chunk, msg = loadstring(ld, source)
+ else
+ local ld_type = type(ld)
+ if ld_type ~= "function" then
+ error("bad argument #1 to 'load' (function expected, got "..
+ ld_type..")", 2)
+ end
+ if mode ~= "bt" then
+ local checked, merr = false, nil
+ local function checked_ld()
+ if checked then
+ return ld()
+ else
+ checked = true
+ local v = ld()
+ merr = check_mode(mode, v or "")
+ if merr then return nil end
+ return v
+ end
+ end
+ chunk, msg = load(checked_ld, source)
+ if merr then return nil, merr end
+ else
+ chunk, msg = load(ld, source)
+ end
+ end
+ if not chunk then
+ return chunk, msg
+ end
+ if env ~= nil then
+ setfenv(chunk, env)
+ end
+ return chunk
+ end
+
+ M.loadstring = M.load
+
+ function M.loadfile(file, mode, env)
+ mode = mode or "bt"
+ if mode ~= "bt" then
+ local f = io_open(file, "rb")
+ if f then
+ local prefix = f:read(1)
+ f:close()
+ if prefix then
+ local merr = check_mode(mode, prefix)
+ if merr then return nil, merr end
+ end
+ end
+ end
+ local chunk, msg = loadfile(file)
+ if not chunk then
+ return chunk, msg
+ end
+ if env ~= nil then
+ setfenv(chunk, env)
+ end
+ return chunk
+ end
+ end -- not luajit
+
+
+ if not is_luajit52 then
+ function M.rawlen(v)
+ local t = type(v)
+ if t ~= "string" and t ~= "table" then
+ error("bad argument #1 to 'rawlen' (table or string expected)", 2)
+ end
+ return #v
+ end
+ end
+
+
+ if not is_luajit then
+ function M.xpcall(f, msgh, ...)
+ local args, n = { ... }, select('#', ...)
+ return xpcall(function() return f(unpack(args, 1, n)) end, msgh)
+ end
+ end
+
+
+ if not is_luajit52 then
+ function M.os.execute(cmd)
+ local code = os_execute(cmd)
+ -- Lua 5.1 does not report exit by signal.
+ if code == 0 then
+ return true, "exit", code
+ else
+ if package.config:sub(1, 1) == '/' then
+ code = code/256 -- only correct on Linux!
+ end
+ return nil, "exit", code
+ end
+ end
+ end
+
+
+ if not table_ok and not is_luajit52 then
+ M.table.pack = function(...)
+ return { n = select('#', ...), ... }
+ end
+ end
+
+
+ local main_coroutine = coroutine_create(function() end)
+
+ function M.coroutine.create(func)
+ local success, result = pcall(coroutine_create, func)
+ if not success then
+ if type(func) ~= "function" then
+ error("bad argument #1 (function expected)", 0)
+ end
+ result = coroutine_create(function(...) return func(...) end)
+ end
+ return result
+ end
+
+ if not is_luajit52 then
+ function M.coroutine.running()
+ local co = coroutine_running()
+ if co then
+ return co, false
+ else
+ return main_coroutine, true
+ end
+ end
+ end
+
+ function M.coroutine.yield(...)
+ local co, flag = coroutine_running()
+ if co and not flag then
+ return coroutine_yield(...)
+ else
+ error("attempt to yield from outside a coroutine", 0)
+ end
+ end
+
+ if not is_luajit then
+ function M.coroutine.resume(co, ...)
+ if co == main_coroutine then
+ return false, "cannot resume non-suspended coroutine"
+ else
+ return coroutine_resume(co, ...)
+ end
+ end
+
+ function M.coroutine.status(co)
+ local notmain = coroutine_running()
+ if co == main_coroutine then
+ return notmain and "normal" or "running"
+ else
+ return coroutine_status(co)
+ end
+ end
+ end -- not luajit
+
+
+ if not is_luajit then
+ M.math.log = function(x, base)
+ if base ~= nil then
+ return math_log(x)/math_log(base)
+ else
+ return math_log(x)
+ end
+ end
+ end
+
+
+ if not is_luajit then
+ function M.package.searchpath(name, path, sep, rep)
+ sep = (sep or "."):gsub("(%p)", "%%%1")
+ rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1")
+ local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1")
+ local msg = {}
+ for subpath in path:gmatch("[^;]+") do
+ local fpath = subpath:gsub("%?", pname)
+ local f = io_open(fpath, "r")
+ if f then
+ f:close()
+ return fpath
+ end
+ msg[#msg+1] = "\n\tno file '" .. fpath .. "'"
+ end
+ return nil, table_concat(msg)
+ end
+ end
+
+
+ local function fix_pattern(pattern)
+ return (string_gsub(pattern, "%z", "%%z"))
+ end
+
+ function M.string.find(s, pattern, ...)
+ return string_find(s, fix_pattern(pattern), ...)
+ end
+
+ function M.string.gmatch(s, pattern)
+ return string_gmatch(s, fix_pattern(pattern))
+ end
+
+ function M.string.gsub(s, pattern, ...)
+ return string_gsub(s, fix_pattern(pattern), ...)
+ end
+
+ function M.string.match(s, pattern, ...)
+ return string_match(s, fix_pattern(pattern), ...)
+ end
+
+ if not is_luajit then
+ function M.string.rep(s, n, sep)
+ if sep ~= nil and sep ~= "" and n >= 2 then
+ return s .. string_rep(sep..s, n-1)
+ else
+ return string_rep(s, n)
+ end
+ end
+ end
+
+ if not is_luajit then
+ do
+ local addqt = {
+ ["\n"] = "\\\n",
+ ["\\"] = "\\\\",
+ ["\""] = "\\\""
+ }
+
+ local function addquoted(c, d)
+ return (addqt[c] or string_format(d~="" and "\\%03d" or "\\%d", c:byte()))..d
+ end
+
+ function M.string.format(fmt, ...)
+ local args, n = { ... }, select('#', ...)
+ local i = 0
+ local function adjust_fmt(lead, mods, kind)
+ if #lead % 2 == 0 then
+ i = i + 1
+ if kind == "s" then
+ args[i] = _G.tostring(args[i])
+ elseif kind == "q" then
+ args[i] = '"'..string_gsub(args[i], "([%z%c\\\"\n])(%d?)", addquoted)..'"'
+ return lead.."%"..mods.."s"
+ end
+ end
+ end
+ fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt)
+ return string_format(fmt, unpack(args, 1, n))
+ end
+ end
+ end
+
+
+ function M.io.write(...)
+ local res, msg, errno = io_write(...)
+ if res then
+ return io_output()
+ else
+ return nil, msg, errno
+ end
+ end
+
+ if not is_luajit then
+ local function helper(st, var_1, ...)
+ if var_1 == nil then
+ if st.doclose then st.f:close() end
+ if (...) ~= nil then
+ error((...), 2)
+ end
+ end
+ return var_1, ...
+ end
+
+ local function lines_iterator(st)
+ return helper(st, st.f:read(unpack(st, 1, st.n)))
+ end
+
+ function M.io.lines(fname, ...)
+ local doclose, file, msg
+ if fname ~= nil then
+ doclose, file, msg = true, io_open(fname, "r")
+ if not file then error(msg, 2) end
+ else
+ doclose, file = false, io_input()
+ end
+ local st = { f=file, doclose=doclose, n=select('#', ...), ... }
+ for i = 1, st.n do
+ local t = type(st[i])
+ if t == "string" then
+ local fmt = st[i]:match("^%*?([aln])")
+ if not fmt then
+ error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
+ end
+ st[i] = "*"..fmt
+ elseif t ~= "number" then
+ error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
+ end
+ end
+ return lines_iterator, st
+ end
+ end -- not luajit
+
+ end -- lua 5.1
+
+ -- further write should be forwarded to _G
+ M_meta.__newindex = _G
+
+end -- lua < 5.3
+
+
+-- return module table
+return M
+
+-- vi: set expandtab softtabstop=3 shiftwidth=3 :