464 lines
11 KiB
Lua
464 lines
11 KiB
Lua
--- Adds to the existing global functions
|
|
module ("base", package.seeall)
|
|
|
|
--- Functional forms of infix operators.
|
|
-- Defined here so that other modules can write to it.
|
|
-- @class table
|
|
-- @name _G.op
|
|
_G.op = {}
|
|
|
|
require "table_ext"
|
|
require "list"
|
|
require "string_ext"
|
|
--require "io_ext" FIXME: allow loops
|
|
|
|
|
|
--- Return given metamethod, if any, or nil.
|
|
-- @param x object to get metamethod of
|
|
-- @param n name of metamethod to get
|
|
-- @return metamethod function or nil if no metamethod or not a
|
|
-- function
|
|
function _G.metamethod (x, n)
|
|
local _, m = pcall (function (x)
|
|
return getmetatable (x)[n]
|
|
end,
|
|
x)
|
|
if type (m) ~= "function" then
|
|
m = nil
|
|
end
|
|
return m
|
|
end
|
|
|
|
--- Turn tables into strings with recursion detection.
|
|
-- N.B. Functions calling render should not recurse, or recursion
|
|
-- detection will not work.
|
|
-- @see render_OpenRenderer, render_CloseRenderer
|
|
-- @see render_ElementRenderer, render_PairRenderer
|
|
-- @see render_SeparatorRenderer
|
|
-- @param x object to convert to string
|
|
-- @param open open table renderer
|
|
-- @param close close table renderer
|
|
-- @param elem element renderer
|
|
-- @param pair pair renderer
|
|
-- @param sep separator renderer
|
|
-- @return string representation
|
|
function _G.render (x, open, close, elem, pair, sep, roots)
|
|
local function stop_roots (x)
|
|
return roots[x] or render (x, open, close, elem, pair, sep, table.clone (roots))
|
|
end
|
|
roots = roots or {}
|
|
if type (x) ~= "table" or metamethod (x, "__tostring") then
|
|
return elem (x)
|
|
else
|
|
local s = strbuf.new ()
|
|
s = s .. open (x)
|
|
roots[x] = elem (x)
|
|
local i, v = nil, nil
|
|
for j, w in pairs (x) do
|
|
s = s .. sep (x, i, v, j, w) .. pair (x, j, w, stop_roots (j), stop_roots (w))
|
|
i, v = j, w
|
|
end
|
|
s = s .. sep(x, i, v, nil, nil) .. close (x)
|
|
return s:tostring ()
|
|
end
|
|
end
|
|
|
|
---
|
|
-- @class function
|
|
-- @name render_OpenRenderer
|
|
-- @param t table
|
|
-- @return open table string
|
|
|
|
---
|
|
-- @class function
|
|
-- @name render_CloseRenderer
|
|
-- @param t table
|
|
-- @return close table string
|
|
|
|
---
|
|
-- @class function
|
|
-- @name render_ElementRenderer
|
|
-- @param e element
|
|
-- @return element string
|
|
|
|
---
|
|
-- @class function
|
|
-- @name render_PairRenderer
|
|
-- N.B. the function should not try to render i and v, or treat
|
|
-- them recursively.
|
|
-- @param t table
|
|
-- @param i index
|
|
-- @param v value
|
|
-- @param is index string
|
|
-- @param vs value string
|
|
-- @return element string
|
|
|
|
---
|
|
-- @class function
|
|
-- @name render_SeparatorRenderer
|
|
-- @param t table
|
|
-- @param i preceding index (nil on first call)
|
|
-- @param v preceding value (nil on first call)
|
|
-- @param j following index (nil on last call)
|
|
-- @param w following value (nil on last call)
|
|
-- @return separator string
|
|
|
|
--- Extend <code>tostring</code> to work better on tables.
|
|
-- @class function
|
|
-- @name _G.tostring
|
|
-- @param x object to convert to string
|
|
-- @return string representation
|
|
_G._tostring = tostring -- make original tostring available
|
|
local _tostring = tostring
|
|
function _G.tostring (x)
|
|
return render (x,
|
|
function () return "{" end,
|
|
function () return "}" end,
|
|
_tostring,
|
|
function (t, _, _, i, v)
|
|
return i .. "=" .. v
|
|
end,
|
|
function (_, i, _, j)
|
|
if i and j then
|
|
return ","
|
|
end
|
|
return ""
|
|
end)
|
|
end
|
|
|
|
--- Pretty-print a table.
|
|
-- @param t table to print
|
|
-- @param indent indent between levels ["\t"]
|
|
-- @param spacing space before every line
|
|
-- @return pretty-printed string
|
|
function _G.prettytostring (t, indent, spacing)
|
|
indent = indent or "\t"
|
|
spacing = spacing or ""
|
|
return render (t,
|
|
function ()
|
|
local s = spacing .. "{"
|
|
spacing = spacing .. indent
|
|
return s
|
|
end,
|
|
function ()
|
|
spacing = string.gsub (spacing, indent .. "$", "")
|
|
return spacing .. "}"
|
|
end,
|
|
function (x)
|
|
if type (x) == "string" then
|
|
return string.format ("%q", x)
|
|
else
|
|
return tostring (x)
|
|
end
|
|
end,
|
|
function (x, i, v, is, vs)
|
|
local s = spacing .. "["
|
|
if type (i) == "table" then
|
|
s = s .. "\n"
|
|
end
|
|
s = s .. is
|
|
if type (i) == "table" then
|
|
s = s .. "\n"
|
|
end
|
|
s = s .. "] ="
|
|
if type (v) == "table" then
|
|
s = s .. "\n"
|
|
else
|
|
s = s .. " "
|
|
end
|
|
s = s .. vs
|
|
return s
|
|
end,
|
|
function (_, i)
|
|
local s = "\n"
|
|
if i then
|
|
s = "," .. s
|
|
end
|
|
return s
|
|
end)
|
|
end
|
|
|
|
--- Turn an object into a table according to __totable metamethod.
|
|
-- @param x object to turn into a table
|
|
-- @return table or nil
|
|
function _G.totable (x)
|
|
local m = metamethod (x, "__totable")
|
|
if m then
|
|
return m (x)
|
|
elseif type (x) == "table" then
|
|
return x
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
--- Convert a value to a string.
|
|
-- The string can be passed to dostring to retrieve the value.
|
|
-- <br>TODO: Make it work for recursive tables.
|
|
-- @param x object to pickle
|
|
-- @return string such that eval (s) is the same value as x
|
|
function _G.pickle (x)
|
|
if type (x) == "string" then
|
|
return string.format ("%q", x)
|
|
elseif type (x) == "number" or type (x) == "boolean" or
|
|
type (x) == "nil" then
|
|
return tostring (x)
|
|
else
|
|
x = totable (x) or x
|
|
if type (x) == "table" then
|
|
local s, sep = "{", ""
|
|
for i, v in pairs (x) do
|
|
s = s .. sep .. "[" .. pickle (i) .. "]=" .. pickle (v)
|
|
sep = ","
|
|
end
|
|
s = s .. "}"
|
|
return s
|
|
else
|
|
die ("cannot pickle " .. tostring (x))
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Identity function.
|
|
-- @param ...
|
|
-- @return the arguments passed to the function
|
|
function _G.id (...)
|
|
return ...
|
|
end
|
|
|
|
--- Turn a tuple into a list.
|
|
-- @param ... tuple
|
|
-- @return list
|
|
function _G.pack (...)
|
|
return {...}
|
|
end
|
|
|
|
--- Partially apply a function.
|
|
-- @param f function to apply partially
|
|
-- @param ... arguments to bind
|
|
-- @return function with ai already bound
|
|
function _G.bind (f, ...)
|
|
local fix = {...}
|
|
return function (...)
|
|
return f (unpack (list.concat (fix, {...})))
|
|
end
|
|
end
|
|
|
|
--- Curry a function.
|
|
-- @param f function to curry
|
|
-- @param n number of arguments
|
|
-- @return curried version of f
|
|
function _G.curry (f, n)
|
|
if n <= 1 then
|
|
return f
|
|
else
|
|
return function (x)
|
|
return curry (bind (f, x), n - 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Compose functions.
|
|
-- @param f1...fn functions to compose
|
|
-- @return composition of f1 ... fn
|
|
function _G.compose (...)
|
|
local arg = {...}
|
|
local fns, n = arg, #arg
|
|
return function (...)
|
|
local arg = {...}
|
|
for i = n, 1, -1 do
|
|
arg = {fns[i] (unpack (arg))}
|
|
end
|
|
return unpack (arg)
|
|
end
|
|
end
|
|
|
|
--- Evaluate a string.
|
|
-- @param s string
|
|
-- @return value of string
|
|
function _G.eval (s)
|
|
return loadstring ("return " .. s)()
|
|
end
|
|
|
|
--- An iterator like ipairs, but in reverse.
|
|
-- @param t table to iterate over
|
|
-- @return iterator function
|
|
-- @return the table, as above
|
|
-- @return #t + 1
|
|
function _G.ripairs (t)
|
|
return function (t, n)
|
|
n = n - 1
|
|
if n > 0 then
|
|
return n, t[n]
|
|
end
|
|
end,
|
|
t, #t + 1
|
|
end
|
|
|
|
--- Tree iterator.
|
|
-- @see tree_Iterator
|
|
-- @param tr tree to iterate over
|
|
-- @return iterator function
|
|
function _G.nodes (tr)
|
|
local function visit (n, p)
|
|
if type (n) == "table" then
|
|
coroutine.yield ("branch", p, n)
|
|
for i, v in pairs (n) do
|
|
table.insert (p, i)
|
|
visit (v, p)
|
|
table.remove (p)
|
|
end
|
|
coroutine.yield ("join", p, n)
|
|
else
|
|
coroutine.yield ("leaf", p, n)
|
|
end
|
|
end
|
|
return coroutine.wrap (visit), tr, {}
|
|
end
|
|
|
|
---
|
|
-- @class function
|
|
-- @name tree_Iterator
|
|
-- @param n current node
|
|
-- @param p path to node within the tree
|
|
-- @return type ("leaf", "branch" (pre-order) or "join" (post-order))
|
|
-- @return path to node ({i1...ik})
|
|
-- @return node
|
|
|
|
--- Collect the results of an iterator.
|
|
-- @param i iterator
|
|
-- @return results of running the iterator on its arguments
|
|
function _G.collect (i, ...)
|
|
local t = {}
|
|
for e in i (...) do
|
|
table.insert (t, e)
|
|
end
|
|
return t
|
|
end
|
|
|
|
--- Map a function over an iterator.
|
|
-- @param f function
|
|
-- @param i iterator
|
|
-- @return result table
|
|
function _G.map (f, i, ...)
|
|
local t = {}
|
|
for e in i (...) do
|
|
local r = f (e)
|
|
if r then
|
|
table.insert (t, r)
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
|
|
--- Filter an iterator with a predicate.
|
|
-- @param p predicate
|
|
-- @param i iterator
|
|
-- @return result table containing elements e for which p (e)
|
|
function _G.filter (p, i, ...)
|
|
local t = {}
|
|
for e in i (...) do
|
|
if p (e) then
|
|
table.insert (t, e)
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
|
|
--- Fold a binary function into an iterator.
|
|
-- @param f function
|
|
-- @param d initial first argument
|
|
-- @param i iterator
|
|
-- @return result
|
|
function _G.fold (f, d, i, ...)
|
|
local r = d
|
|
for e in i (...) do
|
|
r = f (r, e)
|
|
end
|
|
return r
|
|
end
|
|
|
|
--- Extend to allow formatted arguments.
|
|
-- @param v value to assert
|
|
-- @param f format
|
|
-- @param ... arguments to format
|
|
-- @return value
|
|
function _G.assert (v, f, ...)
|
|
if not v then
|
|
if f == nil then
|
|
f = ""
|
|
end
|
|
error (string.format (f, ...))
|
|
end
|
|
return v
|
|
end
|
|
|
|
--- Give warning with the name of program and file (if any).
|
|
-- @param ... arguments for format
|
|
function _G.warn (...)
|
|
if prog.name then
|
|
io.stderr:write (prog.name .. ":")
|
|
end
|
|
if prog.file then
|
|
io.stderr:write (prog.file .. ":")
|
|
end
|
|
if prog.line then
|
|
io.stderr:write (tostring (prog.line) .. ":")
|
|
end
|
|
if prog.name or prog.file or prog.line then
|
|
io.stderr:write (" ")
|
|
end
|
|
io.writeline (io.stderr, string.format (...))
|
|
end
|
|
|
|
--- Die with error.
|
|
-- @param ... arguments for format
|
|
function _G.die (...)
|
|
warn (unpack (arg))
|
|
error ()
|
|
end
|
|
|
|
-- Function forms of operators.
|
|
-- FIXME: Make these visible in LuaDoc (also list.concat in list)
|
|
_G.op["[]"] =
|
|
function (t, s)
|
|
return t[s]
|
|
end
|
|
|
|
_G.op["+"] =
|
|
function (a, b)
|
|
return a + b
|
|
end
|
|
_G.op["-"] =
|
|
function (a, b)
|
|
return a - b
|
|
end
|
|
_G.op["*"] =
|
|
function (a, b)
|
|
return a * b
|
|
end
|
|
_G.op["/"] =
|
|
function (a, b)
|
|
return a / b
|
|
end
|
|
_G.op["and"] =
|
|
function (a, b)
|
|
return a and b
|
|
end
|
|
_G.op["or"] =
|
|
function (a, b)
|
|
return a or b
|
|
end
|
|
_G.op["not"] =
|
|
function (a)
|
|
return not a
|
|
end
|
|
_G.op["=="] =
|
|
function (a, b)
|
|
return a == b
|
|
end
|
|
_G.op["~="] =
|
|
function (a, b)
|
|
return a ~= b
|
|
end
|