summaryrefslogtreecommitdiff
path: root/Data/BuiltIn/Libraries/lua-stdlib/lib/std/table.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Data/BuiltIn/Libraries/lua-stdlib/lib/std/table.lua')
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/lib/std/table.lua439
1 files changed, 439 insertions, 0 deletions
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)