diff options
Diffstat (limited to 'Data/BuiltIn/Libraries/lua-stdlib/lib/std')
-rw-r--r-- | Data/BuiltIn/Libraries/lua-stdlib/lib/std/_base.lua | 204 | ||||
-rw-r--r-- | Data/BuiltIn/Libraries/lua-stdlib/lib/std/debug.lua | 156 | ||||
-rw-r--r-- | Data/BuiltIn/Libraries/lua-stdlib/lib/std/init.lua | 389 | ||||
-rw-r--r-- | Data/BuiltIn/Libraries/lua-stdlib/lib/std/io.lua | 322 | ||||
-rw-r--r-- | Data/BuiltIn/Libraries/lua-stdlib/lib/std/math.lua | 92 | ||||
-rw-r--r-- | Data/BuiltIn/Libraries/lua-stdlib/lib/std/package.lua | 263 | ||||
-rw-r--r-- | Data/BuiltIn/Libraries/lua-stdlib/lib/std/string.lua | 505 | ||||
-rw-r--r-- | Data/BuiltIn/Libraries/lua-stdlib/lib/std/table.lua | 439 |
8 files changed, 2370 insertions, 0 deletions
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/lib/std/_base.lua b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/_base.lua new file mode 100644 index 0000000..0c623d4 --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/_base.lua @@ -0,0 +1,204 @@ +--[[ + General Lua Libraries for Lua 5.1, 5.2 & 5.3 + Copyright (C) 2002-2018 stdlib authors +]] +--[[-- + Prevent dependency loops with key function implementations. + + A few key functions are used in several stdlib modules; we implement those + functions in this internal module to prevent dependency loops in the first + instance, and to minimise coupling between modules where the use of one of + these functions might otherwise load a whole selection of other supporting + modules unnecessarily. + + Although the implementations are here for logistical reasons, we re-export + them from their respective logical modules so that the api is not affected + as far as client code is concerned. The functions in this file do not make + use of `argcheck` or similar, because we know that they are only called by + other stdlib functions which have already performed the necessary checking + and neither do we want to slow everything down by recheckng those argument + types here. + + This implies that when re-exporting from another module when argument type + checking is in force, we must export a wrapper function that can check the + user's arguments fully at the API boundary. +]] + + +local _ENV = require 'std.normalize' { + concat = 'table.concat', + dirsep = 'package.dirsep', + find = 'string.find', + gsub = 'string.gsub', + insert = 'table.insert', + min = 'math.min', + shallow_copy = 'table.merge', + sort = 'table.sort', + sub = 'string.sub', + table_maxn = table.maxn, + wrap = 'coroutine.wrap', + yield = 'coroutine.yield', +} + + + +--[[ ============================ ]]-- +--[[ Enhanced Core Lua functions. ]]-- +--[[ ============================ ]]-- + + +-- These come as early as possible, because we want the rest of the code +-- in this file to use these versions over the core Lua implementation +-- (which have slightly varying semantics between releases). + + +local maxn = table_maxn or function(t) + local n = 0 + for k in pairs(t) do + if type(k) == 'number' and k > n then + n = k + end + end + return n +end + + + +--[[ ============================ ]]-- +--[[ Shared Stdlib API functions. ]]-- +--[[ ============================ ]]-- + + +-- No need to recurse because functables are second class citizens in +-- Lua: +-- func = function() print 'called' end +-- func() --> 'called' +-- functable=setmetatable({}, {__call=func}) +-- functable() --> 'called' +-- nested=setmetatable({}, {__call=functable}) +-- nested() +-- --> stdin:1: attempt to call a table value(global 'd') +-- --> stack traceback: +-- --> stdin:1: in main chunk +-- --> [C]: in ? +local function callable(x) + if type(x) == 'function' then + return x + end + return (getmetatable(x) or {}).__call +end + + +local function catfile(...) + return concat({...}, dirsep) +end + + +local function compare(l, m) + local lenl, lenm = len(l), len(m) + for i = 1, min(lenl, lenm) do + local li, mi = tonumber(l[i]), tonumber(m[i]) + if li == nil or mi == nil then + li, mi = l[i], m[i] + end + if li < mi then + return -1 + elseif li > mi then + return 1 + end + end + if lenl < lenm then + return -1 + elseif lenl > lenm then + return 1 + end + return 0 +end + + +local function escape_pattern(s) + return (gsub(s, '[%^%$%(%)%%%.%[%]%*%+%-%?]', '%%%0')) +end + + +local function invert(t) + local i = {} + for k, v in pairs(t) do + i[v] = k + end + return i +end + + +local function leaves(it, tr) + local function visit(n) + if type(n) == 'table' then + for _, v in it(n) do + visit(v) + end + else + yield(n) + end + end + return wrap(visit), tr +end + + +local function split(s, sep) + local r, patt = {} + if sep == '' then + patt = '(.)' + insert(r, '') + else + patt = '(.-)' ..(sep or '%s+') + end + local b, slen = 0, len(s) + while b <= slen do + local e, n, m = find(s, patt, b + 1) + insert(r, m or sub(s, b + 1, slen)) + b = n or slen + 1 + end + return r +end + + +--[[ ============= ]]-- +--[[ Internal API. ]]-- +--[[ ============= ]]-- + + +-- For efficient use within stdlib, these functions have no type-checking. +-- In debug mode, type-checking wrappers are re-exported from the public- +-- facing modules as necessary. +-- +-- Also, to provide some sanity, we mirror the subtable layout of stdlib +-- public API here too, which means everything looks relatively normal +-- when importing the functions into stdlib implementation modules. +return { + io = { + catfile = catfile, + }, + + list = { + compare = compare, + }, + + object = { + Module = Module, + mapfields = mapfields, + }, + + string = { + escape_pattern = escape_pattern, + split = split, + }, + + table = { + invert = invert, + maxn = maxn, + }, + + tree = { + leaves = leaves, + }, +} diff --git a/Data/BuiltIn/Libraries/lua-stdlib/lib/std/debug.lua b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/debug.lua new file mode 100644 index 0000000..cab29ff --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/debug.lua @@ -0,0 +1,156 @@ +--[[ + General Lua Libraries for Lua 5.1, 5.2 & 5.3 + Copyright (C) 2002-2018 stdlib authors +]] +--[[-- + Additions to the core debug module. + + The module table returned by `std.debug` also contains all of the entries + from the core debug table. An hygienic way to import this module, then, is + simply to override the core `debug` locally: + + local debug = require 'std.debug' + + @corelibrary std.debug +]] + + +local _ENV = require 'std.normalize' { + 'debug', + _debug = require 'std._debug', + concat = 'table.concat', + huge = 'math.huge', + max = 'math.max', + merge = 'table.merge', + stderr = 'io.stderr', +} + + + +--[[ =============== ]]-- +--[[ Implementation. ]]-- +--[[ =============== ]]-- + + +local function say(n, ...) + local level, argt = n, {...} + if type(n) ~= 'number' then + level, argt = 1, {n, ...} + end + if _debug.level ~= huge and + ((type(_debug.level) == 'number' and _debug.level >= level) or level <= 1) + then + local t = {} + for k, v in pairs(argt) do + t[k] = str(v) + end + stderr:write(concat(t, '\t') .. '\n') + end +end + + +local level = 0 + +local function trace(event) + local t = debug.getinfo(3) + local s = ' >>> ' + for i = 1, level do + s = s .. ' ' + end + if t ~= nil and t.currentline >= 0 then + s = s .. t.short_src .. ':' .. t.currentline .. ' ' + end + t = debug.getinfo(2) + if event == 'call' then + level = level + 1 + else + level = max(level - 1, 0) + end + if t.what == 'main' then + if event == 'call' then + s = s .. 'begin ' .. t.short_src + else + s = s .. 'end ' .. t.short_src + end + elseif t.what == 'Lua' then + s = s .. event .. ' ' ..(t.name or '(Lua)') .. ' <' .. + t.linedefined .. ':' .. t.short_src .. '>' + else + s = s .. event .. ' ' ..(t.name or '(C)') .. ' [' .. t.what .. ']' + end + stderr:write(s .. '\n') +end + +-- Set hooks according to _debug +if _debug.call then + debug.sethook(trace, 'cr') +end + + + +local M = { + --- Function Environments + -- @section environments + + --- Extend `debug.getfenv` to unwrap functables correctly. + -- @function getfenv + -- @tparam int|function|functable fn target function, or stack level + -- @treturn table environment of *fn* + getfenv = getfenv, + + --- Extend `debug.setfenv` to unwrap functables correctly. + -- @function setfenv + -- @tparam function|functable fn target function + -- @tparam table env new function environment + -- @treturn function *fn* + setfenv = setfenv, + + + --- Functions + -- @section functions + + --- Print a debugging message to `io.stderr`. + -- Display arguments passed through `std.tostring` and separated by tab + -- characters when `std._debug` hinting is `true` and *n* is 1 or less; + -- or `std._debug.level` is a number greater than or equal to *n*. If + -- `std._debug` hinting is false or nil, nothing is written. + -- @function say + -- @int[opt=1] n debugging level, smaller is higher priority + -- @param ... objects to print(as for print) + -- @usage + -- local _debug = require 'std._debug' + -- _debug.level = 3 + -- say(2, '_debug status level:', _debug.level) + say = say, + + --- Trace function calls. + -- Use as debug.sethook(trace, 'cr'), which is done automatically + -- when `std._debug.call` is set. + -- Based on test/trace-calls.lua from the Lua distribution. + -- @function trace + -- @string event event causing the call + -- @usage + -- local _debug = require 'std._debug' + -- _debug.call = true + -- local debug = require 'std.debug' + trace = trace, +} + + +--- Metamethods +-- @section metamethods + +--- Equivalent to calling `debug.say(1, ...)` +-- @function __call +-- @see say +-- @usage +-- local debug = require 'std.debug' +-- debug 'oh noes!' +local metatable = { + __call = function(self, ...) + M.say(1, ...) + end, +} + + +return setmetatable(merge(debug, M), metatable) diff --git a/Data/BuiltIn/Libraries/lua-stdlib/lib/std/init.lua b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/init.lua new file mode 100644 index 0000000..732d41f --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/init.lua @@ -0,0 +1,389 @@ +--[[ + General Lua Libraries for Lua 5.1, 5.2 & 5.3 + Copyright (C) 2002-2018 stdlib authors +]] +--[[-- + Enhanced Lua core functions, and others. + + After requiring this module, simply referencing symbols in the submodule + hierarchy will load the necessary modules on demand. There are no + changes to any global symbols, or monkey patching of core module tables + and metatables. + + @todo Write a style guide(indenting/wrapping, capitalisation, + function and variable names); library functions should call + error, not die; OO vs non-OO(a thorny problem). + @todo pre-compile. + @corefunction std +]] + + +local _ = require 'std._base' + +local argscheck = _.typecheck and _.typecheck.argscheck +local compare = _.list.compare +local maxn = _.table.maxn +local split = _.string.split + +_ = nil + + +local _ENV = require 'std.normalize' { + format = 'string.format', + match = 'string.match', +} + + + +--[[ =============== ]]-- +--[[ Implementation. ]]-- +--[[ =============== ]]-- + + +local M + + +local function _assert(expect, fmt, arg1, ...) + local msg =(arg1 ~= nil) and format(fmt, arg1, ...) or fmt or '' + return expect or error(msg, 2) +end + + +local function elems(t) + -- capture pairs iterator initial state + local fn, istate, ctrl = pairs(t) + return function(state, _) + local v + ctrl, v = fn(state, ctrl) + if ctrl then + return v + end + end, istate, true -- wrapped initial state +end + + +local function eval(s) + return load('return ' .. s)() +end + + +local function ielems(t) + -- capture pairs iterator initial state + local fn, istate, ctrl = ipairs(t) + return function(state, _) + local v + ctrl, v = fn(state, ctrl) + if ctrl then + return v + end + end, istate, true -- wrapped initial state +end + + +local function npairs(t) + local m = getmetamethod(t, '__len') + local i, n = 0, m and m(t) or maxn(t) + return function(t) + i = i + 1 + if i <= n then + return i, t[i] + end + end, + t, i +end + + +local function ripairs(t) + local oob = 1 + while t[oob] ~= nil do + oob = oob + 1 + end + + return function(t, n) + n = n - 1 + if n > 0 then + return n, t[n] + end + end, t, oob +end + + +local function rnpairs(t) + local m = getmetamethod(t, '__len') + local oob =(m and m(t) or maxn(t)) + 1 + + return function(t, n) + n = n - 1 + if n > 0 then + return n, t[n] + end + end, t, oob +end + + +local vconvert = setmetatable({ + string = function(x) + return split(x, '%.') + end, + number = function(x) + return {x} + end, + table = function(x) + return x + end, +}, { + __call = function(self, x) + local fn = self[type(x)] or function() + return 0 + end + return fn(x) + end, +}) + + +local function vcompare(a, b) + return compare(vconvert(a), vconvert(b)) +end + + +local function _require(module, min, too_big, pattern) + pattern = pattern or '([%.%d]+)%D*$' + + local s, m = '', require(module) + if type(m) == 'table' then + s = tostring(m.version or m._VERSION or '') + end + local v = match(s, pattern) or 0 + if min then + _assert(vcompare(v, min) >= 0, "require '" .. module .. + "' with at least version " .. min .. ', but found version ' .. v) + end + if too_big then + _assert(vcompare(v, too_big) < 0, "require '" .. module .. + "' with version less than " .. too_big .. ', but found version ' .. v) + end + return m +end + + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X(decl, fn) + return argscheck and argscheck('std.' .. decl, fn) or fn +end + +M = { + --- Release version string. + -- @field version + + + --- Core Functions + -- @section corefuncs + + --- Enhance core `assert` to also allow formatted arguments. + -- @function assert + -- @param expect expression, expected to be *truthy* + -- @string[opt=''] f format string + -- @param[opt] ... arguments to format + -- @return value of *expect*, if *truthy* + -- @usage + -- std.assert(expect == nil, '100% unexpected!') + -- std.assert(expect == 'expect', '%s the unexpected!', expect) + assert = X('assert(?any, ?string, [any...])', _assert), + + --- Evaluate a string as Lua code. + -- @function eval + -- @string s string of Lua code + -- @return result of evaluating `s` + -- @usage + -- --> 2 + -- std.eval 'math.min(2, 10)' + eval = X('eval(string)', eval), + + --- Return named metamethod, if any, otherwise `nil`. + -- The value found at the given key in the metatable of *x* must be a + -- function or have its own `__call` metamethod to qualify as a + -- callable. Any other value found at key *n* will cause this function + -- to return `nil`. + -- @function getmetamethod + -- @param x item to act on + -- @string n name of metamethod to lookup + -- @treturn callable|nil callable metamethod, or `nil` if no metamethod + -- @usage + -- clone = std.getmetamethod(std.object.prototype, '__call') + getmetamethod = X('getmetamethod(?any, string)', getmetamethod), + + + --- Module Functions + -- @section modulefuncs + + --- Enhance core `require` to assert version number compatibility. + -- By default match against the last substring of(dot-delimited) + -- digits in the module version string. + -- @function require + -- @string module module to require + -- @string[opt] min lowest acceptable version + -- @string[opt] too_big lowest version that is too big + -- @string[opt] pattern to match version in `module.version` or + -- `module._VERSION`(default: `'([%.%d]+)%D*$'`) + -- @usage + -- -- posix.version == 'posix library for Lua 5.2 / 32' + -- posix = require('posix', '29') + require = X('require(string, ?string, ?string, ?string)', _require), + + --- Iterator Functions + -- @section iteratorfuncs + + --- An iterator over all values of a table. + -- If *t* has a `__pairs` metamethod, use that to iterate. + -- @function elems + -- @tparam table t a table + -- @treturn function iterator function + -- @treturn table *t*, the table being iterated over + -- @return *key*, the previous iteration key + -- @see ielems + -- @see pairs + -- @usage + -- --> foo + -- --> bar + -- --> baz + -- --> 5 + -- std.functional.map(print, std.ielems, {'foo', 'bar', [4]='baz', d=5}) + elems = X('elems(table)', elems), + + --- An iterator over the integer keyed elements of a table. + -- + -- If *t* has a `__len` metamethod, iterate up to the index it + -- returns, otherwise up to the first `nil`. + -- + -- This function does **not** support the Lua 5.2 `__ipairs` metamethod. + -- @function ielems + -- @tparam table t a table + -- @treturn function iterator function + -- @treturn table *t*, the table being iterated over + -- @treturn int *index*, the previous iteration index + -- @see elems + -- @see ipairs + -- @usage + -- --> foo + -- --> bar + -- std.functional.map(print, std.ielems, {'foo', 'bar', [4]='baz', d=5}) + ielems = X('ielems(table)', ielems), + + --- An iterator over integer keyed pairs of a sequence. + -- + -- Like Lua 5.1 and 5.3, this iterator returns successive key-value + -- pairs with integer keys starting at 1, up to the first `nil` valued + -- pair. + -- + -- If there is a `_len` metamethod, keep iterating up to and including + -- that element, regardless of any intervening `nil` values. + -- + -- This function does **not** support the Lua 5.2 `__ipairs` metamethod. + -- @function ipairs + -- @tparam table t a table + -- @treturn function iterator function + -- @treturn table *t*, the table being iterated over + -- @treturn int *index*, the previous iteration index + -- @see ielems + -- @see npairs + -- @see pairs + -- @usage + -- --> 1 foo + -- --> 2 bar + -- std.functional.map(print, std.ipairs, {'foo', 'bar', [4]='baz', d=5}) + ipairs = X('ipairs(table)', ipairs), + + --- Ordered iterator for integer keyed values. + -- Like ipairs, but does not stop until the __len or maxn of *t*. + -- @function npairs + -- @tparam table t a table + -- @treturn function iterator function + -- @treturn table t + -- @see ipairs + -- @see rnpairs + -- @usage + -- --> 1 foo + -- --> 2 bar + -- --> 3 nil + -- --> 4 baz + -- std.functional.map(print, std.npairs, {'foo', 'bar', [4]='baz', d=5}) + npairs = X('npairs(table)', npairs), + + --- Enhance core `pairs` to respect `__pairs` even in Lua 5.1. + -- @function pairs + -- @tparam table t a table + -- @treturn function iterator function + -- @treturn table *t*, the table being iterated over + -- @return *key*, the previous iteration key + -- @see elems + -- @see ipairs + -- @usage + -- --> 1 foo + -- --> 2 bar + -- --> 4 baz + -- --> d 5 + -- std.functional.map(print, std.pairs, {'foo', 'bar', [4]='baz', d=5}) + pairs = X('pairs(table)', pairs), + + --- An iterator like ipairs, but in reverse. + -- Apart from the order of the elements returned, this function follows + -- the same rules as @{ipairs} for determining first and last elements. + -- @function ripairs + -- @tparam table t any table + -- @treturn function iterator function + -- @treturn table *t* + -- @treturn number `#t + 1` + -- @see ipairs + -- @see rnpairs + -- @usage + -- --> 2 bar + -- --> 1 foo + -- std.functional.map(print, std.ripairs, {'foo', 'bar', [4]='baz', d=5}) + ripairs = X('ripairs(table)', ripairs), + + --- An iterator like npairs, but in reverse. + -- Apart from the order of the elements returned, this function follows + -- the same rules as @{npairs} for determining first and last elements. + -- @function rnpairs + -- @tparam table t a table + -- @treturn function iterator function + -- @treturn table t + -- @see npairs + -- @see ripairs + -- @usage + -- --> 4 baz + -- --> 3 nil + -- --> 2 bar + -- --> 1 foo + -- std.functional.map(print, std.rnpairs, {'foo', 'bar', [4]='baz', d=5}) + rnpairs = X('rnpairs(table)', rnpairs), +} + + +--- Metamethods +-- @section Metamethods + +return setmetatable(M, { + --- Lazy loading of stdlib modules. + -- Don't load everything on initial startup, wait until first attempt + -- to access a submodule, and then load it on demand. + -- @function __index + -- @string name submodule name + -- @treturn table|nil the submodule that was loaded to satisfy the missing + -- `name`, otherwise `nil` if nothing was found + -- @usage + -- local std = require 'std' + -- local Object = std.object.prototype + __index = function(self, name) + local ok, t = pcall(require, 'std.' .. name) + if ok then + rawset(self, name, t) + return t + end + end, +}) diff --git a/Data/BuiltIn/Libraries/lua-stdlib/lib/std/io.lua b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/io.lua new file mode 100644 index 0000000..1a2b79f --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/io.lua @@ -0,0 +1,322 @@ +--[[ + General Lua Libraries for Lua 5.1, 5.2 & 5.3 + Copyright (C) 2002-2018 stdlib authors +]] +--[[-- + Additions to the core io module. + + The module table returned by `std.io` also contains all of the entries from + the core `io` module table. An hygienic way to import this module, then, + is simply to override core `io` locally: + + local io = require 'std.io' + + @corelibrary std.io +]] + + +local _ = require 'std._base' + +local argscheck = _.typecheck and _.typecheck.argscheck +local catfile = _.io.catfile +local leaves = _.tree.leaves +local split = _.string.split + +_ = nil + + +local _ENV = require 'std.normalize' { + 'io', + _G = _G, -- FIXME: don't use the host _G as an API! + concat = 'table.concat', + dirsep = 'package.dirsep', + format = 'string.format', + gsub = 'string.gsub', + input = 'io.input', + insert = 'table.insert', + io_type = 'io.type', + merge = 'table.merge', + open = 'io.open', + output = 'io.output', + popen = 'io.popen', + stderr = 'io.stderr', + stdin = 'io.stdin', + write = 'io.write', +} + + +--[[ =============== ]]-- +--[[ Implementation. ]]-- +--[[ =============== ]]-- + + +local M + + +local function input_handle(h) + if h == nil then + return input() + elseif type(h) == 'string' then + return open(h) + end + return h +end + + +local function slurp(file) + local h, err = input_handle(file) + if h == nil then + argerror('std.io.slurp', 1, err, 2) + end + + if h then + local s = h:read('*a') + h:close() + return s + end +end + + +local function readlines(file) + local h, err = input_handle(file) + if h == nil then + argerror('std.io.readlines', 1, err, 2) + end + + local l = {} + for line in h:lines() do + l[#l + 1] = line + end + h:close() + return l +end + + +local function writelines(h, ...) + if io_type(h) ~= 'file' then + write(h, '\n') + h = output() + end + for v in leaves(ipairs, {...}) do + h:write(v, '\n') + end +end + + +local function process_files(fn) + -- N.B. 'arg' below refers to the global array of command-line args + if len(arg) == 0 then + insert(arg, '-') + end + for i, v in ipairs(arg) do + if v == '-' then + input(stdin) + else + input(v) + end + fn(v, i) + end +end + + +local function warnfmt(msg, ...) + local prefix = '' + local prog = rawget(_G, 'prog') or {} + local opts = rawget(_G, 'opts') or {} + if prog.name then + prefix = prog.name .. ':' + if prog.line then + prefix = prefix .. str(prog.line) .. ':' + end + elseif prog.file then + prefix = prog.file .. ':' + if prog.line then + prefix = prefix .. str(prog.line) .. ':' + end + elseif opts.program then + prefix = opts.program .. ':' + if opts.line then + prefix = prefix .. str(opts.line) .. ':' + end + end + if #prefix > 0 then + prefix = prefix .. ' ' + end + return prefix .. format(msg, ...) +end + + +local function warn(msg, ...) + writelines(stderr, warnfmt(msg, ...)) +end + + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X(decl, fn) + return argscheck and argscheck('std.io.' .. decl, fn) or fn +end + + +M = { + --- Diagnostic functions + -- @section diagnosticfuncs + + --- Die with error. + -- This function uses the same rules to build a message prefix + -- as @{warn}. + -- @function die + -- @string msg format string + -- @param ... additional arguments to plug format string specifiers + -- @see warn + -- @usage + -- die('oh noes!(%s)', tostring(obj)) + die = X('die(string, [any...])', function(...) + error(warnfmt(...), 0) + end), + + --- Give warning with the name of program and file(if any). + -- If there is a global `prog` table, prefix the message with + -- `prog.name` or `prog.file`, and `prog.line` if any. Otherwise + -- if there is a global `opts` table, prefix the message with + -- `opts.program` and `opts.line` if any. + -- @function warn + -- @string msg format string + -- @param ... additional arguments to plug format string specifiers + -- @see die + -- @usage + -- local OptionParser = require 'std.optparse' + -- local parser = OptionParser 'eg 0\nUsage: eg\n' + -- _G.arg, _G.opts = parser:parse(_G.arg) + -- if not _G.opts.keep_going then + -- require 'std.io'.warn 'oh noes!' + -- end + warn = X('warn(string, [any...])', warn), + + + --- Path Functions + -- @section pathfuncs + + --- Concatenate directory names into a path. + -- @function catdir + -- @string ... path components + -- @return path without trailing separator + -- @see catfile + -- @usage + -- dirpath = catdir('', 'absolute', 'directory') + catdir = X('catdir(string...)', function(...) + return(gsub(concat({...}, dirsep), '^$', dirsep)) + end), + + --- Concatenate one or more directories and a filename into a path. + -- @function catfile + -- @string ... path components + -- @treturn string path + -- @see catdir + -- @see splitdir + -- @usage + -- filepath = catfile('relative', 'path', 'filename') + catfile = X('catfile(string...)', catfile), + + --- Remove the last dirsep delimited element from a path. + -- @function dirname + -- @string path file path + -- @treturn string a new path with the last dirsep and following + -- truncated + -- @usage + -- dir = dirname '/base/subdir/filename' + dirname = X('dirname(string)', function(path) + return(gsub(path, catfile('', '[^', ']*$'), '')) + end), + + --- Split a directory path into components. + -- Empty components are retained: the root directory becomes `{'', ''}`. + -- @function splitdir + -- @param path path + -- @return list of path components + -- @see catdir + -- @usage + -- dir_components = splitdir(filepath) + splitdir = X('splitdir(string)', function(path) + return split(path, dirsep) + end), + + + --- IO Functions + -- @section iofuncs + + --- Process files specified on the command-line. + -- Each filename is made the default input source with `io.input`, and + -- then the filename and argument number are passed to the callback + -- function. In list of filenames, `-` means `io.stdin`. If no + -- filenames were given, behave as if a single `-` was passed. + -- @todo Make the file list an argument to the function. + -- @function process_files + -- @tparam fileprocessor fn function called for each file argument + -- @usage + -- #! /usr/bin/env lua + -- -- minimal cat command + -- local io = require 'std.io' + -- io.process_files(function() io.write(io.slurp()) end) + process_files = X('process_files(function)', process_files), + + --- Read a file or file handle into a list of lines. + -- The lines in the returned list are not `\n` terminated. + -- @function readlines + -- @tparam[opt=io.input()] file|string file file handle or name; + -- if file is a file handle, that file is closed after reading + -- @treturn list lines + -- @usage + -- list = readlines '/etc/passwd' + readlines = X('readlines(?file|string)', readlines), + + --- Perform a shell command and return its output. + -- @function shell + -- @string c command + -- @treturn string output, or nil if error + -- @see os.execute + -- @usage + -- users = shell [[cat /etc/passwd | awk -F: '{print $1;}']] + shell = X('shell(string)', function(c) return slurp(popen(c)) end), + + --- Slurp a file handle. + -- @function slurp + -- @tparam[opt=io.input()] file|string file file handle or name; + -- if file is a file handle, that file is closed after reading + -- @return contents of file or handle, or nil if error + -- @see process_files + -- @usage + -- contents = slurp(filename) + slurp = X('slurp(?file|string)', slurp), + + --- Write values adding a newline after each. + -- @function writelines + -- @tparam[opt=io.output()] file h open writable file handle; + -- the file is **not** closed after writing + -- @tparam string|number ... values to write(as for write) + -- @usage + -- writelines(io.stdout, 'first line', 'next line') + writelines = X('writelines(?file|string|number, [string|number...])', writelines), +} + + +return merge(io, M) + + + +--- Types +-- @section Types + +--- Signature of @{process_files} callback function. +-- @function fileprocessor +-- @string filename filename +-- @int i argument number of *filename* +-- @usage +-- local fileprocessor = function(filename, i) +-- io.write(tostring(i) .. ':\n===\n' .. io.slurp(filename) .. '\n') +-- end +-- io.process_files(fileprocessor) diff --git a/Data/BuiltIn/Libraries/lua-stdlib/lib/std/math.lua b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/math.lua new file mode 100644 index 0000000..d955862 --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/math.lua @@ -0,0 +1,92 @@ +--[[ + General Lua Libraries for Lua 5.1, 5.2 & 5.3 + Copyright (C) 2002-2018 stdlib authors +]] +--[[-- + Additions to the core math module. + + The module table returned by `std.math` also contains all of the entries from + the core math table. An hygienic way to import this module, then, is simply + to override the core `math` locally: + + local math = require 'std.math' + + @corelibrary std.math +]] + + +local _ = require 'std._base' + +local argscheck = _.typecheck and _.typecheck.argscheck + +_ = nil + + +local _ENV = require 'std.normalize' { + 'math', + merge = 'table.merge', +} + + + +--[[ ================= ]]-- +--[[ Implementatation. ]]-- +--[[ ================= ]]-- + + +local M + + +local _floor = math.floor + +local function floor(n, p) + if(p or 0) == 0 then + return _floor(n) + end + local e = 10 ^ p + return _floor(n * e) / e +end + + +local function round(n, p) + local e = 10 ^(p or 0) + return _floor(n * e + 0.5) / e +end + + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X(decl, fn) + return argscheck and argscheck('std.math.' .. decl, fn) or fn +end + + +M = { + --- Core Functions + -- @section corefuncs + + --- Extend `math.floor` to take the number of decimal places. + -- @function floor + -- @number n number + -- @int[opt=0] p number of decimal places to truncate to + -- @treturn number `n` truncated to `p` decimal places + -- @usage + -- tenths = floor(magnitude, 1) + floor = X('floor(number, ?int)', floor), + + --- Round a number to a given number of decimal places. + -- @function round + -- @number n number + -- @int[opt=0] p number of decimal places to round to + -- @treturn number `n` rounded to `p` decimal places + -- @usage + -- roughly = round(exactly, 2) + round = X('round(number, ?int)', round), +} + + +return merge(math, M) diff --git a/Data/BuiltIn/Libraries/lua-stdlib/lib/std/package.lua b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/package.lua new file mode 100644 index 0000000..e3e8243 --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/package.lua @@ -0,0 +1,263 @@ +--[[ + General Lua Libraries for Lua 5.1, 5.2 & 5.3 + Copyright (C) 2002-2018 stdlib authors +]] +--[[-- + Additions to the core package module. + + The module table returned by `std.package` also contains all of the entries + from the core `package` table. An hygienic way to import this module, then, is + simply to override core `package` locally: + + local package = require 'std.package' + + Manage `package.path` with normalization, duplicate removal, + insertion & removal of elements and automatic folding of '/' and '?' + onto `package.dirsep` and `package.pathmark`, for easy addition of + new paths. For example, instead of all this: + + lib = std.io.catfile('.', 'lib', package.pathmark .. '.lua') + paths = std.string.split(package.path, package.pathsep) + for i, path in ipairs(paths) do + -- ... lots of normalization code... + end + i = 1 + while i <= #paths do + if paths[i] == lib then + table.remove(paths, i) + else + i = i + 1 + end + end + table.insert(paths, 1, lib) + package.path = table.concat(paths, package.pathsep) + + You can now write just: + + package.path = package.normalize('./lib/?.lua', package.path) + + @corelibrary std.package +]] + + +local _ = require 'std._base' + +local argscheck = _.typecheck and _.typecheck.argscheck +local catfile = _.io.catfile +local escape_pattern = _.string.escape_pattern +local invert = _.table.invert +local split = _.string.split + +_ = nil + +local _ENV = require 'std.normalize' { + 'package', + concat = 'table.concat', + dirsep = 'package.dirsep', + gsub = 'string.gsub', + merge = 'table.merge', + pathmark = 'package.pathmark', + pathsep = 'package.pathsep', + string_find = 'string.find', + table_insert = 'table.insert', + table_remove = 'table.remove', +} + + + +--[[ =============== ]]-- +--[[ Implementation. ]]-- +--[[ =============== ]]-- + + +--- Make named constants for `package.config` +-- (undocumented in 5.1; see luaconf.h for C equivalents). +-- @table package +-- @string dirsep directory separator +-- @string pathsep path separator +-- @string pathmark string that marks substitution points in a path template +-- @string execdir(Windows only) replaced by the executable's directory in a path +-- @string igmark Mark to ignore all before it when building `luaopen_` function name. + + +local function pathsub(path) + return gsub(path, '%%?.', function(capture) + if capture == '?' then + return pathmark + elseif capture == '/' then + return dirsep + else + return gsub(capture, '^%%', '', 1) + end + end) +end + + +local function find(pathstrings, patt, init, plain) + local paths = split(pathstrings, pathsep) + if plain then + patt = escape_pattern(patt) + end + init = init or 1 + if init < 0 then + init = #paths - init + end + for i = init, #paths do + if string_find(paths[i], patt) then + return i, paths[i] + end + end +end + + +local function normalize(...) + local i, paths, pathstrings = 1, {}, concat({...}, pathsep) + for _, path in ipairs(split(pathstrings, pathsep)) do + path = gsub(pathsub(path), catfile('^[^', ']'), catfile('.', '%0')) + path = gsub(path, catfile('', '%.', ''), dirsep) + path = gsub(path, catfile('', '%.$'), '') + path = gsub(path, catfile('^%.', '%..', ''), catfile('..', '')) + path = gsub(path, catfile('', '$'), '') + + -- Carefully remove redundant /foo/../ matches. + repeat + local again = false + path = gsub(path, catfile('', '([^', ']+)', '%.%.', ''), + function(dir1) + if dir1 == '..' then -- don't remove /../../ + return catfile('', '..', '..', '') + else + again = true + return dirsep + end + end) + path = gsub(path, catfile('', '([^', ']+)', '%.%.$'), + function(dir1) + if dir1 == '..' then -- don't remove /../.. + return catfile('', '..', '..') + else + again = true + return '' + end + end) + until again == false + + -- Build an inverted table of elements to eliminate duplicates after + -- normalization. + if not paths[path] then + paths[path], i = i, i + 1 + end + end + return concat(invert(paths), pathsep) +end + + +local function insert(pathstrings, ...) + local paths = split(pathstrings, pathsep) + table_insert(paths, ...) + return normalize(unpack(paths, 1, len(paths))) +end + + +local function mappath(pathstrings, callback, ...) + for _, path in ipairs(split(pathstrings, pathsep)) do + local r = callback(path, ...) + if r ~= nil then + return r + end + end +end + + +local function remove(pathstrings, pos) + local paths = split(pathstrings, pathsep) + table_remove(paths, pos) + return concat(paths, pathsep) +end + + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X(decl, fn) + return argscheck and argscheck('std.package.' .. decl, fn) or fn +end + + +local M = { + --- Look for a path segment match of *patt* in *pathstrings*. + -- @function find + -- @string pathstrings `pathsep` delimited path elements + -- @string patt a Lua pattern to search for in *pathstrings* + -- @int[opt=1] init element(not byte index!) to start search at. + -- Negative numbers begin counting backwards from the last element + -- @bool[opt=false] plain unless false, treat *patt* as a plain + -- string, not a pattern. Note that if *plain* is given, then *init* + -- must be given as well. + -- @return the matching element number(not byte index!) and full text + -- of the matching element, if any; otherwise nil + -- @usage + -- i, s = find(package.path, '^[^' .. package.dirsep .. '/]') + find = X('find(string, string, ?int, ?boolean|:plain)', find), + + --- Insert a new element into a `package.path` like string of paths. + -- @function insert + -- @string pathstrings a `package.path` like string + -- @int[opt=n+1] pos element index at which to insert *value*, where `n` is + -- the number of elements prior to insertion + -- @string value new path element to insert + -- @treturn string a new string with the new element inserted + -- @usage + -- package.path = insert(package.path, 1, install_dir .. '/?.lua') + insert = X('insert(string, [int], string)', insert), + + --- Call a function with each element of a path string. + -- @function mappath + -- @string pathstrings a `package.path` like string + -- @tparam mappathcb callback function to call for each element + -- @param ... additional arguments passed to *callback* + -- @return nil, or first non-nil returned by *callback* + -- @usage + -- mappath(package.path, searcherfn, transformfn) + mappath = X('mappath(string, function, [any...])', mappath), + + --- Normalize a path list. + -- Removing redundant `.` and `..` directories, and keep only the first + -- instance of duplicate elements. Each argument can contain any number + -- of `pathsep` delimited elements; wherein characters are subject to + -- `/` and `?` normalization, converting `/` to `dirsep` and `?` to + -- `pathmark`(unless immediately preceded by a `%` character). + -- @function normalize + -- @param ... path elements + -- @treturn string a single normalized `pathsep` delimited paths string + -- @usage + -- package.path = normalize(user_paths, sys_paths, package.path) + normalize = X('normalize(string...)', normalize), + + --- Remove any element from a `package.path` like string of paths. + -- @function remove + -- @string pathstrings a `package.path` like string + -- @int[opt=n] pos element index from which to remove an item, where `n` + -- is the number of elements prior to removal + -- @treturn string a new string with given element removed + -- @usage + -- package.path = remove(package.path) + remove = X('remove(string, ?int)', remove), +} + + +return merge(package, M) + + +--- Types +-- @section Types + +--- Function signature of a callback for @{mappath}. +-- @function mappathcb +-- @string element an element from a `pathsep` delimited string of +-- paths +-- @param ... additional arguments propagated from @{mappath} +-- @return non-nil to break, otherwise continue with the next element diff --git a/Data/BuiltIn/Libraries/lua-stdlib/lib/std/string.lua b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/string.lua new file mode 100644 index 0000000..6ad9014 --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/string.lua @@ -0,0 +1,505 @@ +--[[ + General Lua Libraries for Lua 5.1, 5.2 & 5.3 + Copyright (C) 2002-2018 stdlib authors +]] +--[[-- + Additions to the core string module. + + The module table returned by `std.string` also contains all of the entries + from the core string table. An hygienic way to import this module, then, is + simply to override the core `string` locally: + + local string = require 'std.string' + + @corelibrary std.string +]] + + +local _ = require 'std._base' + +local argscheck = _.typecheck and _.std.typecheck.argscheck +local escape_pattern = _.string.escape_pattern +local split = _.string.split + +_ = nil + + +local _ENV = require 'std.normalize' { + 'string', + abs = 'math.abs', + concat = 'table.concat', + find = 'string.find', + floor = 'math.floor', + format = 'string.format', + gsub = 'string.gsub', + insert = 'table.insert', + match = 'string.match', + merge = 'table.merge', + render = 'string.render', + sort = 'table.sort', + sub = 'string.sub', + upper = 'string.upper', +} + + + +--[[ =============== ]]-- +--[[ Implementation. ]]-- +--[[ =============== ]]-- + + +local M + + +local function toqstring(x, xstr) + if type(x) ~= 'string' then + return xstr + end + return format('%q', x) +end + + +local concatvfns = { + elem = tostring, + term = function(x) + return type(x) ~= 'table' or getmetamethod(x, '__tostring') + end, + sort = function(keys) + return keys + end, + open = function(x) return '{' end, + close = function(x) return '}' end, + pair = function(x, kp, vp, k, v, kstr, vstr, seqp) + return toqstring(k, kstr) .. '=' .. toqstring(v, vstr) + end, + sep = function(x, kp, vp, kn, vn, seqp) + return kp ~= nil and kn ~= nil and ',' or '' + end, +} + + +local function __concat(s, o) + -- Don't use normalize.str here, because we don't want ASCII escape rendering. + return render(s, concatvfns) .. render(o, concatvfns) +end + + +local function __index(s, i) + if type(i) == 'number' then + return sub(s, i, i) + else + -- Fall back to module metamethods + return M[i] + end +end + + +local _format = string.format + +local function format(f, arg1, ...) + return(arg1 ~= nil) and _format(f, arg1, ...) or f +end + + +local function tpack(from, to, ...) + return from, to, {...} +end + +local function tfind(s, ...) + return tpack(find(s, ...)) +end + + +local function finds(s, p, i, ...) + i = i or 1 + local l = {} + local from, to, r + repeat + from, to, r = tfind(s, p, i, ...) + if from ~= nil then + insert(l, {from, to, capt=r}) + i = to + 1 + end + until not from + return l +end + + +local function caps(s) + return(gsub(s, '(%w)([%w]*)', function(l, ls) + return upper(l) .. ls + end)) +end + + +local function escape_shell(s) + return(gsub(s, '([ %(%)%\\%[%]\'"])', '\\%1')) +end + + +local function ordinal_suffix(n) + n = abs(n) % 100 + local d = n % 10 + if d == 1 and n ~= 11 then + return 'st' + elseif d == 2 and n ~= 12 then + return 'nd' + elseif d == 3 and n ~= 13 then + return 'rd' + else + return 'th' + end +end + + +local function pad(s, w, p) + p = string.rep(p or ' ', abs(w)) + if w < 0 then + return string.sub(p .. s, w) + end + return string.sub(s .. p, 1, w) +end + + +local function wrap(s, w, ind, ind1) + w = w or 78 + ind = ind or 0 + ind1 = ind1 or ind + assert(ind1 < w and ind < w, + 'the indents must be less than the line width') + local r = {string.rep(' ', ind1)} + local i, lstart, lens = 1, ind1, len(s) + while i <= lens do + local j = i + w - lstart + while len(s[j]) > 0 and s[j] ~= ' ' and j > i do + j = j - 1 + end + local ni = j + 1 + while s[j] == ' ' do + j = j - 1 + end + insert(r, sub(s, i, j)) + i = ni + if i < lens then + insert(r, '\n' .. string.rep(' ', ind)) + lstart = ind + end + end + return concat(r) +end + + +local function numbertosi(n) + local SIprefix = { + [-8]='y', [-7]='z', [-6]='a', [-5]='f', + [-4]='p', [-3]='n', [-2]='mu', [-1]='m', + [0]='', [1]='k', [2]='M', [3]='G', + [4]='T', [5]='P', [6]='E', [7]='Z', + [8]='Y' + } + local t = _format('% #.2e', n) + local _, _, m, e = find(t, '.(.%...)e(.+)') + local man, exp = tonumber(m), tonumber(e) + local siexp = floor(exp / 3) + local shift = exp - siexp * 3 + local s = SIprefix[siexp] or 'e' .. tostring(siexp) + man = man *(10 ^ shift) + return _format('%0.f', man) .. s +end + + +-- Ordor numbers first then asciibetically. +local function keycmp(a, b) + if type(a) == 'number' then + return type(b) ~= 'number' or a < b + end + return type(b) ~= 'number' and tostring(a) < tostring(b) +end + + +local render_fallbacks = { + __index = concatvfns, +} + + +local function prettytostring(x, indent, spacing) + indent = indent or '\t' + spacing = spacing or '' + return render(x, setmetatable({ + elem = function(x) + if type(x) ~= 'string' then + return tostring(x) + end + return format('%q', x) + end, + + sort = function(keylist) + sort(keylist, keycmp) + return keylist + end, + + open = function() + local s = spacing .. '{' + spacing = spacing .. indent + return s + end, + + close = function() + spacing = string.gsub(spacing, indent .. '$', '') + return spacing .. '}' + end, + + pair = function(x, _, _, k, v, kstr, vstr) + local type_k = type(k) + local s = spacing + if type_k ~= 'string' or match(k, '[^%w_]') then + s = s .. '[' + if type_k == 'table' then + s = s .. '\n' + end + s = s .. kstr + if type_k == 'table' then + s = s .. '\n' + end + s = s .. ']' + else + s = s .. k + end + s = s .. ' =' + if type(v) == 'table' then + s = s .. '\n' + else + s = s .. ' ' + end + s = s .. vstr + return s + end, + + sep = function(_, k) + local s = '\n' + if k then + s = ',' .. s + end + return s + end, + }, render_fallbacks)) +end + + +local function trim(s, r) + r = r or '%s+' + return (gsub(gsub(s, '^' .. r, ''), r .. '$', '')) +end + + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X(decl, fn) + return argscheck and argscheck('std.string.' .. decl, fn) or fn +end + +M = { + --- Metamethods + -- @section metamethods + + --- String concatenation operation. + -- @function __concat + -- @string s initial string + -- @param o object to stringify and concatenate + -- @return s .. tostring(o) + -- @usage + -- local string = setmetatable('', require 'std.string') + -- concatenated = 'foo' .. {'bar'} + __concat = __concat, + + --- String subscript operation. + -- @function __index + -- @string s string + -- @tparam int|string i index or method name + -- @return `sub(s, i, i)` if i is a number, otherwise + -- fall back to a `std.string` metamethod(if any). + -- @usage + -- getmetatable('').__index = require 'std.string'.__index + -- third =('12345')[3] + __index = __index, + + + --- Core Functions + -- @section corefuncs + + --- Capitalise each word in a string. + -- @function caps + -- @string s any string + -- @treturn string *s* with each word capitalized + -- @usage + -- userfullname = caps(input_string) + caps = X('caps(string)', caps), + + --- Remove any final newline from a string. + -- @function chomp + -- @string s any string + -- @treturn string *s* with any single trailing newline removed + -- @usage + -- line = chomp(line) + chomp = X('chomp(string)', function(s) + return(gsub(s, '\n$', '')) + end), + + --- Escape a string to be used as a pattern. + -- @function escape_pattern + -- @string s any string + -- @treturn string *s* with active pattern characters escaped + -- @usage + -- substr = match(inputstr, escape_pattern(literal)) + escape_pattern = X('escape_pattern(string)', escape_pattern), + + --- Escape a string to be used as a shell token. + -- Quotes spaces, parentheses, brackets, quotes, apostrophes and + -- whitespace. + -- @function escape_shell + -- @string s any string + -- @treturn string *s* with active shell characters escaped + -- @usage + -- os.execute('echo ' .. escape_shell(outputstr)) + escape_shell = X('escape_shell(string)', escape_shell), + + --- Repeatedly `string.find` until target string is exhausted. + -- @function finds + -- @string s target string + -- @string pattern pattern to match in *s* + -- @int[opt=1] init start position + -- @bool[opt] plain inhibit magic characters + -- @return list of `{from, to; capt={captures}}` + -- @see std.string.tfind + -- @usage + -- for t in std.elems(finds('the target string', '%S+')) do + -- print(tostring(t.capt)) + -- end + finds = X('finds(string, string, ?int, ?boolean|:plain)', finds), + + --- Extend to work better with one argument. + -- If only one argument is passed, no formatting is attempted. + -- @function format + -- @string f format string + -- @param[opt] ... arguments to format + -- @return formatted string + -- @usage + -- print(format '100% stdlib!') + format = X('format(string, [any...])', format), + + --- Remove leading matter from a string. + -- @function ltrim + -- @string s any string + -- @string[opt='%s+'] r leading pattern + -- @treturn string *s* with leading *r* stripped + -- @usage + -- print('got: ' .. ltrim(userinput)) + ltrim = X('ltrim(string, ?string)', function(s, r) + return (gsub(s, '^' ..(r or '%s+'), '')) + end), + + --- Write a number using SI suffixes. + -- The number is always written to 3 s.f. + -- @function numbertosi + -- @tparam number|string n any numeric value + -- @treturn string *n* simplifed using largest available SI suffix. + -- @usage + -- print(numbertosi(bitspersecond) .. 'bps') + numbertosi = X('numbertosi(number|string)', numbertosi), + + --- Return the English suffix for an ordinal. + -- @function ordinal_suffix + -- @tparam int|string n any integer value + -- @treturn string English suffix for *n* + -- @usage + -- local now = os.date '*t' + -- print('%d%s day of the week', now.day, ordinal_suffix(now.day)) + ordinal_suffix = X('ordinal_suffix(int|string)', ordinal_suffix), + + --- Justify a string. + -- When the string is longer than w, it is truncated(left or right + -- according to the sign of w). + -- @function pad + -- @string s a string to justify + -- @int w width to justify to(-ve means right-justify; +ve means + -- left-justify) + -- @string[opt=' '] p string to pad with + -- @treturn string *s* justified to *w* characters wide + -- @usage + -- print(pad(trim(outputstr, 78)) .. '\n') + pad = X('pad(string, int, ?string)', pad), + + --- Pretty-print a table, or other object. + -- @function prettytostring + -- @param x object to convert to string + -- @string[opt='\t'] indent indent between levels + -- @string[opt=''] spacing space before every line + -- @treturn string pretty string rendering of *x* + -- @usage + -- print(prettytostring(std, ' ')) + prettytostring = X('prettytostring(?any, ?string, ?string)', prettytostring), + + --- Remove trailing matter from a string. + -- @function rtrim + -- @string s any string + -- @string[opt='%s+'] r trailing pattern + -- @treturn string *s* with trailing *r* stripped + -- @usage + -- print('got: ' .. rtrim(userinput)) + rtrim = X('rtrim(string, ?string)', function(s, r) + return (gsub(s, (r or '%s+') .. '$', '')) + end), + + --- Split a string at a given separator. + -- Separator is a Lua pattern, so you have to escape active characters, + -- `^$()%.[]*+-?` with a `%` prefix to match a literal character in *s*. + -- @function split + -- @string s to split + -- @string[opt='%s+'] sep separator pattern + -- @return list of strings + -- @usage + -- words = split 'a very short sentence' + split = X('split(string, ?string)', split), + + --- Do `string.find`, returning a table of captures. + -- @function tfind + -- @string s target string + -- @string pattern pattern to match in *s* + -- @int[opt=1] init start position + -- @bool[opt] plain inhibit magic characters + -- @treturn int start of match + -- @treturn int end of match + -- @treturn table list of captured strings + -- @see std.string.finds + -- @usage + -- b, e, captures = tfind('the target string', '%s', 10) + tfind = X('tfind(string, string, ?int, ?boolean|:plain)', tfind), + + --- Remove leading and trailing matter from a string. + -- @function trim + -- @string s any string + -- @string[opt='%s+'] r trailing pattern + -- @treturn string *s* with leading and trailing *r* stripped + -- @usage + -- print('got: ' .. trim(userinput)) + trim = X('trim(string, ?string)', trim), + + --- Wrap a string into a paragraph. + -- @function wrap + -- @string s a paragraph of text + -- @int[opt=78] w width to wrap to + -- @int[opt=0] ind indent + -- @int[opt=ind] ind1 indent of first line + -- @treturn string *s* wrapped to *w* columns + -- @usage + -- print(wrap(copyright, 72, 4)) + wrap = X('wrap(string, ?int, ?int, ?int)', wrap), +} + + +return merge(string, M) + diff --git a/Data/BuiltIn/Libraries/lua-stdlib/lib/std/table.lua b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/table.lua new file mode 100644 index 0000000..7bda608 --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-stdlib/lib/std/table.lua @@ -0,0 +1,439 @@ +--[[ + General Lua Libraries for Lua 5.1, 5.2 & 5.3 + Copyright (C) 2002-2018 stdlib authors +]] +--[[-- + Extensions to the core table module. + + The module table returned by `std.table` also contains all of the entries from + the core table module. An hygienic way to import this module, then, is simply + to override the core `table` locally: + + local table = require 'std.table' + + @corelibrary std.table +]] + + +local _ = require 'std._base' + +local argscheck = _.typecheck and _.typecheck.argscheck +local invert = _.table.invert +local maxn = _.table.maxn + +_ = nil + +local _ENV = require 'std.normalize' { + 'table', + merge = 'table.merge', + min = 'math.min', +} + + + +--[[ =============== ]]-- +--[[ Implementation. ]]-- +--[[ =============== ]]-- + + +local M + + +local function merge_allfields(t, u, map, nometa) + if type(map) ~= 'table' then + map, nometa = nil, map + end + + if not nometa then + setmetatable(t, getmetatable(u)) + end + if map then + for k, v in pairs(u) do + t[map[k] or k] = v + end + else + for k, v in pairs(u) do + t[k] = v + end + end + return t +end + + +local function merge_namedfields(t, u, keys, nometa) + if type(keys) ~= 'table' then + keys, nometa = nil, keys + end + + if not nometa then + setmetatable(t, getmetatable(u)) + end + for _, k in pairs(keys or {}) do + t[k] = u[k] + end + return t +end + + +local function depair(ls) + local t = {} + for _, v in ipairs(ls) do + t[v[1]] = v[2] + end + return t +end + + +local function enpair(t) + local tt = {} + for i, v in pairs(t) do + tt[#tt + 1] = {i, v} + end + return tt +end + + +local _insert = table.insert + +local function insert(t, pos, v) + if v == nil then + pos, v = len(t) + 1, pos + end + if pos < 1 or pos > len(t) + 1 then + argerror('std.table.insert', 2, 'position ' .. pos .. ' out of bounds', 2) + end + _insert(t, pos, v) + return t +end + + +local function keys(t) + local l = {} + for k in pairs(t) do + l[#l + 1] = k + end + return l +end + + +local function new(x, t) + return setmetatable(t or {}, {__index = function(t, i) + return x + end}) +end + + +local function project(fkey, tt) + local r = {} + for _, t in ipairs(tt) do + r[#r + 1] = t[fkey] + end + return r +end + + +local function size(t) + local n = 0 + for _ in pairs(t) do + n = n + 1 + end + return n +end + + +-- Preserve core table sort function. +local _sort = table.sort + +local function sort(t, c) + _sort(t, c) + return t +end + + +local _remove = table.remove + +local function remove(t, pos) + local lent = len(t) + pos = pos or lent + if pos < min(1, lent) or pos > lent + 1 then -- +1? whu? that's what 5.2.3 does!?! + argerror('std.table.remove', 2, 'position ' .. pos .. ' out of bounds', 2) + end + return _remove(t, pos) +end + + +local _unpack = unpack + +local function unpack(t, i, j) + if j == nil then + -- if j was not given, respect __len, otherwise use maxn + local m = getmetamethod(t, '__len') + j = m and m(t) or maxn(t) + end + return _unpack(t, tonumber(i) or 1, tonumber(j)) +end + + +local function values(t) + local l = {} + for _, v in pairs(t) do + l[#l + 1] = v + end + return l +end + + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X(decl, fn) + return argscheck and argscheck('std.table.' .. decl, fn) or fn +end + +M = { + --- Core Functions + -- @section corefuncs + + --- Enhance core *table.insert* to return its result. + -- If *pos* is not given, respect `__len` metamethod when calculating + -- default append. Also, diagnose out of bounds *pos* arguments + -- consistently on any supported version of Lua. + -- @function insert + -- @tparam table t a table + -- @int[opt=len(t)] pos index at which to insert new element + -- @param v value to insert into *t* + -- @treturn table *t* + -- @usage + -- --> {1, 'x', 2, 3, 'y'} + -- insert(insert({1, 2, 3}, 2, 'x'), 'y') + insert = X('insert(table, [int], any)', insert), + + --- Largest integer key in a table. + -- @function maxn + -- @tparam table t a table + -- @treturn int largest integer key in *t* + -- @usage + -- --> 42 + -- maxn {'a', b='c', 99, [42]='x', 'x', [5]=67} + maxn = X('maxn(table)', maxn), + + --- Turn a tuple into a list, with tuple-size in field `n` + -- @function pack + -- @param ... tuple + -- @return list-like table, with tuple-size in field `n` + -- @usage + -- --> {1, 2, 'ax', n=3} + -- pack(find('ax1', '(%D+)')) + pack = pack, + + --- Enhance core *table.remove* to respect `__len` when *pos* is omitted. + -- Also, diagnose out of bounds *pos* arguments consistently on any supported + -- version of Lua. + -- @function remove + -- @tparam table t a table + -- @int[opt=len(t)] pos index from which to remove an element + -- @return removed value, or else `nil` + -- @usage + -- --> {1, 2, 5} + -- t = {1, 2, 'x', 5} + -- remove(t, 3) == 'x' and t + remove = X('remove(table, ?int)', remove), + + --- Enhance core *table.sort* to return its result. + -- @function sort + -- @tparam table t unsorted table + -- @tparam[opt=std.operator.lt] comparator c ordering function callback + -- @return *t* with keys sorted according to *c* + -- @usage + -- table.concat(sort(object)) + sort = X('sort(table, ?function)', sort), + + --- Enhance core *table.unpack* to always unpack up to __len or maxn. + -- @function unpack + -- @tparam table t table to act on + -- @int[opt=1] i first index to unpack + -- @int[opt=table.maxn(t)] j last index to unpack + -- @return ... values of numeric indices of *t* + -- @usage + -- return unpack(results_table) + unpack = X('unpack(table, ?int, ?int)', unpack), + + + --- Accessor Functions + -- @section accessorfuncs + + --- Make a shallow copy of a table, including any metatable. + -- @function clone + -- @tparam table t source table + -- @tparam[opt={}] table map table of `{old_key=new_key, ...}` + -- @bool[opt] nometa if non-nil don't copy metatable + -- @return copy of *t*, also sharing *t*'s metatable unless *nometa* + -- is true, and with keys renamed according to *map* + -- @see merge + -- @see clone_select + -- @usage + -- shallowcopy = clone(original, {rename_this='to_this'}, ':nometa') + clone = X('clone(table, [table], ?boolean|:nometa)', function(...) + return merge_allfields({}, ...) + end), + + --- Make a partial clone of a table. + -- + -- Like `clone`, but does not copy any fields by default. + -- @function clone_select + -- @tparam table t source table + -- @tparam[opt={}] table keys list of keys to copy + -- @bool[opt] nometa if non-nil don't copy metatable + -- @treturn table copy of fields in *selection* from *t*, also sharing *t*'s + -- metatable unless *nometa* + -- @see clone + -- @see merge_select + -- @usage + -- partialcopy = clone_select(original, {'this', 'and_this'}, true) + clone_select = X('clone_select(table, [table], ?boolean|:nometa)', function(...) + return merge_namedfields({}, ...) + end), + + --- Turn a list of pairs into a table. + -- @todo Find a better name. + -- @function depair + -- @tparam table ls list of lists + -- @treturn table a flat table with keys and values from *ls* + -- @see enpair + -- @usage + -- --> {a=1, b=2, c=3} + -- depair {{'a', 1}, {'b', 2}, {'c', 3}} + depair = X('depair(list of lists)', depair), + + --- Turn a table into a list of pairs. + -- @todo Find a better name. + -- @function enpair + -- @tparam table t a table `{i1=v1, ..., in=vn}` + -- @treturn table a new list of pairs containing `{{i1, v1}, ..., {in, vn}}` + -- @see depair + -- @usage + -- --> {{1, 'a'}, {2, 'b'}, {3, 'c'}} + -- enpair {'a', 'b', 'c'} + enpair = X('enpair(table)', enpair), + + --- Return whether table is empty. + -- @function empty + -- @tparam table t any table + -- @treturn boolean `true` if *t* is empty, otherwise `false` + -- @usage + -- if empty(t) then error 'ohnoes' end + empty = X('empty(table)', function(t) + return not next(t) + end), + + --- Make a table with a default value for unset keys. + -- @function new + -- @param[opt=nil] x default entry value + -- @tparam[opt={}] table t initial table + -- @treturn table table whose unset elements are *x* + -- @usage + -- t = new(0) + new = X('new(?any, ?table)', new), + + --- Project a list of fields from a list of tables. + -- @function project + -- @param fkey field to project + -- @tparam table tt a list of tables + -- @treturn table list of *fkey* fields from *tt* + -- @usage + -- --> {1, 3, 'yy'} + -- project('xx', {{'a', xx=1, yy='z'}, {'b', yy=2}, {'c', xx=3}, {xx='yy'}) + project = X('project(any, list of tables)', project), + + --- Find the number of elements in a table. + -- @function size + -- @tparam table t any table + -- @treturn int number of non-nil values in *t* + -- @usage + -- --> 3 + -- size {foo=true, bar=true, baz=false} + size = X('size(table)', size), + + --- Make the list of values of a table. + -- @function values + -- @tparam table t any table + -- @treturn table list of values in *t* + -- @see keys + -- @usage + -- --> {'a', 'c', 42} + -- values {'a', b='c', [-1]=42} + values = X('values(table)', values), + + + --- Mutator Functions + -- @section mutatorfuncs + + --- Invert a table. + -- @function invert + -- @tparam table t a table with `{k=v, ...}` + -- @treturn table inverted table `{v=k, ...}` + -- @usage + -- --> {a=1, b=2, c=3} + -- invert {'a', 'b', 'c'} + invert = X('invert(table)', invert), + + --- Make the list of keys in table. + -- @function keys + -- @tparam table t a table + -- @treturn table list of keys from *t* + -- @see values + -- @usage + -- globals = keys(_G) + keys = X('keys(table)', keys), + + --- Destructively merge one table's fields into another. + -- @function merge + -- @tparam table t destination table + -- @tparam table u table with fields to merge + -- @tparam[opt={}] table map table of `{old_key=new_key, ...}` + -- @bool[opt] nometa if `true` or ':nometa' don't copy metatable + -- @treturn table *t* with fields from *u* merged in + -- @see clone + -- @see merge_select + -- @usage + -- merge(_G, require 'std.debug', {say='log'}, ':nometa') + merge = X('merge(table, table, [table], ?boolean|:nometa)', merge_allfields), + + --- Destructively merge another table's named fields into *table*. + -- + -- Like `merge`, but does not merge any fields by default. + -- @function merge_select + -- @tparam table t destination table + -- @tparam table u table with fields to merge + -- @tparam[opt={}] table keys list of keys to copy + -- @bool[opt] nometa if `true` or ':nometa' don't copy metatable + -- @treturn table copy of fields in *selection* from *t*, also sharing *t*'s + -- metatable unless *nometa* + -- @see merge + -- @see clone_select + -- @usage + -- merge_select(_G, require 'std.debug', {'say'}, false) + merge_select = X('merge_select(table, table, [table], ?boolean|:nometa)', + merge_namedfields), +} + + +return merge(table, M) + + + +--- Types +-- @section Types + +--- Signature of a @{sort} comparator function. +-- @function comparator +-- @param a any object +-- @param b any object +-- @treturn boolean `true` if *a* sorts before *b*, otherwise `false` +-- @see sort +-- @usage +-- local reversor = function(a, b) return a > b end +-- sort(t, reversor) |