summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Data/BuiltIn/Libraries/GameLab/Class.lua44
-rw-r--r--Data/BuiltIn/Libraries/GameLab/Containers/Array.lua0
-rw-r--r--Data/BuiltIn/Libraries/GameLab/Containers/Tuple.lua49
-rw-r--r--Data/BuiltIn/Libraries/GameLab/Containers/init.lua6
-rw-r--r--Data/BuiltIn/Libraries/GameLab/Enum.lua2
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/.gitignore9
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/.luacov52
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/.travis.yml34
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/AUTHORS.md27
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/LICENSE.md20
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/NEWS.md1414
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/README.md92
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/STYLE.md97
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/_base.lua204
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/build-aux/config.ld.in53
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/debug.lua156
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/init.lua389
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/io.lua322
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/lib/std/_base.lua204
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/lib/std/debug.lua156
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/lib/std/init.lua389
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/lib/std/io.lua322
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/lib/std/math.lua92
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/lib/std/package.lua263
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/lib/std/string.lua505
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/lib/std/table.lua439
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/math.lua92
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/package.lua263
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/spec/debug_spec.yaml222
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/spec/io_spec.yaml439
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/spec/math_spec.yaml99
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/spec/package_spec.yaml202
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/spec/spec_helper.lua416
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/spec/std_spec.yaml444
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/spec/string_spec.yaml549
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/spec/table_spec.yaml589
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/stdlib-git-1.rockspec51
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/string.lua505
-rw-r--r--Data/BuiltIn/Libraries/lua-stdlib/table.lua439
-rw-r--r--Data/Libraries/GameLab/Editor/Window/SplitWindow.lua3
-rw-r--r--Data/Scripts/EditorApplication.lua3
-rw-r--r--Data/boot.lua1
42 files changed, 9650 insertions, 7 deletions
diff --git a/Data/BuiltIn/Libraries/GameLab/Class.lua b/Data/BuiltIn/Libraries/GameLab/Class.lua
index 8a18182..db8e2b2 100644
--- a/Data/BuiltIn/Libraries/GameLab/Class.lua
+++ b/Data/BuiltIn/Libraries/GameLab/Class.lua
@@ -1,9 +1,38 @@
--- Declare class
+-- 声明类型
--- 保留方法:New, Ctor, Is
+-- 保留方法:New, Ctor, Is, get_, set_
-- 保留字段:_type, _base
-local TrackClassInstances = false
+local TRACKCLASSINSTANCES = false
+local PROPERTY = true
+
+local index = function(self, key)
+ local c = getmetatable(self)
+
+ local field = rawget(c, key)
+ if field then
+ return field
+ end
+
+ local getter = rawget(c, 'get_' .. key)
+ if getter then
+ return getter(self)
+ end
+
+ return nil
+end
+
+local newindex = function(self, key, value)
+ local c = getmetatable(self)
+
+ local setter = rawget(c, 'set_' .. key)
+ if setter then
+ setter(self, value)
+ return
+ end
+
+ rawset(self, key, value)
+end
local newclass = function (name, ctor)
local c = {}
@@ -22,8 +51,13 @@ local newclass = function (name, ctor)
fullName = name
}
- c.__index = c
-
+ if PROPERTY then
+ c.__index = index
+ c.__newindex = newindex
+ else
+ c.__index = c
+ end
+
c.New = function(...)
local obj = {}
setmetatable(obj, c)
diff --git a/Data/BuiltIn/Libraries/GameLab/Containers/Array.lua b/Data/BuiltIn/Libraries/GameLab/Containers/Array.lua
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Data/BuiltIn/Libraries/GameLab/Containers/Array.lua
diff --git a/Data/BuiltIn/Libraries/GameLab/Containers/Tuple.lua b/Data/BuiltIn/Libraries/GameLab/Containers/Tuple.lua
new file mode 100644
index 0000000..aa7f8bc
--- /dev/null
+++ b/Data/BuiltIn/Libraries/GameLab/Containers/Tuple.lua
@@ -0,0 +1,49 @@
+--[[
+
+ (...) -> tuple
+ wrap(t, [n]) -> tuple
+
+]]
+local meta = {__type = 'tuple'}
+
+local function wrap(t, n)
+ t.n = n or t.n or #t
+ setmetatable(t, meta)
+ return t
+end
+
+local function new(...)
+ return wrap({n=select('#',...),...})
+end
+
+function meta:__eq(other)
+ if self.n ~= other.n then
+ return false
+ end
+ for i=1,self.n do
+ if self[i] ~= other[i] then
+ return false
+ end
+ end
+ return true
+end
+
+function meta:__tostring()
+ local t = {}
+ for i=1,self.n do
+ t[i] = tostring(self[i])
+ end
+ return '('..table.concat(t, ', ', 1, self.n)..')'
+end
+
+local M = {
+ meta = meta,
+ wrap = wrap,
+ new = new,
+}
+
+local tuple = setmetatable(M, {__call = function(_,...) return new(...) end})
+
+GameLab.Containers.Tuple = tuple
+
+return tuple
diff --git a/Data/BuiltIn/Libraries/GameLab/Containers/init.lua b/Data/BuiltIn/Libraries/GameLab/Containers/init.lua
new file mode 100644
index 0000000..2568cd9
--- /dev/null
+++ b/Data/BuiltIn/Libraries/GameLab/Containers/init.lua
@@ -0,0 +1,6 @@
+local m = GameLab.Package("GameLab.Containers")
+local import = GameLab.Import(...)
+
+import("Tuple")
+
+return m \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/GameLab/Enum.lua b/Data/BuiltIn/Libraries/GameLab/Enum.lua
index 52c2beb..b7808fa 100644
--- a/Data/BuiltIn/Libraries/GameLab/Enum.lua
+++ b/Data/BuiltIn/Libraries/GameLab/Enum.lua
@@ -1,4 +1,4 @@
--- Declare enum
+-- 声明枚举
local Debug = GameLab.Debug
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/.gitignore b/Data/BuiltIn/Libraries/lua-stdlib/.gitignore
new file mode 100644
index 0000000..dbac14c
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/.gitignore
@@ -0,0 +1,9 @@
+*~
+.DS_Store
+/*.src.rock
+/Makefile
+/build-aux/config.ld
+/doc
+/lib/std/version.lua
+/luacov.*.out
+/stdlib-*.tar.gz
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/.luacov b/Data/BuiltIn/Libraries/lua-stdlib/.luacov
new file mode 100644
index 0000000..40b9d6a
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/.luacov
@@ -0,0 +1,52 @@
+return {
+ -- filename to store stats collected
+ ["statsfile"] = "luacov.stats.out",
+
+ -- filename to store report
+ ["reportfile"] = "luacov.report.out",
+
+ -- luacov.stats file updating frequency.
+ -- The lower this value - the more frequenty results will be written out to luacov.stats
+ -- You may want to reduce this value for short lived scripts (to for example 2) to avoid losing coverage data.
+ ["savestepsize"] = 100,
+
+ -- Run reporter on completion? (won't work for ticks)
+ runreport = true,
+
+ -- Delete stats file after reporting?
+ deletestats = false,
+
+ -- Process Lua code loaded from raw strings
+ -- (that is, when the 'source' field in the debug info
+ -- does not start with '@')
+ codefromstrings = false,
+
+ -- Patterns for files to include when reporting
+ -- all will be included if nothing is listed
+ -- (exclude overrules include, do not include
+ -- the .lua extension, path separator is always '/')
+ ["include"] = {
+ "lib/std/_base$",
+ "lib/std/debug$",
+ "lib/std/init$",
+ "lib/std/io$",
+ "lib/std/math$",
+ "lib/std/package$",
+ "lib/std/string$",
+ "lib/std/table$",
+ --"lib/std/version$",
+ },
+
+ -- Patterns for files to exclude when reporting
+ -- all will be included if nothing is listed
+ -- (exclude overrules include, do not include
+ -- the .lua extension, path separator is always '/')
+ ["exclude"] = {
+ "luacov$",
+ "luacov/reporter$",
+ "luacov/defaults$",
+ "luacov/runner$",
+ "luacov/stats$",
+ "luacov/tick$",
+ },
+}
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/.travis.yml b/Data/BuiltIn/Libraries/lua-stdlib/.travis.yml
new file mode 100644
index 0000000..92f4f73
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/.travis.yml
@@ -0,0 +1,34 @@
+language: python
+
+sudo: false
+
+env:
+ matrix:
+ - LUA="lua=5.3"
+ - LUA="lua=5.2"
+ - LUA="lua=5.1"
+ - LUA="luajit=2.1"
+ - LUA="luajit=2.0"
+
+before_install:
+ - pip install hererocks
+ - hererocks here -r^ --$LUA --patch
+ - export PATH=$PWD/here/bin:$PATH
+
+install:
+ - luarocks install ldoc
+ - luarocks install ansicolors
+ - luarocks install specl
+ - luarocks install luacov
+
+script:
+ - make
+ - luarocks make
+ - make check SPECL_OPTS='-vfreport --coverage'
+
+after_success:
+ - tail luacov.report.out
+ - bash <(curl -s https://codecov.io/bash) -v
+
+notifications:
+ slack: aspirinc:JyWeNrIdS0J5nf2Pn2BS1cih
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/AUTHORS.md b/Data/BuiltIn/Libraries/lua-stdlib/AUTHORS.md
new file mode 100644
index 0000000..6589984
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/AUTHORS.md
@@ -0,0 +1,27 @@
+# Stdlib's contributors
+
+This file lists major contributors to _stdlib_. If you think you
+should be on it, please raise a [github][] issue. Thanks also to all
+those who have contributed bug fixes, suggestions and support.
+
+Gary V. Vaughan now maintains _stdlib_, having rewritten and reorganised
+the libraries for hygiene, consistent argument type checking in debug
+mode, and object orientation, in addition to adding a lot of new
+functionality.
+
+Reuben Thomas started the standard libraries project, wrote many of the
+libraries, and integrated code from other authors.
+
+John Belmonte helped set the project up on lua-users, and contributed
+to the early organisation of the libraries.
+
+The call trace debugging code is based on test/trace-calls.lua from
+the Lua 5.0 distribution.
+
+Jamie Webb contributed several miscellaneous functions from his
+private standard library.
+
+Johann Hibschman supplied the code on which math.floor and math.round
+were based.
+
+[github]: https://github.com/lua-stdlib/lua-stdlib/issues
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/LICENSE.md b/Data/BuiltIn/Libraries/lua-stdlib/LICENSE.md
new file mode 100644
index 0000000..c2b4620
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/LICENSE.md
@@ -0,0 +1,20 @@
+Copyright (C) 2002-2018 stdlib authors
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGE-
+MENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/NEWS.md b/Data/BuiltIn/Libraries/lua-stdlib/NEWS.md
new file mode 100644
index 0000000..fdbf720
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/NEWS.md
@@ -0,0 +1,1414 @@
+# Stdlib NEWS - User visible changes
+
+## Noteworthy changes in release ?.? (????-??-??) [?]
+
+### New features
+
+ - Overhaul of the LDoc documentation, adding more introductory
+ material, clearer usage examples and better internal consistency. At
+ this point we're pushing the technical limits of what LDoc can do for
+ us organization-wise, but improvements and corrections to the content
+ are always welcome!
+
+ - With this release, stdlib is much more focused, and non-core modules
+ `optparse`, `std.functional`, 'std.prototype', `std.strict` and
+ `typecheck` have been moved into their own packages and release
+ cycle. Also, the shared debug initialization, Lua host normalization
+ and API deprecation code have been split out into new 'std._debug',
+ 'std.normalize' and 'apimaturity' respectively, and are pulled in
+ automatically as dependencies for any of any modules that need them.
+ You can still install them all separately from their own projects or
+ by using Luarocks:
+
+ ```bash
+ luarocks install optparse
+ luarocks install std.functional
+ luarocks install std.prototype
+ luarocks install std.strict
+ luarocks install typecheck
+ ```
+
+ - All support for previously deprecated APIs has been removed, reducing
+ the install size even further.
+
+ - `std.string.prettytostring` continues to use `normalize.string.render`
+ for more legible deeply nested table output, identically to previous
+ releases.
+
+ - `std.npairs` and `std.rnpairs` now respect `__len` metamethod, if any.
+
+ - `std.table.okeys` has been removed for lack of utility. If you
+ still need it, use this instead:
+
+ ```lua
+ local okeys = std.functional.compose (std.table.keys, std.table.sort)
+ ```
+
+### Bug fixes
+
+ - `std.string.wrap` doesn't throw a StrBuf deprecation warning any more.
+
+ - `std.getmetamethod` now returns functable valued metamethods
+ correctly, rather than `nil` as in previous releases. It's also
+ considerably faster now that it doesn't use `pcall` any more.
+
+ - `table.pack` now sets `n` field to number of arguments packed, even
+ in Lua 5.1.
+
+### Incompatible changes
+
+ - `std.container`, `std.functional`, `std.list`, `std.maturity`,
+ `std.object`, `std.operator`, `std.optparse`, `std.set`,
+ `std.strbuf`, `std.strict` and `std.tuple` have been moved to their
+ own packages, and are no longer shipped as part of stdlib.
+
+ - Monkey patching calls `std.barrel`, `std.monkey_patch`,
+ `std.io.monkey_patch`, `std.math.monkey_patch`,
+ `std.string.monkey_patch` and `std.table.monkey_patch` have all
+ been removed.
+
+ - `std.debug.argerror`, `std.debug.argcheck`, `std.debug.argscheck`,
+ `std.debug.extramsg_mismatch`, `std.debug.extramsg_toomany`,
+ `std.debug.parsetypes`, `std.debug.resulterror` and `std.debug.typesplit`
+ have all been moved to their own package, and are no longer shipped
+ as part of stdlib.
+
+ - `std.debug.DEPRECATED` and `std.debug.DEPRECATIONMSG` have been
+ removed. At some point these will resurface in a new standalone
+ package.
+
+ - Deprecated functions `string.assert`, `string.require_version`,
+ `string.tostring`, `table.clone_rename`, `table.metamethod`,
+ `table.ripairs` and `table.totable` have been removed. See previous
+ NEWS entries below for what they were replaced by.
+
+ - Passing a table with a `__len` metamethod, that returns a value other
+ the index of the largest non-nil valued integer key, to `std.npairs`
+ now iterates upto whatever `__len` returns rather than `std.table.maxn`.
+ If `__len` is not present, or gives the same result as `maxn` then
+ `npairs` continues to behave as in the previous release.
+
+ - `std.tostring` and `std.string.render` have been superceded by their
+ equivalents from 'std.normalize': `str` and `string.render`. Those
+ implementations handle skipping initial sequence keys for a more
+ compact output, escaping of whitespace and other C escape characters
+ for even more compact output and stringification of nested Objects and
+ Containers using their `__tostring` metamethods.
+
+ - For consistency with std.normalize and other package symbols, we now
+ spell `package.path_mark` as `package.pathmark`.
+
+
+## Noteworthy changes in release 41.2.0 (2015-03-08) [stable]
+
+### New features
+
+ - New iterators, `std.npairs` and `std.rnpairs` behave like
+ `std.ipairs` and `std.ripairs` resp., except that they will visit
+ all integer keyed elements, including nil-valued "holes". This is
+ useful for iterating over argument lists with nils:
+
+ ```lua
+ function fn(a, b, c) for _, v in npairs {...} do print(v) end
+ fn(nil, nil, 3) --> nil nil 3
+ ```
+
+ - New `debug.getfenv` and `debug.setfenv` that work with Lua 5.2 and
+ 5.3.
+
+ - `debug.argscheck` will skip typecheck for `self` parameter if the
+ function name specification contains a colon.
+
+ - New `debug.resulterror` is much like `debug.argerror`, but uses the
+ message format "bad result #n from 'fname'".
+
+ - New `debug.extramsg_mismatch` to generate `extramsg` argument for
+ `debug.argerror` or `debug.resulterror` on encountering a type
+ mismatch.
+
+ - New `debug.extramsg_toomany` to generate a too many arguments or
+ similar `extramsg` argument.
+
+### Deprecations
+
+ - `debug.toomanyargmsg` has been deprecated in favour of the more
+ orthogal `debug.extramsg_toomany` api. You can rewrite clients of
+ deprecated api like this:
+
+ ```lua
+ if maxn(argt) > 7 then
+ argerror('fname', 8, extramsg_toomany('argument', 7, maxn(argt)), 2)
+ end
+ ```
+
+### Bug fixes
+
+ - `std.getmetamethod` no longer rejects non-table subjects when
+ `_DEBUG.argcheck` is set.
+
+ - `functional.bind`, `functional.collect`, `functional.compose`,
+ `functional.filter` and `functional.map` propagate nil valued
+ arguments correctly.
+
+ - `functional.callable` no longer raises an argument error when passed
+ a nil valued argument.
+
+ - `debug.argcheck` and `debug.argscheck` accept "bool" as an alias for
+ "boolean" consistently.
+
+ - `io.catdir` and `io.dirname` no longer leak extra results from
+ implementation details.
+
+### Incompatible changes
+
+ - `functional.collect` uses `std.npairs` as a default iterator rather
+ than `std.ipairs`.
+
+
+## Noteworthy changes in release 41.1.1 (2015-01-31) [stable]
+
+### Bug fixes
+
+ - `std.barrel` no longer gets stuck in an infinite loop when called in
+ Lua 5.3.
+
+
+## Noteworthy changes in release 41.1.0 (2015-01-30) [stable]
+
+### New features
+
+ - Anything that responds to `tostring` can be appended to a `std.strbuf`:
+
+ ```lua
+ local a, b = StrBuf {'foo', 'bar'}, StrBuf {'baz', 'quux'}
+ a = a .. b --> 'foobarbazquux'
+ ```
+
+ - `std.strbuf` stringifies lazily, so adding tables to a StrBuf
+ object, and then changing the content of them before calling
+ `tostring` also changes the contents of the buffer. See LDocs for
+ an example.
+
+ - `debug.argscheck` accepts square brackets around final optional
+ parameters, which is distinct to the old way of appending `?` or
+ `|nil` in that no spurious "or nil" is reported for type mismatches
+ against a final bracketed argument.
+
+ - `debug.argscheck` can also check types of function return values, when
+ specified as:
+
+ ```lua
+ fn = argscheck('fname(?any...) => int, table or nil, string', fname)
+ ```
+
+ Optional results can be marked with brackets, and an ellipsis following
+ the final type denotes any additional results must match that final
+ type specification. Alternative result type groups are separated by "or".
+
+ - New `table.unpack(t, [i, [j]])` function that defaults j to
+ `table.maxn(t)`, even on luajit which stops before the first nil
+ valued numeric index otherwise.
+
+### Deprecations
+
+ - `std.strbuf.tostring` has been deprecated in favour of `tostring`.
+ Why write `std.strbuf.tostring(sb)` or `sb:tostring()` when it is
+ more idiomatic to write `tostring(sb)`?
+
+### Bug fixes
+
+ - `std.barrel` and the various `monkey_patch` functions now return
+ their parent module table as documented.
+
+ - stdlib modules are all `std.strict` compliant; require 'std.strict'
+ before requiring other modules no longer raises an error.
+
+ - `debug.argscheck` can now diagnose when there are too many arguments,
+ even in the case where the earlier arguments match parameters by
+ skipping bracketed optionals, and the total number of arguments is
+ still less than the absolute maximum allowed if optionals are counted
+ too.
+
+ - `package.normalize` now leaves valid ./../../ path prefixes unmolested.
+
+### Incompatible changes
+
+ - `debug.argscheck` requires nil parameter type `?` notation to be
+ prepended to match Specl and TypedLua syntax. `?` suffixes are a
+ syntax error.
+
+ - `debug.argscheck` uses `...` instead of `*` appended to the final element
+ if all unmatched argument types should match. The trailing `*` syntax
+ was confusing, because it was easy to misread it as "followed by zero-or-
+ more of this type".
+
+
+## Noteworthy changes in release 41.0.0 (2015-01-03) [beta]
+
+### New features
+
+ - Preliminary Lua 5.3.0 compatibility.
+
+ - `object.prototype` now reports "file" for open file handles, and
+ "closed file" for closed file handles.
+
+ - New `debug.argerror` and `debug.argcheck` functions that provide Lua
+ equivalents of `luaL_argerror` and `luaL_argcheck`.
+
+ - New `debug.argscheck` function for checking all function parameter
+ types with a single function call in the common case.
+
+ - New `debug.export` function, which returns a wrapper function for
+ checking all arguments of an inner function against a type list.
+
+ - New `_DEBUG.argcheck` field that disables `debug.argcheck`, and
+ changes `debug.argscheck` to return its function argument unwrapped,
+ for production code. Similarly `_DEBUG = false` deactivates these
+ functions in the same way.
+
+ - New `std.operator` module, with easier to type operator names (`conj`,
+ `deref`, `diff`, `disj`, `eq`, `neg`, `neq`, `prod`, `quot`, and `sum`),
+ and a functional operator for concatenation `concat`; plus new mathematical
+ operators `mod`, and `pow`; and relational operators `lt`, `lte`, `gt` and
+ `gte`.
+
+ - `functional.case` now accepts non-callable branch values, which are
+ simply returned as is, and functable values which are called and
+ their return value propagated back to the case caller. Function
+ values behave the same as in previous releases.
+
+ - `functional.collect`, `functional.filter`, `functional.map` and
+ `functional.reduce` now work with standard multi-return iterators,
+ such as `std.pairs`.
+
+ - `functional.collect` defaults to using `std.ipairs` as an iterator.
+
+ - New `functional.cond`, for evaluating multiple distinct expressions
+ to determine what following value to be the returned.
+
+ - `functional.filter` and `functional.map` default to using `std.pairs`
+ as an iterator.
+
+ - The init argument to `functional.foldl` and `functional.foldr` is now
+ optional; when omitted these functions automatically start with
+ the left- or right-most element of the table argument resp.
+
+ - New `functional.callable` function for unwrapping objects or
+ primitives that can be called as if they were a function.
+
+ - New `functional.lambda` function for compiling lambda strings:
+
+ ```lua
+ table.sort(t, lambda '|a,b| a<b')
+ ```
+
+ or, equivalently using auto-arguments:
+
+ ```lua
+ table.sort(t, lambda '= _1 < _2'
+ ```
+
+ - New `functional.map_with` that returns a new table with keys matching
+ the argument table, and values made by mapping the supplied function
+ over value tables. This replaces the misplaced, and less powerful
+ `list.map_with`.
+
+ - `functional.memoize` now propagates multiple return values correctly.
+ This allows memoizing of functions that use the `return nil, 'message'`
+ pattern for error message reporting.
+
+ - New `functional.nop` function, for use where a function is required
+ but no work should be done.
+
+ - New `functional.zip`, which in addition to replacing the functionality
+ of deprecated `list.transpose` when handling lists of lists, correctly
+ zips arbitrary tables of tables, and is orthogonal to `functional.map`.
+ It is also more than twice as fast as `list.transpose`, processing
+ with a single pass over the argument table as opposed to the two
+ passes and addition book-keeping required by `list.transpose`s
+ algorithm.
+
+ - New `functional.zip_with`, subsumes functionality of deprecated
+ `list.zip_with`, but also handles arbitrary tables of tables correctly,
+ and is orthogonal to `functional.map_with`.
+
+ - `std` module now collects stdlib functions that do not really belong
+ in specific type modules: including `std.assert`, `std.eval`, and
+ `std.tostring`. See LDocs for details.
+
+ - New `std.ipairs` function that ignores `__ipairs` metamethod (like Lua
+ 5.1 and Lua 5.3), while always iterating from index 1 through n, where n
+ is the last non-`nil` valued integer key. Writing your loops to use
+ `std.ipairs` ensures your code will behave consistently across supported
+ versions of Lua.
+
+ All of stdlib's implementation now uses `std.ipairs` rather than `ipairs`
+ internally.
+
+ - New `std.ielems` and `std.elems` functions for iterating sequences
+ analagously to `std.ipairs` and `std.pairs`, but returning only the
+ value part of each key-value pair visited.
+
+ - New `std.ireverse` function for reversing the proper sequence part of
+ any table.
+
+ - New `std.pairs` function that respects `__pairs` metamethod, even on
+ Lua 5.1.
+
+ All of stdlib's implementation now uses `std.pairs` rather than `pairs`
+ internally. Among other improvements, this makes for a much more
+ elegant imlementation of `std.object`, which also behaves intuitively
+ and consistently when passed to `std.pairs`.
+
+ - `std.require` now give a verbose error message when loaded module does not
+ meet version numbers passed.
+
+ - New `std.ripairs` function for returning index & value pairs in
+ reverse order, starting at the highest non-nil-valued contiguous integer
+ key.
+
+ - New `table.len` function for returning the length of a table, much like
+ the core `#` operation, but respecing `__len` even on Lua 5.1.
+
+ - New `table.insert` and `table.remove` that use `table.len` to
+ calculate default *pos* parameter, as well as diagnosing out of bounds
+ *pos* parameters consistently on any supported version of Lua.
+
+ - `table.insert` returns the modified table.
+
+ - New `table.maxn` is available even when Lua compiled without
+ compatibility, but uses the core implementation when possible.
+
+ - New `table.okeys` function, like `table.keys` except that the list of
+ keys is returned with numerical keys in order followed by remaining
+ keys in asciibetical order.
+
+ - `std.tostring`, `std.string.prettytostring` and the base `std.object`
+ `__tostring` metamethod now all use `table.okeys` to sort keys in the
+ generated stringification of a table.
+
+### Deprecations
+
+ - Deprecated APIs are kept for a minimum of 1 year following the first
+ release that contains the deprecations. With each new release of
+ lua-stdlib, any APIs that have been deprecated for longer than that
+ will most likely be removed entirely. You can prevent that by
+ raising an issue at <https://github.com/lua-stdlib/lua-stdlib/issues>
+ explaining why any deprecation should be reinstated or at least kept
+ around for more than 1 year.
+
+ - By default, deprecated APIs will issue a warning to stderr on every
+ call. However, in production code, you can turn off these warnings
+ entirely with any of:
+
+ ```lua
+ _DEBUG = false
+ _DEBUG = {deprecate=false}
+ require 'std.debug_init'.deprecate = false
+ ```
+
+ Or, to confirm you're not trying to call a deprecated function at
+ runtime, you can prevent deprecated functions from being defined at
+ all with any of:
+
+ ```lua
+ _DEBUG = true
+ _DEBUG = {deprecate=true}
+ require 'std.debug_init'.deprecate = true
+ ```
+
+ The `_DEBUG` global must be set before requiring any stdlib modules,
+ but you can adjust the fields in the `std.debug_init` table at any
+ time.
+
+ - `functional.eval` has been moved to `std.eval`, the old name now
+ gives a deprecation warning.
+
+ - `functional.fold` has been renamed to `functional.reduce`, the old
+ name now gives a deprecation warning.
+
+ - `functional.op` has been moved to a new `std.operator` module, the
+ old function names now gives deprecation warnings.
+
+ - `list.depair` and `list.enpair` have been moved to `table.depair` and
+ `table.enpair`, the old names now give deprecation warnings.
+
+ - `list.filter` has been moved to `functional.filter`, the old name now
+ gives a deprecation warning.
+
+ - `list.flatten` has been moved to `table.flatten`, the old name now
+ gives a deprecation warning.
+
+ - `list.foldl` and `list.foldr` have been replaced by the richer
+ `functional.foldl` and `functional.foldr` respectively. The old
+ names now give a deprecation warning. Note that List object methods
+ `foldl` and `foldr` are not affected.
+
+ - `list.index_key` and `list.index_value` have been deprecated. These
+ functions are not general enough to belong in lua-stdlib, because
+ (among others) they only work correctly with tables that can be
+ inverted without loss of key values. They currently give deprecation
+ warnings.
+
+ - `list.map` and `list.map_with` has been deprecated, in favour of the
+ more powerful new `functional.map` and `functional.map_with` which
+ handle tables as well as lists.
+
+ - `list.project` has been deprecated in favour of `table.project`, the
+ old name now gives a deprecation warning.
+
+ - `list.relems` has been deprecated, in favour of the more idiomatic
+ `functional.compose(std.ireverse, std.ielems)`.
+
+ - `list.reverse` has been deprecated in favour of the more general
+ and more accurately named `std.ireverse`.
+
+ - `list.shape` has been deprecated in favour of `table.shape`, the old
+ name now gives a deprecation warning.
+
+ - `list.transpose` has been deprecated in favour of `functional.zip`,
+ see above for details.
+
+ - `list.zip_with` has been deprecated in favour of `functional.zip_with`,
+ see above for details.
+
+ - `string.assert` has been moved to `std.assert`, the old name now
+ gives a deprecation warning.
+
+ - `string.require_version` has been moved to `std.require`, the old
+ name now gives a deprecation warning.
+
+ - `string.tostring` has been moved to `std.tostring`, the old name now
+ gives a deprecation warning.
+
+ - `table.metamethod` has been moved to `std.getmetamethod`, the old
+ name now gives a deprecation warning.
+
+ - `table.ripairs` has been moved to `std.ripairs`, the old name now
+ gives a deprecation warning.
+
+ - `table.totable` has been deprecated and now gives a warning when used.
+
+### Incompatible changes
+
+ - `std.monkey_patch` works the same way as the other submodule
+ monkey_patch functions now, by injecting its methods into the given
+ (or global) namespace. To get the previous effect of running all the
+ monkey_patch functions, either run them all manually, or call
+ `std.barrel()` as before.
+
+ - `functional.bind` sets fixed positional arguments when called as
+ before, but when the newly bound function is called, those arguments
+ fill remaining unfixed positions rather than being overwritten by
+ original fixed arguments. For example, where this would have caused
+ an error previously, it now prints "100" as expected.
+
+ ```lua
+ local function add(a, b) return a + b end
+ local incr = functional.bind(add, {1})
+ print(incr(99))
+ ```
+
+ If you have any code that calls functions returned from `bind`, you
+ need to remove the previously ignored arguments that correspond to
+ the fixed argument positions in the `bind` invocation.
+
+ - `functional.collect`, `functional.filter` and `functional.map` still
+ make a list from the results from an iterator that returns single
+ values, but when an iterator returns multiple values they now make a
+ table with key:value pairs taken from the first two returned values of
+ each iteration.
+
+ - The `functional.op` table has been factored out into its own new
+ module `std.operator`. It will also continue to be available from the
+ legacy `functional.op` access point for the forseeable future.
+
+ - The `functional.op['..']` operator is no longer a list concatenation
+ only loaded when `std.list` is required, but a regular string
+ concatenation just like Lua's `..` operator.
+
+ - `io.catdir` now raises an error when called with no arguments, for
+ consistency with `io.catfile`.
+
+ - `io.die` no longer calls `io.warn` to write the error message to
+ stderr, but passes that error message to the core `error` function.
+
+ - `std.set` objects used to be lax about enforcing type correctness in
+ function arguments, but now that we have strict type-checking on all
+ apis, table arguments are not coerced to Set objects but raise an
+ error. Due to an accident of implementation, you can get the old
+ inconsistent behaviour back for now by turning off type checking
+ before loading any stdlib modules:
+
+ ```lua
+ _DEBUG = {argcheck=false}
+ local set = require 'std.set'
+ ```
+
+ - `string.pad` will still (by implementation accident) coerce non-
+ string initial arguments to a string using `string.tostring` as long
+ as argument checking is disabled. Under normal circumstances,
+ passing a non-string will now raise an error as specified in the api
+ documentation.
+
+ - `table.totable` is deprecated, and thus objects no longer provide or
+ use a `__totable` metamethod. Instead, using a `__pairs` metamethod
+ to return key/value pairs, and that will automatically be used by
+ `__tostring`, `object.mapfields` etc. The base object now provides a
+ `__pairs` metamethod that returns key/value pairs in order, and
+ ignores private fields. If you have objects that relied on the
+ previous treatment of `__totable`, please convert them to set a
+ custom `__pairs` instead.
+
+
+### Bug fixes
+
+ - Removed LDocs for unused `_DEBUG.std` field.
+
+ - `debug.trace` works with Lua 5.2.x again.
+
+ - `list:foldr` works again instead of raising a "bad argument #1 to
+ 'List'" error.
+
+ - `list.transpose` works again, and handles empty lists without
+ raising an error; but is deprecated and will be removed in a future
+ release (see above).
+
+ - `list.zip_with` no longer raises an argument error on every call; but,
+ like `list.transpose`, is also deprecated (see above).
+
+ - `optparse.on` now works with `std.strict` enabled.
+
+ - `std.require` (nee `string.require_version`) now extracts the last
+ substring made entirely of digits and periods from the required
+ module's version string before splitting on period. That means, for
+ version strings like luaposix's "posix library for Lua 5.2 / 32" we
+ now correctly compare just the numeric part against specified version
+ range rather than an ASCII comparison of the whole thing as before!
+
+ - The documentation now correcly notes that `std.require` looks
+ first in `module.version` and then `module._VERSION` to match the
+ long-standing implementation.
+
+ - `string.split` now really does split on whitespace when no split
+ pattern argument is provided. Also, the documentation now
+ correctly cites `%s+` as the default whitespace splitting pattern
+ (not `%s*` which splits between every non-whitespace character).
+
+
+## Noteworthy changes in release 40 (2014-05-01) [stable]
+
+### New features
+
+ - `functional.memoize` now accepts a user normalization function,
+ falling back on `string.tostring` otherwise.
+
+ - `table.merge` now supports `map` and `nometa` arguments orthogonally
+ to `table.clone`.
+
+ - New `table.merge_select` function, orthogonal to
+ `table.clone_select`. See LDocs for details.
+
+### Incompatible changes
+
+ - Core methods and metamethods are no longer monkey patched by default
+ when you `require 'std'` (or `std.io`, `std.math`, `std.string` or
+ `std.table`). Instead they provide a new `monkey_patch` method you
+ should use when you don't care about interactions with other
+ modules:
+
+ ```lua
+ local io = require 'std.io'.monkey_patch()
+ ```
+
+ To install all of stdlib's monkey patches, the `std` module itself
+ has a `monkey_patch` method that loads all submodules with their own
+ `monkey_patch` method and runs them all.
+
+ If you want full compatibility with the previous release, in addition
+ to the global namespace scribbling snippet above, then you need to
+ adjust the first line to:
+
+ ```lua
+ local std = require 'std'.monkey_patch()
+ ```
+
+ - The global namespace is no longer clobbered by `require 'std'`. To
+ get the old behaviour back:
+
+ ```lua
+ local std = require 'std'.barrel(_G)
+ ```
+
+ This will execute all available monkey_patch functions, and then
+ scribble all over the `_G` namespace, just like the old days.
+
+ - The `metamethod` call is no longer in `std.functional`, but has moved
+ to `std.table` where it properly belongs. It is a utility method for
+ tables and has nothing to do with functional programming.
+
+ - The following deprecated camelCase names have been removed, you
+ should update your code to use the snake_case equivalents:
+ `std.io.processFiles`, `std.list.indexKey`, `std.list.indexValue`,
+ `std.list.mapWith`, `std.list.zipWith`, `std.string.escapePattern`,
+ `std.string. escapeShell`, `std.string.ordinalSuffix`.
+
+ - The following deprecated function names have been removed:
+ `std.list.new` (call `std.list` directly instead),
+ `std.list.slice` (use `std.list.sub` instead),
+ `std.set.new` (call `std.set` directly instead),
+ `std.strbuf.new` (call `std.strbuf` directly instead), and
+ `std.tree.new` (call `std.tree` directly instead).
+
+### Bug fixes
+
+ - Allow `std.object` derived tables as `std.tree` keys again.
+
+
+## Noteworthy changes in release 39 (2014-04-23) [stable]
+
+### New features
+
+ - New `std.functional.case` function for rudimentary case statements.
+ The main difference from serial if/elseif/end comparisons is that
+ `with` is evaluated only once, and then the match function is looked
+ up with an O(1) table reference and function call, as opposed to
+ hoisting an expression result into a temporary variable, and O(n)
+ comparisons.
+
+ The function call overhead is much more significant than several
+ comparisons, and so `case` is slower for all but the largest series
+ of if/elseif/end comparisons. It can make your code more readable,
+ however.
+
+ See LDocs for usage.
+
+ - New pathstring management functions in `std.package`.
+
+ Manage `package.path` with normalization, duplicate removal,
+ insertion & removal of elements and automatic folding of '/' and '?'
+ onto `package.dirsep` and `package.path_mark`, for easy addition of
+ new paths. For example, instead of all this:
+
+ ```lua
+ lib = std.io.catfile('.', 'lib', package.path_mark .. '.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:
+
+ ```lua
+ package.path = package.normalize('./lib/?.lua', package.path)
+ ```
+
+ - `std.optparse:parse` accepts a second optional parameter, a table of
+ default option values.
+
+ - `table.clone` accepts an optional table of key field renames in the
+ form of `{oldkey=newkey, ...}` subsuming the functionality of
+ `table.clone_rename`. The final `nometa` parameter is supported
+ whether or not a rename map is given:
+
+ ```lua
+ r = table.clone(t, 'nometa')
+ r = table.clone(t, {oldkey=newkey}, 'nometa')
+ ```
+
+### Deprecations
+
+ - `table.clone_rename` now gives a warning on first call, and will be
+ removed entirely in a few releases. The functionality has been
+ subsumed by the improvements to `table.clone` described above.
+
+### Bug fixes
+
+ - `std.optparse` no longer throws an error when it encounters an
+ unhandled option in a combined (i.e. `-xyz`) short option string.
+
+ - Surplus unmapped fields are now discarded during object cloning, for
+ example when a prototype has `_init` set to `{'first', 'second'}`,
+ and is cloned using `Proto {'one', 'two', 'three'}`, then the
+ unmapped `three` argument is now discarded.
+
+ - The path element returned by `std.tree.nodes` can now always be
+ used as a key list to dereference the root of the tree, particularly
+ `tree[{}]` now returns the root node of `tree`, to match the initial
+ `branch` and final `join` results from a full traversal by
+ `std.tree.nodes(tree)`.
+
+### Incompatible changes
+
+ - `std.string` no longer sets `__append`, `__concat` and `__index` in
+ the core strings metatable by default, though `require 'std'` does
+ continue to do so. See LDocs for `std.string` for details.
+
+ - `std.optparse` no longer normalizes unhandled options. For example,
+ `--unhandled-option=argument` is returned unmolested from `parse`,
+ rather than as two elements split on the `=`; and if a combined
+ short option string contains an unhandled option, then whatever was
+ typed at the command line is returned unmolested, rather than first
+ stripping off and processing handled options, and returning only the
+ unhandled substring.
+
+ - Setting `_init` to `{}` in a prototype object will now discard all
+ positional parameters passed during cloning, because a table valued
+ `_init` is a list of field names, beyond which surplus arguments (in
+ this case, all arguments!) are discarded.
+
+
+## Noteworthy changes in release 38 (2014-01-30) [stable]
+
+### New features
+
+ - The separator parameter to `std.string.split` is now optional. It
+ now splits strings with `%s+` when no separator is specified. The
+ new implementation is faster too.
+
+ - New `std.object.mapfields` method factors out the table field copying
+ and mapping performed when cloning a table `_init` style object. This
+ means you can call it from a function `_init` style object after
+ collecting a table to serve as `src` to support derived objects with
+ normal std.object syntax:
+
+ ```lua
+ Proto = Object {
+ _type = 'proto'
+ _init = function(self, arg, ...)
+ if type(arg) == 'table' then
+ mapfields(self, arg)
+ else
+ -- non-table instantiation code
+ end
+ end,
+ }
+ new = Proto(str, #str)
+ Derived = proto {_type='Derived', ...}
+ ```
+
+ - Much faster object cloning; `mapfields` is in imperative style and
+ makes one pass over each table it looks at, where previous releases
+ used functional style (stack frame overhead) and multiple passes over
+ input tables.
+
+ On my 2013 Macbook Air with 1.3GHz Core i5 CPU, I can now create a
+ million std.objects with several assorted fields in 3.2s. Prior to
+ this release, the same process took 8.15s... and even release 34.1,
+ with drastically simpler Objects (19SLOC vs over 120) took 5.45s.
+
+ - `std.object.prototype` is now almost an order of magnitude faster
+ than previous releases, taking about 20% of the time it previously
+ used to return its results.
+
+ - `io.warn` and `io.die` now integrate properly with `std.optparse`,
+ provided you save the `opts` return from `parser:parse` back to the
+ global namespace where they can access it:
+
+ ```lua
+ 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
+ ```
+
+ will, when run, output to stderr: "eg: oh noes!"
+
+### Bug fixes
+
+ - Much improved documentation for `optparse`, so you should be able
+ to use it without reading the source code now!
+
+ - `io.warn` and `io.die` no longer output a line-number when there is
+ no file name to append it to.
+
+ - `io.warn` and `io.die` no longer crash in the absence of a global
+ `prog` table.
+
+ - `string.split` no longer goes into an infinite loop when given an
+ empty separator string.
+
+ - Fix `getmetatable(container._functions) == getmetatable(container)`,
+ which made tostring on containers misbehave, among other latent bugs.
+
+ - `_functions` is never copied into a metatable now, finally solving
+ the conflicted concerns of needing metatables to be shared between
+ all objects of the same `_type` (for `__lt` to work correctly for one
+ thing) and not leaving a dangling `_functions` list in the metatable
+ of cloned objects, which could delete functions with matching names
+ from subsequent clones.
+
+
+## Noteworthy changes in release 37 (2014-01-19) [stable]
+
+### New features
+
+ - Lazy loading of submodules into `std` on first reference. On initial
+ load, `std` has the usual single `version` entry, but the `__index`
+ metatable will automatically require submodules on first reference:
+
+ ```lua
+ local std = require 'std'
+ local prototype = std.container.prototype
+ ```
+
+ - New `std.optparse` module: A civilised option parser.
+ (L)Documentation distributed in doc/classes/std.optparse.html.
+
+### Bug fixes
+
+ - Modules no longer leak `new' and `proper_subset' into the global
+ table.
+
+ - Cloned `Object` and `Container` derived types are more aggressive
+ about sharing metatables, where previously the metatable was copied
+ unnecessarily the base object used `_functions` for module functions
+
+ - The retracted release 36 changed the operand order of many `std.list`
+ module functions unnecessarily. Now that `_function` support is
+ available, there's no need to be so draconian, so the original v35
+ and earlier operand order works as before again.
+
+ - `std.list.new`, `std.set.new`, `set.strbuf.new` and `std.tree.new`
+ are available again for backwards compatibility.
+
+ - LuaRocks install doesn't copy config.ld and config.ld to $docdir.
+
+### Incompatible changes
+
+ - `std.getopt` is no more. It appears to have no users, though if there
+ is a great outcry, it should be easy to make a compatibility api over
+ `std.optparse` in the next release.
+
+
+## Noteworthy changes in release 36 (2014-01-16) [stable]
+
+### New features
+
+ - Modules have been refactored so that they can be safely
+ required individually, and without loading themselves or any
+ dependencies on other std modules into the global namespace.
+
+ - Objects derived from the `std.object` prototype have a new
+ <derived_object>:prototype() method that returns the contents of the
+ new internal `_type` field. This can be overridden during cloning
+ with, e.g.:
+
+ ```lua
+ local Object = require 'std.object'
+ Prototype = Object {_type='Prototype', <other_fields>}
+ ```
+
+ - Objects derived from the `std.object` prototype return a new table
+ with a shallow copy of all non-private fields (keys that do not
+ begin with '_') when passed to `table.totable` - unless overridden
+ in the derived object's __totable field.
+
+ - list and strbuf are now derived from `std.object`, which means that
+ they respond to `object.prototype` with appropriate type names ('List',
+ 'StrBuf', etc.) and can be used as prototypes for further derived
+ objects or clones; support object:prototype(); respond to totable etc.
+
+ - A new Container module at `std.container` makes separation between
+ container objects (which are free to use __index as a '[]' access
+ metamethod, but) which have no object methods, and regular objects
+ (which do have object methods, but) which cannot use the __index
+ metamethod for '[]' access to object contents.
+
+ - set and tree are now derived from `std.container`, so there are no
+ object methods. Instead there are a full complement of equivalent
+ module functions. Metamethods continue to work as before.
+
+ - `string.prettytostring` always displays table elements in the same
+ order, as provided by `table.sort`.
+
+ - `table.totable` now accepts a string, and returns a list of the
+ characters that comprise the string.
+
+ - Can now be installed directly from a release tarball by `luarocks`.
+ No need to run `./configure` or `make`, unless you want to install to
+ a custom location, or do not use LuaRocks.
+
+### Bug fixes
+
+ - string.escape_pattern is now Lua 5.2 compatible.
+
+ - all objects now reuse prototype metatables, as required for __le and
+ __lt metamethods to work as documented.
+
+### Deprecations
+
+ - To avoid confusion between the builtin Lua `type` function and the
+ method for finding the object prototype names, `std.object.type` is
+ deprecated in favour of `std.object.prototype`. `std.object.type`
+ continues to work for now, but might be removed from a future
+ release.
+
+ ```lua
+ local prototype = require 'std.object'.prototype
+ ```
+
+ ...makes for more readable code, rather than confusion between the
+ different flavours of `type`.
+
+### Incompatible changes
+
+ - Following on from the Grand Renaming™ change in the last release,
+ `std.debug_ext`, `std.io_ext`, `std.math_ext`, `std.package_ext`,
+ `std.string_ext` and `std.table_ext` no longer have the spurious
+ `_ext` suffix. Instead, you must now use, e.g.:
+
+ ```lua
+ local string = require 'std.string'
+ ```
+
+ These names are now stable, and will be available from here for
+ future releases.
+
+ - The `std.list` module, as a consequence of returning a List object
+ prototype rather than a table of functions including a constructor,
+ now always has the list operand as the first argument, whether that
+ function is called with `.` syntax or `:` syntax. Functions which
+ previously had the list operand in a different position when called
+ with `.` syntax were: list.filter, list.foldl, list.foldr,
+ list.index_key, list.index_value, list.map, list.map_with,
+ list.project, list.shape and list.zip_with. Calls made as object
+ methods using `:` calling syntax are unchanged.
+
+ - The `std.set` module is a `std.container` with no object methods,
+ and now uses prototype functions instead:
+
+ ```lua
+ local union = Set.union(set1, set2)
+ ```
+
+
+## Noteworthy changes in release 35 (2013-05-06) [stable]
+
+### New features
+
+ - Move to the Slingshot release system.
+ - Continuous integration from Travis automatically builds stdilb
+ with Lua 5.1, Lua 5.2 and luajit-2.0 with every commit, which
+ should help prevent future release breaking compatibility with
+ one or another of those interpreters.
+
+### Bug fixes
+
+ - `std.package_ext` no longer overwrites the core `package` table,
+ leaving the core holding on to memory that Lua code could no
+ longer access.
+
+### Incompatible changes
+
+ - The Grand Renaming™ - everything now installs to $luaprefix/std/,
+ except `std.lua` itself. Importing individual modules now involves:
+
+ ```lua
+ local list = require 'std.list'
+ ```
+
+ If you want to have all the symbols previously available from the
+ global and core module namespaces, you will need to put them there
+ yourself, or import everything with:
+
+ ```lua
+ require 'std'
+ ```
+
+ which still behaves per previous releases.
+
+ Not all of the modules work correctly when imported individually
+ right now, until we figure out how to break some circular dependencies.
+
+
+## Noteworthy changes in release 34.1 (2013-04-01) [stable]
+
+ - This is a maintenance release to quickly fix a breakage in getopt
+ from release v34. Getopt no longer parses non-options, but stops
+ on the first non-option... if a use case for the other method
+ comes up, we can always add it back in.
+
+
+## Noteworthy changes in release 34 (2013-03-25) [stable]
+
+ - stdlib is moving towards supporting separate requirement of individual
+ modules, without scribbling on the global environment; the work is not
+ yet complete, but we're collecting tests along the way to ensure that
+ once it is all working, it will carry on working;
+
+ - there are some requirement loops between modules, so not everything can
+ be required independently just now;
+
+ - `require 'std'` will continue to inject std symbols into the system
+ tables for backwards compatibility;
+
+ - stdlib no longer ships a copy of Specl, which you will need to install
+ separately if you want to run the bundled tests;
+
+ - getopt supports parsing of undefined options; useful for programs that
+ wrap other programs;
+
+ - getopt.Option constructor is no longer used, pass a plain Lua table of
+ options, and getopt will do the rest;
+
+
+## Noteworthy changes in release 33 (2013-07-27) [stable]
+
+ - This release improves stability where Specl has helped locate some
+ corner cases that are now fixed.
+
+ - `string_ext.wrap` and `string_ext.tfind` now diagnose invalid arguments.
+
+ - Specl code coverage is improving.
+
+ - OrdinalSuffix improvements.
+
+ - Use '%' instead of math.mod, as the latter does not exist in Lua 5.2.
+
+ - Accept negative arguments.
+
+
+## Noteworthy changes in release 32 (2013-02-22) [stable]
+
+ - This release fixes a critical bug preventing getopt from returning
+ anything in getopt.opt. Gary V. Vaughan is now a co-maintainer, currently
+ reworking the sources to use (Lua 5.1 compatible) Lua 5.2 style module
+ packaging, which requires you to assign the return values from your imports:
+
+ ```lua
+ getopt = require 'getopt'
+ ```
+
+ - Extension modules, table_ext, package_ext etc. return the unextended module
+ table before injecting additional package methods, so you can ignore those
+ return values or save them for programatically backing out the changes:
+
+ ```lua
+ table_unextended = require 'table_ext'
+ ```
+
+ - Additionally, Specl (see http://github.com/gvvaughan/specl/) specifications
+ are being written for stdlib modules to help us stop accidentally breaking
+ things between releases.
+
+
+## Noteworthy changes in release 31 (2013-02-20) [stable]
+
+ - This release improves the list module: lists now have methods, list.slice
+ is renamed to list.sub (the old name is provided as an alias for backwards
+ compatibility), and all functions that construct a new list return a proper
+ list, not a table. As a result, it is now often possible to write code that
+ works on both lists and strings.
+
+
+## Noteworthy changes in release 30 (2013-02-17) [stable]
+
+ - This release changes some modules to be written in a Lua 5.2 style (but
+ not the way they work with 5.1). Some fixes and improvements were made to
+ the build system. Bugs in the die function, the parser module, and a nasty
+ bug in the set module introduced in the last release (29) were fixed.
+
+
+## Noteworthy changes in release 29 (2013-02-06) [stable]
+
+ - This release overhauls the build system to have LuaRocks install releases
+ directly from git rather than from tarballs, and fixes a bug in set (issue
+ #8).
+
+
+## Noteworthy changes in release 28 (2012-10-28) [stable]
+
+ - This release improves the documentation and build system, and improves
+ require_version to work by default with more libraries.
+
+
+## Noteworthy changes in release 27 (2012-10-03) [stable]
+
+ - This release changes getopt to return all arguments in a list, rather than
+ optionally processing them with a function, fixes an incorrect definition
+ of set.elems introduced in release 26, turns on debugging by default,
+ removes the not-very-useful string.gsubs, adds constructor functions for
+ objects, renames table.rearrange to the more descriptive table.clone_rename
+ and table.indices to table.keys, and makes table.merge not clone but modify
+ its left-hand argument. A function require_version has been added to allow
+ version constraints on a module being required. Gary Vaughan has
+ contributed a memoize function, and minor documentation and build system
+ improvements have been made. Usage information is now output to stdout, not
+ stderr. The build system has been fixed to accept Lua 5.2. The luarock now
+ installs documentation, and the build command used is now more robust
+ against previous builds in the same tree.
+
+
+## Noteworthy changes in release 26 (2012-02-18) [stable]
+
+ - This release improves getopt's output messages and conformance to
+ standard practice for default options. io.processFiles now unsets prog.file
+ when it finishes, so that a program can tell when itâs no longer
+ processing a file. Three new tree iterators, inodes, leaves and ileaves,
+ have been added; the set iterator set.elements (renamed to set.elems for
+ consistency with list.elems) is now leaves rather than pairs. tree indexing
+ has been made to work in more circumstances (thanks, Gary Vaughan).
+ io.writeline is renamed io.writelines for consistency with io.readlines and
+ its function. A slurping function, io.slurp, has been added. Strings now
+ have a __concat metamethod.
+
+
+## Noteworthy changes in release 25 (2011-09-19) [stable]
+
+ - This release adds a version string to the std module and fixes a buglet in
+ the build system.
+
+
+## Noteworthy changes in release 24 (2011-09-19) [stable]
+
+ - This release fixes a rename missing from release 23, and makes a couple of
+ fixes to the new build system, also from release 23.
+
+
+## Noteworthy changes in release 23 (2011-09-17) [stable]
+
+ - This release removes the posix_ext module, which is now part of luaposix,
+ renames string.findl to string.tfind to be the same as lrexlib, and
+ autotoolizes the build system, as well as providing a rockspec file.
+
+
+## Noteworthy changes in release 22 (2011-09-02) [stable]
+
+ - This release adds two new modules: strbuf, a trivial string buffers
+ implementation, which is used to speed up the stdlib tostring method for
+ tables, and bin, which contains a couple of routines for converting binary
+ data into numbers and strings. Some small documentation and build system
+ fixes have been made.
+
+
+## Noteworthy changes in release 21 (2011-06-06) [stable]
+
+ - This release converts the documentation of stdlib to LuaDoc, adds an
+ experimental Lua 5.2 module "fstable", for storing tables directly on
+ disk as files and directories, and fixes a few minor bugs (with help from
+ David Favro).
+
+ - This release has been tested lightly on Lua 5.2 alpha, but is not
+ guaranteed to work fully.
+
+
+## Noteworthy changes in release 20 (2011-04-14) [stable]
+
+ - This release fixes a conflict between the global _DEBUG setting and the use
+ of strict.lua, changes the argument order of some list functions to favour
+ OO-style use, adds posix.euidaccess, and adds OO-style use to set. mk1file
+ can now produce a single-file version of a user-supplied list of modules,
+ not just the standard set.
+
+
+## Noteworthy changes in release 19 (2011-02-26) [stable]
+
+ - This release puts the package.config reflection in a new package_ext
+ module, where it belongs. Thanks to David Manura for this point, and for a
+ small improvement to the code.
+
+
+## Noteworthy changes in release 18 (2011-02-26) [stable]
+
+ - This release provides named access to the contents of package.config, which
+ is undocumented in Lua 5.1. See luaconf.h and the Lua 5.2 manual for more
+ details.
+
+
+## Noteworthy changes in release 17 (2011-02-07) [stable]
+
+ - This release fixes two bugs in string.pad (thanks to Bob Chapman for the
+ fixes).
+
+
+## Noteworthy changes in release 16 (2010-12-09) [stable]
+
+ - Adds posix module, using luaposix, and makes various other small fixes and
+ improvements.
+
+
+## Noteworthy changes in release 15 (2010-06-14) [stable]
+
+ - This release fixes list.foldl, list.foldr, the fold iterator combinator and
+ io.writeLine. It also simplifies the op table, which now merely sugars the
+ built-in operators rather than extending them. It adds a new tree module,
+ which subsumes the old table.deepclone and table.lookup functions.
+ table.subscript has become op['[]'], and table.subscripts has been removed;
+ the old treeIter iterator has been simplified and generalised, and renamed
+ to nodes. The mk1file script and std.lua library loader have had the module
+ list factored out into modules.lua. strict.lua from the Lua distribution is
+ now included in stdlib, which has been fixed to work with it. Some minor
+ documentation and other code improvements and fixes have been made.
+
+
+## Noteworthy changes in release 14 (2010-06-07) [stable]
+
+ - This release makes stdlib compatible with strict.lua, which required a
+ small change to the debug_ext module. Some other minor changes have also
+ been made to that module. The table.subscripts function has been removed
+ from the table_ext.lua.
+
+
+## Noteworthy changes in release 13 (2010-06-02) [stable]
+
+ - This release removes the lcs module from the standard set loaded by
+ 'std', removes an unnecessary definition of print, and tidies up the
+ implementation of the "op" table of functional versions of the infix
+ operators and logical operators.
+
+
+## Noteworthy changes in release 12 (2009-09-07) [stable]
+
+ - This release removes io.basename and io.dirname, which are now available in
+ lposix, and the little-used functions addSuffix and changeSuffix which
+ dependend on them. io.pathConcat is renamed to io.catdir and io.pathSplit
+ to io.splitdir, making them behave the same as the corresponding Perl
+ functions. The dependency on lrexlib has been removed along with the rex
+ wrapper module. Some of the more esoteric and special-purpose modules
+ (mbox, xml, parser) are no longer loaded by 'require 'std''.
+
+ This leaves stdlib with no external dependencies, and a rather more
+ coherent set of basic modules.
+
+
+## Noteworthy changes in release 11 (2009-03-15) [stable]
+
+ - This release fixes a bug in string.format, removes the redundant
+ string.join (it's the same as table.concat), and adds to table.clone and
+ table.deepclone the ability to copy without metatables. Thanks to David
+ Kantowitz for pointing out the various deficiencies.
+
+
+## Noteworthy changes in release 10 (2009-03-13) [stable]
+
+ - This release fixes table.deepclone to copy metatables, as it should.
+ Thanks to David Kantowitz for the fix.
+
+
+## Noteworthy changes in release 9 (2009-02-19) [stable]
+
+ - This release updates the object module to be the same as that published
+ in "Lua Gems", and fixes a bug in the utility mk1file which makes a
+ one-file version of the library, to stop it permanently redefining require.
+
+
+## Noteworthy changes in release 8 (2008-09-04) [stable]
+
+ - This release features fixes and improvements to the set module; thanks to
+ Jiutian Yanling for a bug report and suggestion which led to this work.
+
+
+## Noteworthy changes in release 7 (2008-09-04) [stable]
+
+ - just a bug fix
+
+
+## Noteworthy changes in release 6 (2008-07-28) [stable]
+
+ - This release rewrites the iterators in a more Lua-ish 5.1 style.
+
+
+## Noteworthy changes in release 5 (2008-03-04) [stable]
+
+ - I'm happy to announce a new release of my standard Lua libraries. It's been
+ nearly a year since the last release, and I'm happy to say that since then
+ only one bug has been found (thanks Roberto!). Two functions have been
+ added in this release, to deal with file paths, and one removed (io.length,
+ which is handled by lfs.attributes) along with one constant (INTEGER_BITS,
+ handled by bitlib's bit.bits).
+
+ - For those not familiar with stdlib, it's a pure-Lua library of mostly
+ fundamental data structures and algorithms, in particular support for
+ functional and object-oriented programming, string and regex operations and
+ extensible pretty printing of data structures. More specific modules
+ include a getopt implementation, a generalised least common subsequences
+ (i.e. diff algorithm) implementation, a recursive-descent parser generator,
+ and an mbox parser.
+
+ - It's quite a mixed bag, but almost all written for real projects. It's
+ written in a doc-string-ish style with the supplied very simple ldoc tool.
+
+ - I am happy with this code base, but there are various things it could use:
+
+ 0. Tests. Tests. Tests. The code has no unit tests. It so needs them.
+
+ 1. More code. Nothing too specialised (unless it's too small to be released
+ on its own, although very little seems "too small" in the Lua
+ community). Anything that either has widespread applicability (like
+ getopt) or is very general (data structures, algorithms, design
+ patterns) is good.
+
+ 2. Refactoring. The code is not ideally factored. At the moment it is
+ divided into modules that extend existing libraries, and new modules
+ constructed along similar lines, but I think that some of the divisions
+ are confusing. For example, the functional programming support is spread
+ between the list and base modules, and would probably be better in its
+ own module, as those who aren't interested in the functional style won't
+ want the functional list support or the higher-order functions support,
+ and those who want one will probably want the other.
+
+ 3. Documentation work. There's not a long wrong with the existing
+ documentation, but it would be nice, now that there is a stable LuaDoc,
+ to use that instead of the built-in ldoc, which I'm happy to discard now
+ that LuaDoc is stable. ldoc was always designed as a minimal LuaDoc
+ substitute in any case.
+
+ 4. Maintenance and advocacy. For a while I have been reducing my work on
+ Lua, and am also now reducing my work in Lua. If anyone would like to
+ take on stdlib, please talk to me. It fills a much-needed function: I
+ suspect a lot of Lua programmers have invented the wheels with which it
+ is filled over and over again. In particular, many programmers could
+ benefit from the simplicity of its simple and well-designed functional,
+ string and regex capabilities, and others will love its comprehensive
+ getopt.
+
+
+## Noteworthy changes in release 4 (2007-04-26) [beta]
+
+ - This release removes the dependency on the currently unmaintained lposix
+ library, includes pre-built HTML documentation, and fixes some 5.0-style
+ uses of variadic arguments.
+
+ Thanks to Matt for pointing out all these problems. stdlib is very much
+ user-driven at the moment, since it already does everything I need, and I
+ don't have much time to work on it, so do please contact me if you find
+ bugs or problems or simply don't understand it, as the one thing I *do*
+ want to do is make it useful and accessible!
+
+
+## Noteworthy changes in release 3 (2007-02-25) [beta]
+
+ - This release fixes the "set" and "lcs" (longest common subsequence, or
+ "grep") libraries, which were broken, and adds one or two other bug and
+ design fixes. Thanks are due to Enrico Tassi for pointing out some of the
+ problems.
+
+
+## Noteworthy changes in release 2 (2007-01-05) [beta]
+
+ - This release includes some bug fixes, and compatibility with lrexlib 2.0.
+
+
+## Noteworthy changes in release 1 (2011-09-02) [beta]
+
+ - It's just a snapshot of CVS, but it's pretty stable at the moment; stdlib,
+ until such time as greater interest or participation enables (or forces!)
+ formal releases will be in permanent beta, and tracking CVS is recommended.
+
+
+[optparse]: https://github.com/gvvaughan/optparse
+[strict]: https://github.com/lua-stdlib/strict
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/README.md b/Data/BuiltIn/Libraries/lua-stdlib/README.md
new file mode 100644
index 0000000..49b0328
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/README.md
@@ -0,0 +1,92 @@
+Standard Lua libraries
+======================
+
+Copyright (C) 2000-2018 [stdlib authors][github]
+
+[![License](http://img.shields.io/:license-mit-blue.svg)](http://mit-license.org)
+[![travis-ci status](https://secure.travis-ci.org/lua-stdlib/lua-stdlib.png?branch=master)](http://travis-ci.org/lua-stdlib/lua-stdlib/builds)
+[![codecov.io](https://codecov.io/gh/lua-stdlib/lua-stdlib/branch/master/graph/badge.svg)](https://codecov.io/gh/lua-stdlib/lua-stdlib)
+[![Stories in Ready](https://badge.waffle.io/lua-stdlib/lua-stdlib.png?label=ready&title=Ready)](https://waffle.io/lua-stdlib/lua-stdlib)
+
+
+This is a collection of Lua libraries for Lua 5.1 (including LuaJIT), 5.2
+and 5.3. The libraries are copyright by their authors (see the [AUTHORS][]
+file for details), and released under the [MIT license][mit] (the same
+license as Lua itself). There is no warranty.
+
+_stdlib_ has no run-time prerequisites beyond a standard Lua system,
+though it will take advantage of [strict][] and [typecheck][] if they
+are installed.
+
+[authors]: http://github.com/lua-stdlib/lua-stdlib/blob/master/AUTHORS.md
+[github]: http://github.com/lua-stdlib/lua-stdlib/ "Github repository"
+[lua]: http://www.lua.org "The Lua Project"
+[mit]: http://mit-license.org "MIT License"
+[strict]: https://github.com/lua-stdlib/strict "strict variables"
+[typecheck]: https://github.com/gvvaughan/typecheck "function type checks"
+
+
+Installation
+------------
+
+The simplest and best way to install stdlib is with [LuaRocks][]. To
+install the latest release (recommended):
+
+```bash
+ luarocks install stdlib
+```
+
+To install current git master (for testing, before submitting a bug
+report for example):
+
+```bash
+ luarocks install http://raw.githubusercontent.com/lua-stdlib/lua-stdlib/master/stdlib-git-1.rockspec
+```
+
+The best way to install without [LuaRocks][] is to copy the `std`
+folder and its contents into a directory on your package search path.
+
+[luarocks]: http://www.luarocks.org "Lua package manager"
+
+
+Documentation
+-------------
+
+The latest release of these libraries is [documented in LDoc][github.io].
+Pre-built HTML files are included in the release.
+
+[github.io]: http://lua-stdlib.github.io/lua-stdlib
+
+
+Bug reports and code contributions
+----------------------------------
+
+These libraries are written and maintained by their users.
+
+Please make bug reports and suggestions as [GitHub Issues][issues].
+Pull requests are especially appreciated.
+
+But first, please check that your issue has not already been reported by
+someone else, and that it is not already fixed by [master][github] in
+preparation for the next release (see Installation section above for how
+to temporarily install master with [LuaRocks][]).
+
+There is no strict coding style, but please bear in mind the following
+points when proposing changes:
+
+0. Follow existing code. There are a lot of useful patterns and avoided
+ traps there.
+
+1. 3-character indentation using SPACES in Lua sources: It makes rogue
+ TABS easier to see, and lines up nicely with 'if' and 'end' keywords.
+
+2. Simple strings are easiest to type using single-quote delimiters,
+ saving double-quotes for where a string contains apostrophes.
+
+3. Save horizontal space by only using SPACES where the parser requires
+ them.
+
+4. Use vertical space to separate out compound statements to help the
+ coverage reports discover untested lines.
+
+[issues]: http://github.com/lua-stdlib/lua-stdlib/issues
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/STYLE.md b/Data/BuiltIn/Libraries/lua-stdlib/STYLE.md
new file mode 100644
index 0000000..f01f630
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/STYLE.md
@@ -0,0 +1,97 @@
+## Lua
+
+ - Requiring any stdlib module must not leak any symbols into the global
+ namespace.
+
+ - Any stdlib module may `require "std.base"`, and use any functions from
+ there, as well as functions from `std.debug` (and `debug_init`); but,
+ all other modules export argument checked functions that should not be
+ called from anywhere in stdlib -- this is the client API. If a
+ function is needed by more than one module, move it to `std.base`
+ without argument checking, and re-export with `argscheck` if necess-
+ ary.
+
+ Obviously, for objects it's perfectly fine to require the file that
+ defines the object being derived from. But to prevent accidentally
+ calling argchecked methods, we always immediately create a prototype
+ object with, e.g:
+
+ local Container = require "std.container" {}
+
+ (`std.object` is an exception to this rule because of how tightly
+ bound to `std.container` it is, and does directly call some of
+ containers methods by design).
+
+ - Minimise forward declarations of functions, because having some
+ declared as `local` in line, and others not is ugly and can easily
+ cause rogue `local` keywords to be introduced that end up shadowing
+ the intended declaration. Mutually recursive functions, and
+ alternate definitions are acceptable, in which case keep the forward
+ declarations and definitions as close together as possible to
+ minimise any possible misunderstandings later.
+
+ - Try to maintain asciibetical ordering of function definitions in each
+ source file, except where doing so would require forward declar-
+ ations. In that case use topological ordering to avoid the forward
+ declarations.
+
+ - Unless a table cannot possibly have a __len metamethod (i.e. it was
+ constructed without one in the current scope), always use
+ `base.insert` and `base.len` rather than core `table.insert` and the
+ `#` operator, which do not honor __len in all implementations.
+
+ - Unless a table cannot possibly have __pairs or __len metamethods
+ (i.e. it was constructed without them in the current scope), always
+ use `base.pairs` or `base.ipairs` rather than core `pairs` and
+ `ipairs`, which do not honor __pairs or __len in all implementations.
+
+ - Use consistent short names for common parameters:
+
+ fh a file handle, usually from io.open or similar
+ fmt a format string
+ fn a function
+ i an index
+ k a value, usually from pairs or similar
+ l a list-like table
+ n a number
+ s a string
+ t a table
+
+ - Do argument check all object methods (functions available from an
+ object created by a module function -- usually listed in the
+ `__index` subtable of the object metatable), to catch pathological
+ calls early, preferably using a `typecheck.argscheck` wrapper around
+ the internal implementation: this way, implementation functions can
+ call each other without excessive rechecking of argument types.
+
+ - Do argument check all module functions (functions available in the
+ table returned from requiring that module).
+
+ - Do argument check metamethods, to catch pathological calls early.
+
+
+## LDocs
+
+ - LDocs should be next to each function's argcheck wrapper (if it has
+ one) in the export table, so that it's easy to check the consistency
+ between the types declared in the LDocs and the argument types
+ enforced by `typecheck.argscheck` or equivalent.
+
+ - `backtick_references` is disabled for stdlib, if you want an inline
+ cross-reference, use `@{reference}`.
+
+ - Be liberal with `@see` references to similar apis.
+
+ - Refer to other argument names with italics (`*italic*` in markdown).
+
+ - Try to add entries for callback function signatures, and name them
+ with the suffix `cb`.
+
+ - Rely on the reader to understand how `:` call syntax works in Lua, and
+ don't waste effort documenting methods that are already documented as
+ functions.
+
+ - Do document the prototype chain. Don't document methods inherited
+ from the prototype, even they have been overridden to behave consist-
+ ently from a UI perspective even though the implementation needs to be
+ different to provide that same UI.
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/_base.lua b/Data/BuiltIn/Libraries/lua-stdlib/_base.lua
new file mode 100644
index 0000000..0c623d4
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/_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/build-aux/config.ld.in b/Data/BuiltIn/Libraries/lua-stdlib/build-aux/config.ld.in
new file mode 100644
index 0000000..20ee48b
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/build-aux/config.ld.in
@@ -0,0 +1,53 @@
+--[[
+ General Lua Libraries for Lua 5.1, 5.2 & 5.3
+ Copyright (C) 2002-2018 stdlib authors
+]]
+
+title = 'stdlib @PACKAGE_VERSION@ Reference'
+project = 'stdlib @PACKAGE_VERSION@'
+description = [[
+# Standard Lua Libraries
+
+This is a collection of light-weight libraries for Lua 5.1 (including
+LuaJIT), 5.2 and 5.3 written in pure Lua, comprising:
+
+1. Enhanced and expanded versions of some core Lua functions in the
+ @{std} module itself;
+
+2. Enhanced versions of some core Lua libraries: @{std.debug}, @{std.io},
+ @{std.math}, @{std.package}, @{std.string} and @{std.table};
+
+## LICENSE
+
+The code is copyright by its respective authors, and released under the
+MIT license (the same license as Lua itself). There is no warranty.
+]]
+
+dir = '../doc'
+
+file = {
+ '../lib/std/init.lua',
+ '../lib/std/debug.lua',
+ '../lib/std/io.lua',
+ '../lib/std/math.lua',
+ '../lib/std/package.lua',
+ '../lib/std/string.lua',
+ '../lib/std/table.lua',
+}
+
+new_type ('corefunction', 'Core_Functions', true)
+new_type ('corelibrary', 'Core_Libraries', true)
+
+function postprocess_html(s)
+ s = s:gsub('<h1>%s*Corefunction (.-)</p>', '<h1>Module %1</h1>')
+ s = s:gsub('<h1>%s*Corelibrary (.-)</p>', '<h1>Module %1</h1>')
+ s = s:gsub('<h2>Core_Functions</h2>', '<h2>Core Functions</h2>')
+ s = s:gsub('<h2>Core_Libraries</h2>', '<h2>Core Libraries</h2>')
+ return s
+end
+
+new_type ('init', 'Initialisation', false, 'Parameters')
+
+format = 'markdown'
+backtick_references = false
+sort = false
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/debug.lua b/Data/BuiltIn/Libraries/lua-stdlib/debug.lua
new file mode 100644
index 0000000..cab29ff
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/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/init.lua b/Data/BuiltIn/Libraries/lua-stdlib/init.lua
new file mode 100644
index 0000000..732d41f
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/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/io.lua b/Data/BuiltIn/Libraries/lua-stdlib/io.lua
new file mode 100644
index 0000000..1a2b79f
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/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/_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)
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/math.lua b/Data/BuiltIn/Libraries/lua-stdlib/math.lua
new file mode 100644
index 0000000..d955862
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/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/package.lua b/Data/BuiltIn/Libraries/lua-stdlib/package.lua
new file mode 100644
index 0000000..e3e8243
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/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/spec/debug_spec.yaml b/Data/BuiltIn/Libraries/lua-stdlib/spec/debug_spec.yaml
new file mode 100644
index 0000000..5102723
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/spec/debug_spec.yaml
@@ -0,0 +1,222 @@
+# General Lua Libraries for Lua 5.1, 5.2 & 5.3
+# Copyright (C) 2011-2018 stdlib authors
+
+before: |
+ base_module = 'debug'
+ this_module = 'std.debug'
+ global_table = '_G'
+
+ extend_base = {'getfenv', 'setfenv', 'say', 'trace'}
+
+ M = require(this_module)
+
+
+specify std.debug:
+- context when required:
+ - context by name:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by=this_module}).
+ to_equal {}
+ - it does not touch the core debug table:
+ expect(show_apis {added_to=base_module, by=this_module}).
+ to_equal {}
+ - it contains apis from the core debug table:
+ expect(show_apis {from=base_module, not_in=this_module}).
+ to_contain.a_permutation_of(extend_base)
+
+ - context via the std module:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by='std'}).
+ to_equal {}
+ - it does not touch the core debug table:
+ expect(show_apis {added_to=base_module, by='std'}).
+ to_equal {}
+
+
+- describe debug:
+ - it does nothing when std._debug is disabled:
+ expect(luaproc [[
+ require 'std._debug'(false)
+ require 'std.debug'('nothing to see here')
+ ]]).not_to_contain_error 'nothing to see here'
+ - it writes to stderr when std._debug is not set:
+ expect(luaproc [[
+ require 'std.debug'('debugging')
+ ]]).to_contain_error 'debugging'
+ - it writes to stderr when std._debug is enabled:
+ expect(luaproc [[
+ require 'std._debug'(true)
+ require 'std.debug'('debugging')
+ ]]).to_contain_error 'debugging'
+ - it writes to stderr when std._debug.level is not set:
+ expect(luaproc [[
+ require 'std._debug'()
+ require 'std.debug'('debugging')
+ ]]).to_contain_error 'debugging'
+ - it writes to stderr when std._debug.level is specified:
+ expect(luaproc [[
+ require 'std._debug'.level = 0
+ require 'std.debug'('debugging')
+ ]]).to_contain_error 'debugging'
+ expect(luaproc [[
+ require 'std._debug'.level = 1
+ require 'std.debug'('debugging')
+ ]]).to_contain_error 'debugging'
+ expect(luaproc [[
+ require 'std._debug'.level = 2
+ require 'std.debug'('debugging')
+ ]]).to_contain_error 'debugging'
+
+
+- describe say:
+ - it uses normalize.str:
+ expect(luaproc [[require 'std.debug'.say {'debugging'}]]).
+ to_contain_error(require 'std.normalize'.str {'debugging'})
+ - context when std._debug is disabled:
+ - before:
+ preamble = [[
+ require 'std._debug'(false)
+ ]]
+ - it does nothing when message level is not set:
+ expect(luaproc(preamble .. [[
+ require 'std.debug'.say 'nothing to see here'
+ ]])).not_to_contain_error 'nothing to see here'
+ - it does nothing when message is set:
+ for _, level in next, {-999, 0, 1, 2, 999} do
+ expect(luaproc(preamble .. [[
+ require 'std.debug'.say(]] .. level .. [[, 'nothing to see here')
+ ]])).not_to_contain_error 'nothing to see here'
+ end
+ - context when std._debug is not set:
+ - it writes to stderr when message level is not set:
+ expect(luaproc [[
+ require 'std.debug'.say 'debugging'
+ ]]).to_contain_error 'debugging'
+ - it writes to stderr when message level is 1 or lower:
+ for _, level in next, {-999, 0, 1} do
+ expect(luaproc([[
+ require 'std.debug'.say(]] .. level .. [[, 'debugging')
+ ]])).to_contain_error 'debugging'
+ end
+ - it does nothing when message level is 2 or higher:
+ for _, level in next, {2, 999} do
+ expect(luaproc([[
+ require 'std.debug'.say(]] .. level .. [[, 'nothing to see here')
+ ]])).not_to_contain_error 'nothing to see here'
+ end
+ - context when std._debug is enabled:
+ - before:
+ preamble = [[
+ require 'std._debug'(true)
+ ]]
+ - it writes to stderr when message level is not set:
+ expect(luaproc(preamble .. [[
+ require 'std.debug'.say 'debugging'
+ ]])).to_contain_error 'debugging'
+ - it writes to stderr when message level is 1 or lower:
+ for _, level in next, {-999, 0, 1} do
+ expect(luaproc(preamble .. [[
+ require 'std.debug'.say(]] .. level .. [[, 'debugging')
+ ]])).to_contain_error 'debugging'
+ end
+ - it does nothing when message level is 2 or higher:
+ for _, level in next, {2, 999} do
+ expect(luaproc(preamble .. [[
+ require 'std.debug'.say(]] .. level .. [[, 'nothing to see here')
+ ]])).not_to_contain_error 'nothing to see here'
+ end
+ - context when std._debug.level is not set:
+ - it writes to stderr when message level is not set:
+ expect(luaproc [[
+ require 'std.debug'.say 'debugging'
+ ]]).to_contain_error 'debugging'
+ - it writes to stderr when message level is 1 or lower:
+ for _, level in next, {-999, 0, 1} do
+ expect(luaproc([[
+ require 'std.debug'.say(]] .. level .. [[, 'debugging')
+ ]])).to_contain_error 'debugging'
+ end
+ - it does nothing when message level is 2 or higher:
+ for _, level in next, {2, 999} do
+ expect(luaproc([[
+ require 'std.debug'.say(]] .. level .. [[, 'nothing to see here')
+ ]])).not_to_contain_error 'nothing to see here'
+ end
+ - context when std._debug.level is specified:
+ - it writes to stderr when message level is 1 or lower:
+ for _, level in next, {0, 1, 2} do
+ expect(luaproc([[
+ require 'std._debug'.level = ]] .. level .. [[
+ require 'std.debug'.say 'debugging'
+ ]])).to_contain_error 'debugging'
+ end
+ - it does nothing when message level is higher than debug level:
+ expect(luaproc [[
+ require 'std._debug'.level = 2
+ require 'std.debug'.say(3, 'nothing to see here')
+ ]]).not_to_contain_error 'nothing to see here'
+ - it writes to stderr when message level equals debug level:
+ expect(luaproc [[
+ require 'std._debug'.level = 2
+ require 'std.debug'.say(2, 'debugging')
+ ]]).to_contain_error 'debugging'
+ - it writes to stderr when message level is lower than debug level:
+ expect(luaproc [[
+ require 'std._debug'.level = 2
+ require 'std.debug'.say(1, 'debugging')
+ ]]).to_contain_error 'debugging'
+
+
+- describe trace:
+ - before:
+ f = init(M, this_module, 'trace')
+
+ - it does nothing when debug hint is disabled:
+ expect(luaproc [[
+ require 'std._debug'(false)
+ require 'std.debug'
+ os.exit(0)
+ ]]).to_succeed_with ''
+ - it does nothing when debug hint is not set:
+ expect(luaproc [[
+ require 'std.debug'
+ os.exit(0)
+ ]]).to_succeed_with ''
+ - it does nothing when debug hint is enabled:
+ expect(luaproc [[
+ require 'std._debug'(true)
+ require 'std.debug'
+ os.exit(0)
+ ]]).to_succeed_with ''
+ - it enables automatically when std._debug.call is set: |
+ expect(luaproc [[
+ require 'std._debug'.call = true
+ require 'std.debug'
+ os.exit(1)
+ ]]).to_fail_while_containing ':3 call exit'
+ - it is enabled manually with debug.sethook: |
+ expect(luaproc [[
+ local debug = require 'std.debug'
+ debug.sethook(debug.trace, 'cr')
+ os.exit(1)
+ ]]).to_fail_while_containing ':3 call exit'
+ - it writes call trace log to standard error: |
+ expect(luaproc [[
+ local debug = require 'std.debug'
+ debug.sethook(debug.trace, 'cr')
+ os.exit(0)
+ ]]).to_contain_error ':3 call exit'
+ - it traces lua calls: |
+ expect(luaproc [[
+ local debug = require 'std.debug' -- line 1
+ local function incr(i) return i + 1 end -- line 2
+ debug.sethook(debug.trace, 'cr') -- line 3
+ os.exit(incr(41)) -- line 4
+ ]]).to_fail_while_matching '.*:4 call incr <2:.*:4 return incr <2:.*'
+ - it traces C api calls: |
+ expect(luaproc [[
+ local debug = require 'std.debug'
+ local function incr(i) return i + 1 end
+ debug.sethook(debug.trace, 'cr')
+ os.exit(incr(41))
+ ]]).to_fail_while_matching '.*:4 call exit %[C%]%s$'
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/spec/io_spec.yaml b/Data/BuiltIn/Libraries/lua-stdlib/spec/io_spec.yaml
new file mode 100644
index 0000000..67b850f
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/spec/io_spec.yaml
@@ -0,0 +1,439 @@
+# General Lua Libraries for Lua 5.1, 5.2 & 5.3
+# Copyright (C) 2011-2018 stdlib authors
+
+before: |
+ base_module = 'io'
+ this_module = 'std.io'
+ global_table = '_G'
+
+ extend_base = {'catdir', 'catfile', 'die', 'dirname',
+ 'process_files', 'readlines', 'shell', 'slurp',
+ 'splitdir', 'warn', 'writelines'}
+
+ dirsep = string.match(package.config, '^([^\n]+)\n')
+
+ M = require(this_module)
+
+
+specify std.io:
+- context when required:
+ - context by name:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by=this_module}).
+ to_equal {}
+ - it does not touch the core io table:
+ expect(show_apis {added_to=base_module, by=this_module}).
+ to_equal {}
+ - it contains apis from the core io table:
+ expect(show_apis {from=base_module, not_in=this_module}).
+ to_contain.a_permutation_of(extend_base)
+
+ - context via the std module:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by='std'}).
+ to_equal {}
+ - it does not touch the core io table:
+ expect(show_apis {added_to=base_module, by='std'}).
+ to_equal {}
+
+
+- describe catdir:
+ - before: |
+ f = M.catdir
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.io.catdir(string*)')
+
+ - it treats initial empty string as root directory:
+ expect(f('')).to_be(dirsep)
+ expect(f('', '')).to_be(dirsep)
+ expect(f('', 'root')).to_be(dirsep .. 'root')
+ - it returns a single argument unchanged:
+ expect(f('hello')).to_be 'hello'
+ - it joins multiple arguments with platform directory separator:
+ expect(f('one', 'two')).to_be('one' .. dirsep .. 'two')
+ expect(f('1', '2', '3', '4', '5')).
+ to_be(table.concat({'1', '2', '3', '4', '5'}, dirsep))
+
+
+- describe catfile:
+ - before:
+ f = M.catfile
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.io.catfile(string*)')
+
+ - it treats initial empty string as root directory:
+ expect(f('', '')).to_be(dirsep)
+ expect(f('', 'root')).to_be(dirsep .. 'root')
+ - it returns a single argument unchanged:
+ expect(f('')).to_be ''
+ expect(f('hello')).to_be 'hello'
+ - it joins multiple arguments with platform directory separator:
+ expect(f('one', 'two')).to_be('one' .. dirsep .. 'two')
+ expect(f('1', '2', '3', '4', '5')).
+ to_be(table.concat({'1', '2', '3', '4', '5'}, dirsep))
+
+
+- describe die:
+ - before: |
+ script = [[require 'std.io'.die "By 'eck!"]]
+
+ f = M.die
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.io.die(string, ?any*)')
+
+ - it outputs a message to stderr: |
+ expect(luaproc(script)).to_fail_while_matching ": By 'eck!\n"
+ - it ignores `prog.line` without `prog.file` or `prog.name`: |
+ script = [[prog = {line=125};]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": By 'eck!\n"
+ - it ignores `opts.line` without `opts.program`: |
+ script = [[opts = {line=99};]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": By 'eck!\n"
+ - it prefixes `prog.name` if any: |
+ script = [[prog = {name='name'};]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": name: By 'eck!\n"
+ - it appends `prog.line` if any, to `prog.name`: |
+ script = [[prog = {line=125, name='name'};]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": name:125: By 'eck!\n"
+ - it prefixes `prog.file` if any: |
+ script = [[prog = {file='file'};]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": file: By 'eck!\n"
+ - it appends `prog.line` if any, to `prog.name`: |
+ script = [[prog = {file='file', line=125};]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": file:125: By 'eck!\n"
+ - it prefers `prog.name` to `prog.file` or `opts.program`: |
+ script = [[
+ prog = {file='file', name='name'}
+ opts = {program='program'}
+ ]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": name: By 'eck!\n"
+ - it appends `prog.line` if any to `prog.name` over anything else: |
+ script = [[
+ prog = {file='file', line=125, name='name'}
+ opts = {line=99, program='program'}
+ ]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": name:125: By 'eck!\n"
+ - it prefers `prog.file` to `opts.program`: |
+ script = [[
+ prog = {file='file'}; opts = {program='program'}
+ ]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": file: By 'eck!\n"
+ - it appends `prog.line` if any to `prog.file` over using `opts`: |
+ script = [[
+ prog = {file='file', line=125}
+ opts = {line=99, program='program'}
+ ]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": file:125: By 'eck!\n"
+ - it prefixes `opts.program` if any: |
+ script = [[opts = {program='program'};]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": program: By 'eck!\n"
+ - it appends `opts.line` if any, to `opts.program`: |
+ script = [[opts = {line=99, program='program'};]] .. script
+ expect(luaproc(script)).to_fail_while_matching ": program:99: By 'eck!\n"
+
+
+- describe dirname:
+ - before:
+ f = M.dirname
+ path = table.concat({'', 'one', 'two', 'three'}, dirsep)
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.io.dirname(string)')
+
+ - it removes final separator and following:
+ expect(f(path)).to_be(table.concat({'', 'one', 'two'}, dirsep))
+
+
+- describe process_files:
+ - before:
+ name = 'Makefile'
+ names = {'LICENSE.md', 'Makefile', 'README.md'}
+ ascript = [[
+ require 'std.io'.process_files(function(a)
+ print(a)
+ end)
+ ]]
+ lscript = [[
+ require 'std.io'.process_files('=print(_1)')
+ ]]
+ iscript = [[
+ require 'std.io'.process_files(function(_, i)
+ print(i)
+ end)
+ ]]
+ catscript = [[
+ require 'std.io'.process_files(function()
+ io.write(io.input():read '*a')
+ end)
+ ]]
+
+ f = M.process_files
+
+ - context with bad arguments: |
+ badargs.diagnose(f, 'std.io.process_files(func)')
+
+ examples {
+ ["it diagnoses non-file 'arg' elements"] = function()
+ expect(luaproc(ascript, 'not-an-existing-file')).to_contain_error.any_of {
+ "cannot open file 'not-an-existing-file'", -- Lua 5.2
+ "bad argument #1 to 'input' (not-an-existing-file:", -- Lua 5.1
+ }
+ end
+ }
+
+ - it defaults to `-` if no arguments were passed:
+ expect(luaproc(ascript)).to_output '-\n'
+ - it iterates over arguments with supplied function:
+ expect(luaproc(ascript, name)).to_output(name .. '\n')
+ expect(luaproc(ascript, names)).
+ to_output(table.concat(names, '\n') .. '\n')
+ - it passes argument numbers to supplied function:
+ expect(luaproc(iscript, names)).to_output '1\n2\n3\n'
+ - it sets each file argument as the default input:
+ expect(luaproc(catscript, name)).to_output(concat_file_content(name))
+ expect(luaproc(catscript, names)).
+ to_output(concat_file_content(unpack(names)))
+ - it processes io.stdin if no arguments were passed:
+ ## FIXME: where does that closing newline come from??
+ expect(luaproc(catscript, nil, 'some\nlines\nof input')).to_output 'some\nlines\nof input\n'
+ - it processes io.stdin for `-` argument:
+ ## FIXME: where does that closing newline come from??
+ expect(luaproc(catscript, '-', 'some\nlines\nof input')).to_output 'some\nlines\nof input\n'
+
+
+- describe readlines:
+ - before: |
+ name = 'Makefile'
+ h = io.open(name)
+ lines = {}
+ for l in h:lines() do
+ lines[#lines + 1] = l
+ end
+ h:close()
+
+ defaultin = io.input()
+
+ f, badarg = init(M, this_module, 'readlines')
+ - after:
+ if io.type(defaultin) ~= 'closed file' then
+ io.input(defaultin)
+ end
+
+ - context with bad arguments: |
+ badargs.diagnose(f, 'std.io.readlines(?file|string)')
+
+ if have_typecheck then
+ examples {
+ ['it diagnoses non-existent file'] = function()
+ expect(f 'not-an-existing-file').
+ to_raise "bad argument #1 to 'std.io.readlines'(" -- system dependent error message
+ end
+ }
+ closed = io.open(name, 'r') closed:close()
+ examples {
+ ['it diagnoses closed file argument'] = function()
+ expect(f(closed)).to_raise(badarg(1, '?file|string', 'closed file'))
+ end
+ }
+ end
+
+ - it closes file handle upon completion:
+ h = io.open(name)
+ expect(io.type(h)).not_to_be 'closed file'
+ f(h)
+ expect(io.type(h)).to_be 'closed file'
+ - it reads lines from an existing named file:
+ expect(f(name)).to_equal(lines)
+ - it reads lines from an open file handle:
+ expect(f(io.open(name))).to_equal(lines)
+ - it reads from default input stream with no arguments:
+ io.input(name)
+ expect(f()).to_equal(lines)
+
+
+- describe shell:
+ - before:
+ f = M.shell
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.io.shell(string)')
+
+ - it returns the output from a shell command string:
+ expect(f [[printf '%s\n' 'foo' 'bar']]).to_be 'foo\nbar\n'
+
+
+- describe slurp:
+ - before: |
+ name = 'Makefile'
+ h = io.open(name)
+ content = h:read '*a'
+ h:close()
+
+ defaultin = io.input()
+ f, badarg = init(M, this_module, 'slurp')
+ - after:
+ if io.type(defaultin) ~= 'closed file' then
+ io.input(defaultin)
+ end
+
+ - context with bad arguments: |
+ badargs.diagnose(f, 'std.io.slurp(?file|string)')
+
+ if have_typecheck then
+ examples {
+ ['it diagnoses non-existent file'] = function()
+ expect(f 'not-an-existing-file').
+ to_raise "bad argument #1 to 'std.io.slurp'(" -- system dependent error message
+ end
+ }
+ closed = io.open(name, 'r') closed:close()
+ examples {
+ ['it diagnoses closed file argument'] = function()
+ expect(f(closed)).to_raise(badarg(1, '?file|string', 'closed file'))
+ end
+ }
+ end
+
+ - it reads content from an existing named file:
+ expect(f(name)).to_be(content)
+ - it reads content from an open file handle:
+ expect(f(io.open(name))).to_be(content)
+ - it closes file handle upon completion:
+ h = io.open(name)
+ expect(io.type(h)).not_to_be 'closed file'
+ f(h)
+ expect(io.type(h)).to_be 'closed file'
+ - it reads from default input stream with no arguments:
+ io.input(name)
+ expect(f()).to_be(content)
+
+
+- describe splitdir:
+ - before:
+ f = M.splitdir
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.io.splitdir(string)')
+
+ - it returns a filename as a one element list:
+ expect(f('hello')).to_equal {'hello'}
+ - it splits root directory in two empty elements:
+ expect(f(dirsep)).to_equal {'', ''}
+ - it returns initial empty string for absolute path:
+ expect(f(dirsep .. 'root')).to_equal {'', 'root'}
+ - it returns multiple components split at platform directory separator:
+ expect(f('one' .. dirsep .. 'two')).to_equal {'one', 'two'}
+ expect(f(table.concat({'1', '2', '3', '4', '5'}, dirsep))).
+ to_equal {'1', '2', '3', '4', '5'}
+
+
+- describe warn:
+ - before:
+ script = [[require 'std.io'.warn 'Ayup!']]
+ f = M.warn
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.io.warn(string, ?any*)')
+
+ - it outputs a message to stderr:
+ expect(luaproc(script)).to_output_error 'Ayup!\n'
+ - it ignores `prog.line` without `prog.file`, `prog.name` or `opts.program`:
+ script = [[prog = {line=125};]] .. script
+ expect(luaproc(script)).to_output_error 'Ayup!\n'
+ - it prefixes `prog.name` if any: |
+ script = [[prog = {name='name'};]] .. script
+ expect(luaproc(script)).to_output_error 'name: Ayup!\n'
+ - it appends `prog.line` if any, to `prog.name`: |
+ script = [[prog = {line=125, name='name'};]] .. script
+ expect(luaproc(script)).to_output_error 'name:125: Ayup!\n'
+ - it prefixes `prog.file` if any: |
+ script = [[prog = {file='file'};]] .. script
+ expect(luaproc(script)).to_output_error 'file: Ayup!\n'
+ - it appends `prog.line` if any, to `prog.name`: |
+ script = [[prog = {file='file', line=125};]] .. script
+ expect(luaproc(script)).to_output_error 'file:125: Ayup!\n'
+ - it prefers `prog.name` to `prog.file` or `opts.program`: |
+ script = [[
+ prog = {file='file', name='name'}
+ opts = {program='program'}
+ ]] .. script
+ expect(luaproc(script)).to_output_error 'name: Ayup!\n'
+ - it appends `prog.line` if any to `prog.name` over anything else: |
+ script = [[
+ prog = {file='file', line=125, name='name'}
+ opts = {line=99, program='program'}
+ ]] .. script
+ expect(luaproc(script)).to_output_error 'name:125: Ayup!\n'
+ - it prefers `prog.file` to `opts.program`: |
+ script = [[
+ prog = {file='file'}; opts = {program='program'}
+ ]] .. script
+ expect(luaproc(script)).to_output_error 'file: Ayup!\n'
+ - it appends `prog.line` if any to `prog.file` over using `opts`: |
+ script = [[
+ prog = {file='file', line=125}
+ opts = {line=99, program='program'}
+ ]] .. script
+ expect(luaproc(script)).to_output_error 'file:125: Ayup!\n'
+ - it prefixes `opts.program` if any: |
+ script = [[opts = {program='program'};]] .. script
+ expect(luaproc(script)).to_output_error 'program: Ayup!\n'
+ - it appends `opts.line` if any, to `opts.program`: |
+ script = [[opts = {line=99, program='program'};]] .. script
+ expect(luaproc(script)).to_output_error 'program:99: Ayup!\n'
+
+
+- describe writelines:
+ - before: |
+ name = os.tmpname()
+ h = io.open(name, 'w')
+ lines = M.readlines(io.open 'Makefile')
+
+ defaultout = io.output()
+ f, badarg = init(M, this_module, 'writelines')
+ - after:
+ if io.type(defaultout) ~= 'closed file' then
+ io.output(defaultout)
+ end
+ h:close()
+ os.remove(name)
+
+ - context with bad arguments:
+ - 'it diagnoses argument #1 type not FILE*, string, number or nil':
+ if have_typecheck then
+ expect(f(false)).to_raise(badarg(1, '?file|string|number', 'boolean'))
+ end
+ - 'it diagnoses argument #2 type not string, number or nil':
+ if have_typecheck then
+ expect(f(1, false)).to_raise(badarg(2, 'string|number', 'boolean'))
+ end
+ - 'it diagnoses argument #3 type not string, number or nil':
+ if have_typecheck then
+ expect(f(1, 2, false)).to_raise(badarg(3, 'string|number', 'boolean'))
+ end
+ - it diagnoses closed file argument: |
+ closed = io.open(name, 'r') closed:close()
+ if have_typecheck then
+ expect(f(closed)).to_raise(badarg(1, '?file|string|number', 'closed file'))
+ end
+
+ - it does not close the file handle upon completion:
+ expect(io.type(h)).not_to_be 'closed file'
+ f(h, 'foo')
+ expect(io.type(h)).not_to_be 'closed file'
+ - it writes lines to an open file handle:
+ f(h, unpack(lines))
+ h:flush()
+ expect(M.readlines(io.open(name))).to_equal(lines)
+ - it accepts number valued arguments:
+ f(h, 1, 2, 3)
+ h:flush()
+ expect(M.readlines(io.open(name))).to_equal {'1', '2', '3'}
+ - it writes to default output stream with non-file first argument:
+ io.output(h)
+ f(unpack(lines))
+ h:flush()
+ expect(M.readlines(io.open(name))).to_equal(lines)
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/spec/math_spec.yaml b/Data/BuiltIn/Libraries/lua-stdlib/spec/math_spec.yaml
new file mode 100644
index 0000000..ed08753
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/spec/math_spec.yaml
@@ -0,0 +1,99 @@
+# General Lua Libraries for Lua 5.1, 5.2 & 5.3
+# Copyright (C) 2011-2018 stdlib authors
+
+before:
+ base_module = 'math'
+ this_module = 'std.math'
+ global_table = '_G'
+
+ extend_base = {'floor', 'round'}
+
+ M = require(this_module)
+
+
+specify std.math:
+- context when required:
+ - context by name:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by=this_module}).
+ to_equal {}
+ - it does not touch the core math table:
+ expect(show_apis {added_to=base_module, by=this_module}).
+ to_equal {}
+ - it contains apis from the core math table:
+ expect(show_apis {from=base_module, not_in=this_module}).
+ to_contain.a_permutation_of(extend_base)
+
+ - context via the std module:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by='std'}).
+ to_equal {}
+ - it does not touch the core math table:
+ expect(show_apis {added_to=base_module, by='std'}).
+ to_equal {}
+
+
+- describe floor:
+ - before:
+ f = M.floor
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.math.floor(number, ?int)')
+
+ - it rounds to the nearest smaller integer:
+ expect(f(1.2)).to_be(1)
+ expect(f(1.9)).to_be(1)
+ expect(f(999e-2)).to_be(9)
+ expect(f(999e-3)).to_be(0)
+ - it rounds down to specified number of decimal places:
+ expect(f(1.2345, 0)).to_be(1.0)
+ expect(f(1.2345, 1)).to_be(1.2)
+ expect(f(1.2345, 2)).to_be(1.23)
+ expect(f(9.9999, 2)).to_be(9.99)
+ expect(f(99999e-3, 3)).to_be(99999e-3)
+ expect(f(99999e-4, 3)).to_be(9999e-3)
+ expect(f(99999e-5, 3)).to_be(999e-3)
+
+
+- describe round:
+ - before:
+ f = M.round
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.math.round(number, ?int)')
+
+ - it rounds to the nearest integer:
+ expect(f(1.2)).to_be(1)
+ expect(f(1.9)).to_be(2)
+ expect(f(949e-2)).to_be(9)
+ expect(f(999e-2)).to_be(10)
+ - it rounds to specified number of decimal places:
+ expect(f(1.234, 0)).to_be(1.0)
+ expect(f(5.678, 0)).to_be(6.0)
+ expect(f(1.234, 1)).to_be(1.2)
+ expect(f(5.678, 1)).to_be(5.7)
+ expect(f(1.234, 2)).to_be(1.23)
+ expect(f(5.678, 2)).to_be(5.68)
+ expect(f(9.999, 2)).to_be(10)
+ expect(f(11111e-2, 3)).to_be(11111e-2)
+ expect(f(99999e-2, 3)).to_be(99999e-2)
+ expect(f(11111e-3, 3)).to_be(11111e-3)
+ expect(f(99999e-3, 3)).to_be(99999e-3)
+ expect(f(11111e-4, 3)).to_be(1111e-3)
+ expect(f(99999e-4, 3)).to_be(10)
+ expect(f(99999e-5, 3)).to_be(1)
+ - it rounds negative values correctly:
+ expect(f(-1.234, 0)).to_be(-1.0)
+ expect(f(-5.678, 0)).to_be(-6.0)
+ expect(f(-1.234, 1)).to_be(-1.2)
+ expect(f(-5.678, 1)).to_be(-5.7)
+ expect(f(-1.234, 2)).to_be(-1.23)
+ expect(f(-5.678, 2)).to_be(-5.68)
+ expect(f(-9.999, 2)).to_be(-10)
+ expect(f(-11111e-2, 3)).to_be(-11111e-2)
+ expect(f(-99999e-2, 3)).to_be(-99999e-2)
+ expect(f(-11111e-3, 3)).to_be(-11111e-3)
+ expect(f(-99999e-3, 3)).to_be(-99999e-3)
+ expect(f(-11111e-4, 3)).to_be(-1111e-3)
+ expect(f(-99999e-4, 3)).to_be(-10)
+ expect(f(-99999e-5, 3)).to_be(-1)
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/spec/package_spec.yaml b/Data/BuiltIn/Libraries/lua-stdlib/spec/package_spec.yaml
new file mode 100644
index 0000000..23ce961
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/spec/package_spec.yaml
@@ -0,0 +1,202 @@
+# General Lua Libraries for Lua 5.1, 5.2 & 5.3
+# Copyright (C) 2011-2018 stdlib authors
+
+before: |
+ base_module = 'package'
+ this_module = 'std.package'
+ global_table = '_G'
+
+ extend_base = {'find', 'insert', 'mappath', 'normalize', 'remove'}
+
+ M = require(this_module)
+
+ path = M.normalize('begin', 'middle', 'end')
+
+ function catfile(...)
+ return table.concat({...}, M.dirsep)
+ end
+ function catpath(...)
+ return table.concat({...}, M.pathsep)
+ end
+
+
+specify std.package:
+- context when required:
+ - context by name:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by=this_module}).
+ to_equal {}
+ - it does not touch the core package table:
+ expect(show_apis {added_to=base_module, by=this_module}).
+ to_equal {}
+ - it contains apis from the core package table:
+ expect(show_apis {from=base_module, not_in=this_module}).
+ to_contain.a_permutation_of(extend_base)
+
+ - context via the std module:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by='std'}).
+ to_equal {}
+ - it does not touch the core package table:
+ expect(show_apis {added_to=base_module, by='std'}).
+ to_equal {}
+
+
+- describe find:
+ - before: |
+ path = table.concat({'begin', 'm%ddl.', 'end'}, M.pathsep)
+
+ f = M.find
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.package.find(string, string, ?int, ?boolean|:plain)')
+
+ - it returns nil for unmatched element:
+ expect(f(path, 'unmatchable')).to_be(nil)
+ - it returns the element index for a matched element:
+ expect(f(path, 'end')).to_be(3)
+ - it returns the element text for a matched element:
+ i, element = f(path, 'e.*n')
+ expect({i, element}).to_equal {1, 'begin'}
+ - it accepts a search start element argument:
+ i, element = f(path, 'e.*n', 2)
+ expect({i, element}).to_equal {3, 'end'}
+ - it works with plain text search strings:
+ expect(f(path, 'm%ddl.')).to_be(nil)
+ i, element = f(path, '%ddl.', 1, ':plain')
+ expect({i, element}).to_equal {2, 'm%ddl.'}
+
+
+- describe insert:
+ - before:
+ f = M.insert
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.package.insert(string, [int], string)')
+
+ - it appends by default:
+ expect(f(path, 'new')).
+ to_be(M.normalize('begin', 'middle', 'end', 'new'))
+ - it prepends with pos set to 1:
+ expect(f(path, 1, 'new')).
+ to_be(M.normalize('new', 'begin', 'middle', 'end'))
+ - it can insert in the middle too:
+ expect(f(path, 2, 'new')).
+ to_be(M.normalize('begin', 'new', 'middle', 'end'))
+ expect(f(path, 3, 'new')).
+ to_be(M.normalize('begin', 'middle', 'new', 'end'))
+ - it normalizes the returned path:
+ path = table.concat({'begin', 'middle', 'end'}, M.pathsep)
+ expect(f(path, 'new')).
+ to_be(M.normalize('begin', 'middle', 'end', 'new'))
+ expect(f(path, 1, './x/../end')).
+ to_be(M.normalize('end', 'begin', 'middle'))
+
+
+- describe mappath:
+ - before: |
+ expected = require 'std.string'.split(path, M.pathsep)
+
+ f = M.mappath
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.package.mappath(string, function, ?any*)')
+
+ - it calls a function with each path element:
+ t = {}
+ f(path, function(e)
+ t[#t + 1] = e
+ end)
+ expect(t).to_equal(expected)
+ - it passes additional arguments through: |
+ reversed = {}
+ for i = #expected, 1, -1 do
+ table.insert(reversed, expected[i])
+ end
+ t = {}
+ f(path, function(e, pos)
+ table.insert(t, pos, e)
+ end, 1)
+ expect(t).to_equal(reversed)
+
+
+- describe normalize:
+ - before:
+ f = M.normalize
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.package.normalize(string*)')
+
+ - context with a single element:
+ - it strips redundant . directories:
+ expect(f './x/./y/.').to_be(catfile('.', 'x', 'y'))
+ - it strips redundant .. directories:
+ expect(f '../x/../y/z/..').to_be(catfile('..', 'y'))
+ expect(f '../x/../y/z/..').to_be(catfile('..', 'y'))
+ expect(f '../../x/../y/z/..').to_be(catfile('..', '..', 'y'))
+ expect(f '../../x/../y/./..').to_be(catfile('..', '..'))
+ expect(f '../../w/x/../../y/z/..').to_be(catfile('..', '..', 'y'))
+ expect(f '../../w/./../.././z/..').to_be(catfile('..', '..', '..'))
+ - it leaves leading .. directories unmolested:
+ expect(f '../../1').to_be(catfile('..', '..', '1'))
+ expect(f './../../1').to_be(catfile('..', '..', '1'))
+ - it normalizes / to platform dirsep:
+ expect(f '/foo/bar').to_be(catfile('', 'foo', 'bar'))
+ - it normalizes ? to platform pathmark:
+ expect(f '?.lua').
+ to_be(catfile('.', M.pathmark .. '.lua'))
+ - it strips redundant trailing /:
+ expect(f '/foo/bar/').to_be(catfile('', 'foo', 'bar'))
+ - it inserts missing ./ for relative paths:
+ for _, path in ipairs {'x', './x'} do
+ expect(f(path)).to_be(catfile('.', 'x'))
+ end
+ - context with multiple elements:
+ - it strips redundant . directories:
+ expect(f('./x/./y/.', 'x')).
+ to_be(catpath(catfile('.', 'x', 'y'), catfile('.', 'x')))
+ - it strips redundant .. directories:
+ expect(f('../x/../y/z/..', 'x')).
+ to_be(catpath(catfile('..', 'y'), catfile('.', 'x')))
+ - it normalizes / to platform dirsep:
+ expect(f('/foo/bar', 'x')).
+ to_be(catpath(catfile('', 'foo', 'bar'), catfile('.', 'x')))
+ - it normalizes ? to platform pathmark:
+ expect(f('?.lua', 'x')).
+ to_be(catpath(catfile('.', M.pathmark .. '.lua'), catfile('.', 'x')))
+ - it strips redundant trailing /:
+ expect(f('/foo/bar/', 'x')).
+ to_be(catpath(catfile('', 'foo', 'bar'), catfile('.', 'x')))
+ - it inserts missing ./ for relative paths:
+ for _, path in ipairs {'x', './x'} do
+ expect(f(path, 'a')).
+ to_be(catpath(catfile('.', 'x'), catfile('.', 'a')))
+ end
+ - it eliminates all but the first equivalent elements:
+ expect(f(catpath('1', 'x', '2', './x', './2', './x/../x'))).
+ to_be(catpath('./1', './x', './2'))
+
+
+- describe remove:
+ - before:
+ f = M.remove
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.package.remove(string, ?int)')
+
+ - it removes the last item by default:
+ expect(f(path)).to_be(M.normalize('begin', 'middle'))
+ - it pops the first item with pos set to 1:
+ expect(f(path, 1)).to_be(M.normalize('middle', 'end'))
+ - it can remove from the middle too:
+ expect(f(path, 2)).to_be(M.normalize('begin', 'end'))
+ - it does not normalize the returned path:
+ path = table.concat({'begin', 'middle', 'end'}, M.pathsep)
+ expect(f(path)).
+ to_be(table.concat({'begin', 'middle'}, M.pathsep))
+
+
+- it splits package.config up:
+ expect(string.format('%s\n%s\n%s\n%s\n%s\n',
+ M.dirsep, M.pathsep, M.pathmark, M.execdir, M.igmark)
+ ).to_contain(package.config)
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/spec/spec_helper.lua b/Data/BuiltIn/Libraries/lua-stdlib/spec/spec_helper.lua
new file mode 100644
index 0000000..642712f
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/spec/spec_helper.lua
@@ -0,0 +1,416 @@
+--[[
+ General Lua Libraries for Lua 5.1, 5.2 & 5.3
+ Copyright (C) 2011-2018 stdlib authors
+]]
+
+local typecheck
+have_typecheck, typecheck = pcall(require, 'typecheck')
+
+local inprocess = require 'specl.inprocess'
+local hell = require 'specl.shell'
+local std = require 'specl.std'
+
+badargs = require 'specl.badargs'
+
+
+local top_srcdir = os.getenv 'top_srcdir' or '.'
+local top_builddir = os.getenv 'top_builddir' or '.'
+
+package.path = std.package.normalize(
+ top_builddir .. '/lib/?.lua',
+ top_builddir .. '/lib/?/init.lua',
+ top_srcdir .. '/lib/?.lua',
+ top_srcdir .. '/lib/?/init.lua',
+ package.path
+)
+
+
+-- Allow user override of LUA binary used by hell.spawn, falling
+-- back to environment PATH search for 'lua' if nothing else works.
+local LUA = os.getenv 'LUA' or 'lua'
+
+
+-- Simplified version for specifications, does not support functable
+-- valued __len metamethod, so don't write examples that need that!
+function len(x)
+ local __len = getmetatable(x) or {}
+ if type(__len) == 'function' then
+ return __len(x)
+ end
+ if type(x) ~= 'table' then
+ return #x
+ end
+
+ local n = #x
+ for i = 1, n do
+ if x[i] == nil then
+ return i -1
+ end
+ end
+ return n
+end
+
+
+-- Make sure we have a maxn even when _VERSION ~= 5.1
+-- @fixme remove this when we get unpack from specl.std
+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
+
+
+pack = table.pack or function(...)
+ return {n=select('#', ...), ...}
+end
+
+
+-- Take care to always unpack upto the highest numeric index, for
+-- consistency across Lua versions.
+local _unpack = table.unpack or unpack
+
+-- @fixme pick this up from specl.std with the next release
+function unpack(t, i, j)
+ return _unpack(t, tonumber(i) or 1, tonumber(j or t.n or len(t)))
+end
+
+
+-- In case we're not using a bleeding edge release of Specl...
+_diagnose = badargs.diagnose
+badargs.diagnose = function(...)
+ if have_typecheck then
+ return _diagnose(...)
+ end
+end
+
+badargs.result = badargs.result or function(fname, i, want, got)
+ if want == nil then -- numbers only for narg error
+ i, want = i - 1, i
+ end
+
+ if got == nil and type(want) == 'number' then
+ local s = "bad result #%d from '%s'(no more than %d result%s expected, got %d)"
+ return s:format(i + 1, fname, i, i == 1 and '' or 's', want)
+ end
+
+ local function showarg(s)
+ return('|' .. s .. '|'):
+ gsub('|%?', '|nil|'):
+ gsub('|nil|', '|no value|'):
+ gsub('|any|', '|any value|'):
+ gsub('|#', '|non-empty '):
+ gsub('|func|', '|function|'):
+ gsub('|file|', '|FILE*|'):
+ gsub('^|', ''):
+ gsub('|$', ''):
+ gsub('|([^|]+)$', 'or %1'):
+ gsub('|', ', ')
+ end
+
+ return string.format("bad result #%d from '%s'(%s expected, got %s)",
+ i, fname, showarg(want), got or 'no value')
+end
+
+
+-- Wrap up badargs function in a succinct single call.
+function init(M, mname, fname)
+ local name =(mname .. '.' .. fname):gsub('^%.', '')
+ return M[fname],
+ function(...)
+ return badargs.format(name, ...)
+ end,
+ function(...)
+ return badargs.result(name, ...)
+ end
+end
+
+
+-- A copy of base.lua:type, so that an unloadable base.lua doesn't
+-- prevent everything else from working.
+function objtype(o)
+ return(getmetatable(o) or {})._type or io.type(o) or type(o)
+end
+
+
+function nop() end
+
+
+-- Error message specifications use this to shorten argument lists.
+-- Copied from functional.lua to avoid breaking all tests if functional
+-- cannot be loaded correctly.
+function bind(f, fix)
+ return function(...)
+ local arg = {}
+ for i, v in pairs(fix) do
+ arg[i] = v
+ end
+ local i = 1
+ for _, v in pairs {...} do
+ while arg[i] ~= nil do
+ i = i + 1
+ end
+ arg[i] = v
+ end
+ return f(unpack(arg))
+ end
+end
+
+
+local function mkscript(code)
+ local f = os.tmpname()
+ local h = io.open(f, 'w')
+ h:write(code)
+ h:close()
+ return f
+end
+
+
+--- Run some Lua code with the given arguments and input.
+-- @string code valid Lua code
+-- @tparam[opt={}] string|table arg single argument, or table of
+-- arguments for the script invocation.
+-- @string[opt] stdin standard input contents for the script process
+-- @treturn specl.shell.Process|nil status of resulting process if
+-- execution was successful, otherwise nil
+function luaproc(code, arg, stdin)
+ local f = mkscript(code)
+ if type(arg) ~= 'table' then
+ arg = {arg}
+ end
+ local cmd = {LUA, f, unpack(arg)}
+ -- inject env and stdin keys separately to avoid truncating `...` in
+ -- cmd constructor
+ cmd.env = {LUA_PATH=package.path, LUA_INIT='', LUA_INIT_5_2=''}
+ cmd.stdin = stdin
+ local proc = hell.spawn(cmd)
+ os.remove(f)
+ return proc
+end
+
+
+--- Check deprecation output when calling a named function in the given module.
+-- Note that the script fragments passed in *argstr* and *objectinit*
+-- can reference the module table as `M`, and(where it would make sense)
+-- an object prototype as `P` and instance as `obj`.
+-- @param deprecate value of `std._debug.deprecate`
+-- @string module dot delimited module path to load
+-- @string fname name of a function in the table returned by requiring *module*
+-- @param[opt=''] args arguments to pass to *fname* call, must be stringifiable
+-- @string[opt=nil] objectinit object initializer to instantiate an
+-- object for object method deprecation check
+-- @treturn specl.shell.Process|nil status of resulting process if
+-- execution was successful, otherwise nil
+function deprecation(deprecate, module, fname, args, objectinit)
+ args = args or ''
+ local script
+ if objectinit == nil then
+ script = string.format([[
+ require 'std._debug'.deprecate = %s
+ M = require '%s'
+ P = M.prototype
+ print(M.%s(%s))
+ ]], tostring(deprecate), module, fname, tostring(args))
+ else
+ script = string.format([[
+ require 'std._debug'.deprecate = %s
+ local M = require '%s'
+ local P = M.prototype
+ local obj = P(%s)
+ print(obj:%s(%s))
+ ]], tostring(deprecate), module, objectinit, fname, tostring(args))
+ end
+ return luaproc(script)
+end
+
+
+--- Concatenate the contents of listed existing files.
+-- @string ... names of existing files
+-- @treturn string concatenated contents of those files
+function concat_file_content(...)
+ local t = {}
+ for _, name in ipairs {...} do
+ h = io.open(name)
+ t[#t + 1] = h:read '*a'
+ end
+ return table.concat(t)
+end
+
+
+local function tabulate_output(code)
+ local proc = luaproc(code)
+ if proc.status ~= 0 then
+ return error(proc.errout)
+ end
+ local r = {}
+ proc.output:gsub('(%S*)[%s]*',
+ function(x)
+ if x ~= '' then
+ r[x] = true
+ end
+ end)
+ return r
+end
+
+
+--- Show changes to tables wrought by a require statement.
+-- There are a few modes to this function, controlled by what named
+-- arguments are given. Lists new keys in T1 after `require 'import'`:
+--
+-- show_apis {added_to=T1, by=import}
+--
+-- List keys returned from `require 'import'`, which have the same
+-- value in T1:
+--
+-- show_apis {from=T1, used_by=import}
+--
+-- List keys from `require 'import'`, which are also in T1 but with
+-- a different value:
+--
+-- show_apis {from=T1, enhanced_by=import}
+--
+-- List keys from T2, which are also in T1 but with a different value:
+--
+-- show_apis {from=T1, enhanced_in=T2}
+--
+-- @tparam table argt one of the combinations above
+-- @treturn table a list of keys according to criteria above
+function show_apis(argt)
+ local added_to, from, not_in, enhanced_in, enhanced_after, by =
+ argt.added_to, argt.from, argt.not_in, argt.enhanced_in,
+ argt.enhanced_after, argt.by
+
+ if added_to and by then
+ return tabulate_output([[
+ local before, after = {}, {}
+ for k in pairs(]] .. added_to .. [[) do
+ before[k] = true
+ end
+
+ local M = require ']] .. by .. [['
+ for k in pairs(]] .. added_to .. [[) do
+ after[k] = true
+ end
+
+ for k in pairs(after) do
+ if not before[k] then
+ print(k)
+ end
+ end
+ ]])
+
+ elseif from and not_in then
+ return tabulate_output([[
+ local _ENV = require 'std.normalize' {
+ from = ']] .. from .. [[',
+ M = require ']] .. not_in .. [[',
+ }
+
+ for k in pairs(M) do
+ -- M[1] is typically the module namespace name, don't match
+ -- that!
+ if k ~= 1 and from[k] ~= M[k] then
+ print(k)
+ end
+ end
+ ]])
+
+ elseif from and enhanced_in then
+ return tabulate_output([[
+ local _ENV = require 'std.normalize' {
+ from = ']] .. from .. [[',
+ M = require ']] .. enhanced_in .. [[',
+ }
+
+ for k, v in pairs(M) do
+ if from[k] ~= M[k] and from[k] ~= nil then
+ print(k)
+ end
+ end
+ ]])
+
+ elseif from and enhanced_after then
+ return tabulate_output([[
+ local _ENV = require 'std.normalize' {
+ from = ']] .. from .. [[',
+ }
+ local before, after = {}, {}
+ for k, v in pairs(from) do
+ before[k] = v
+ end
+ ]] .. enhanced_after .. [[
+ for k, v in pairs(from) do
+ after[k] = v
+ end
+
+ for k, v in pairs(before) do
+ if after[k] ~= nil and after[k] ~= v then
+ print(k)
+ end
+ end
+ ]])
+ end
+
+ assert(false, 'missing argument to show_apis')
+end
+
+
+-- Stub inprocess.capture if necessary; new in Specl 12.
+capture = inprocess.capture or function(f, arg)
+ return nil, nil, f(unpack(arg or {}))
+end
+
+
+do
+ -- Custom matcher for set size and set membership.
+
+ local util = require 'specl.util'
+ local matchers = require 'specl.matchers'
+
+ local Matcher, matchers, q =
+ matchers.Matcher, matchers.matchers, matchers.stringify
+
+ matchers.have_size = Matcher {
+ function(self, actual, expect)
+ local size = 0
+ for _ in pairs(actual) do
+ size = size + 1
+ end
+ return size == expect
+ end,
+
+ actual = 'table',
+
+ format_expect = function(self, expect)
+ return ' a table containing ' .. expect .. ' elements, '
+ end,
+
+ format_any_of = function(self, alternatives)
+ return ' a table with any of ' ..
+ util.concat(alternatives, util.QUOTED) .. ' elements, '
+ end,
+ }
+
+ matchers.have_member = Matcher {
+ function(self, actual, expect)
+ return actual[expect] ~= nil
+ end,
+
+ actual = 'set',
+
+ format_expect = function(self, expect)
+ return ' a set containing ' .. q(expect) .. ', '
+ end,
+
+ format_any_of = function(self, alternatives)
+ return ' a set containing any of ' ..
+ util.concat(alternatives, util.QUOTED) .. ', '
+ end,
+ }
+
+ -- Alias that doesn't tickle sc_error_message_uppercase.
+ matchers.raise = matchers.error
+end
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/spec/std_spec.yaml b/Data/BuiltIn/Libraries/lua-stdlib/spec/std_spec.yaml
new file mode 100644
index 0000000..b747abf
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/spec/std_spec.yaml
@@ -0,0 +1,444 @@
+# General Lua Libraries for Lua 5.1, 5.2 & 5.3
+# Copyright (C) 2011-2018 stdlib authors
+
+before: |
+ this_module = 'std'
+ global_table = '_G'
+
+ exported_apis = {'assert', 'elems', 'eval', 'getmetamethod',
+ 'ielems', 'ipairs', 'npairs', 'pairs',
+ 'require', 'ripairs', 'rnpairs'}
+
+ -- Tables with iterator metamethods used by various examples.
+ __pairs = setmetatable({content='a string'}, {
+ __pairs = function(t)
+ return function(x, n)
+ if n < #x.content then
+ return n+1, string.sub(x.content, n+1, n+1)
+ end
+ end, t, 0
+ end,
+ })
+ __index = setmetatable({content='a string'}, {
+ __index = function(t, n)
+ if n <= #t.content then
+ return t.content:sub(n, n)
+ end
+ end,
+ __len = function(t)
+ return #t.content
+ end,
+ })
+
+ M = require(this_module)
+ M.version = nil -- previous specs may have autoloaded it
+
+
+specify std:
+- context when required:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by=this_module}).
+ to_equal {}
+ - it exports the documented apis:
+ t = {}
+ for k in pairs(M) do
+ t[#t + 1] = k
+ end
+ expect(t).to_contain.a_permutation_of(exported_apis)
+
+- context when lazy loading:
+ - it has no submodules on initial load:
+ for _, v in pairs(M) do
+ expect(type(v)).not_to_be 'table'
+ end
+ - it loads submodules on demand:
+ lazy = M.math
+ expect(lazy).to_be(require 'std.math')
+ - it loads submodule functions on demand:
+ expect(M.math.round(3.141592)).to_be(3)
+
+- describe assert:
+ - before:
+ f = M.assert
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.assert(?any, ?string, ?any*)')
+
+ - context when it does not trigger:
+ - it has a truthy initial argument:
+ expect(f(1)).not_to_raise 'any error'
+ expect(f(true)).not_to_raise 'any error'
+ expect(f 'yes').not_to_raise 'any error'
+ expect(f(false == false)).not_to_raise 'any error'
+ - it returns the initial argument:
+ expect(f(1)).to_be(1)
+ expect(f(true)).to_be(true)
+ expect(f 'yes').to_be 'yes'
+ expect(f(false == false)).to_be(true)
+ - context when it triggers:
+ - it has a falsey initial argument:
+ expect(f()).to_raise()
+ expect(f(false)).to_raise()
+ expect(f(1 == 0)).to_raise()
+ - it throws an optional error string:
+ expect(f(false, 'ah boo')).to_raise 'ah boo'
+ - it plugs specifiers with string.format: |
+ expect(f(nil, '%s %d: %q', 'here', 42, 'a string')).
+ to_raise(string.format('%s %d: %q', 'here', 42, 'a string'))
+
+
+- describe elems:
+ - before:
+ f = M.elems
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.elems(table)')
+
+ - it is an iterator over table values:
+ t = {}
+ for e in f {'foo', bar='baz', 42} do
+ t[#t + 1] = e
+ end
+ expect(t).to_contain.a_permutation_of {'foo', 'baz', 42}
+ - it respects __pairs metamethod: |
+ t = {}
+ for v in f(__pairs) do
+ t[#t + 1] = v
+ end
+ expect(t).
+ to_contain.a_permutation_of {'a', ' ', 's', 't', 'r', 'i', 'n', 'g'}
+ - it works for an empty list:
+ t = {}
+ for e in f {} do
+ t[#t + 1] = e
+ end
+ expect(t).to_equal {}
+
+
+- describe eval:
+ - before:
+ f = M.eval
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.eval(string)')
+
+ - it diagnoses invalid lua:
+ # Some internal error when eval tries to call uncompilable '=' code.
+ expect(f '=').to_raise()
+ - it evaluates a string of lua code:
+ expect(f 'math.min(2, 10)').to_be(math.min(2, 10))
+
+
+- describe getmetamethod:
+ - before:
+ f = M.getmetamethod
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.getmetamethod(?any, string)')
+
+ - context with a table:
+ - before:
+ method = function()
+ return 'called'
+ end
+ functor = setmetatable({}, {__call=method})
+ t = setmetatable({}, {
+ _type='table', _method=method, _functor=functor,
+ })
+ - it returns nil for missing metamethods:
+ expect(f(t, 'not a metamethod on t')).to_be(nil)
+ - it returns nil for non-callable metatable entries:
+ expect(f(t, '_type')).to_be(nil)
+ - it returns a method from the metatable:
+ expect(f(t, '_method')).to_be(method)
+ expect(f(t, '_method')()).to_be 'called'
+ - it returns a functor from the metatable:
+ expect(f(t, '_functor')).to_be(functor)
+ expect(f(t, '_functor')()).to_be 'called'
+
+
+- describe ielems:
+ - before:
+ f = M.ielems
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.ielems(table)')
+
+ - it is an iterator over integer-keyed table values:
+ t = {}
+ for e in f {'foo', 42} do
+ t[#t + 1] = e
+ end
+ expect(t).to_equal {'foo', 42}
+ - it ignores the dictionary part of a table:
+ t = {}
+ for e in f {'foo', 42; bar='baz', qux='quux'} do
+ t[#t + 1] = e
+ end
+ expect(t).to_equal {'foo', 42}
+ - it respects __len metamethod:
+ t = {}
+ for v in f(__index) do
+ t[#t + 1] = v
+ end
+ expect(t).to_equal {'a', ' ', 's', 't', 'r', 'i', 'n', 'g'}
+ - it works for an empty list:
+ t = {}
+ for e in f {} do
+ t[#t + 1] = e
+ end
+ expect(t).to_equal {}
+
+
+- describe ipairs:
+ - before:
+ f = M.ipairs
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.ipairs(table)')
+
+ - it is an iterator over integer-keyed table values:
+ t = {}
+ for i, v in f {'foo', 42} do
+ t[i] = v
+ end
+ expect(t).to_equal {'foo', 42}
+ - it ignores the dictionary part of a table:
+ t = {}
+ for i, v in f {'foo', 42; bar='baz', qux='quux'} do
+ t[i] = v
+ end
+ expect(t).to_equal {'foo', 42}
+ - it respects __len metamethod:
+ t = {}
+ for k, v in f(__index) do
+ t[k] = v
+ end
+ expect(t).to_equal {'a', ' ', 's', 't', 'r', 'i', 'n', 'g'}
+ - it works for an empty list:
+ t = {}
+ for i, v in f {} do
+ t[i] = v
+ end
+ expect(t).to_equal {}
+
+
+- describe npairs:
+ - before:
+ f = M.npairs
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.npairs(table)')
+
+ - it is an iterator over integer-keyed table values:
+ t = {}
+ for i, v in f {'foo', 42, nil, nil, 'five'} do
+ t[i] = v
+ end
+ expect(t).to_equal {'foo', 42, nil, nil, 'five'}
+ - it ignores the dictionary part of a table:
+ t = {}
+ for i, v in f {'foo', 42, nil, nil, 'five'; bar='baz', qux='quux'} do
+ t[i] = v
+ end
+ expect(t).to_equal {'foo', 42, nil, nil, 'five'}
+ - it respects __len metamethod:
+ t = {}
+ for _, v in f(setmetatable({[2]=false}, {__len=function(self) return 4 end})) do
+ t[#t + 1] = tostring(v)
+ end
+ expect(table.concat(t, ',')).to_be 'nil,false,nil,nil'
+ - it works for an empty list:
+ t = {}
+ for i, v in f {} do
+ t[i] = v
+ end
+ expect(t).to_equal {}
+
+
+- describe pairs:
+ - before:
+ f = M.pairs
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.pairs(table)')
+
+ - it is an iterator over all table values:
+ t = {}
+ for k, v in f {'foo', bar='baz', 42} do
+ t[k] = v
+ end
+ expect(t).to_equal {'foo', bar='baz', 42}
+ - it respects __pairs metamethod: |
+ t = {}
+ for k, v in f(__pairs) do
+ t[k] = v
+ end
+ expect(t).
+ to_contain.a_permutation_of {'a', ' ', 's', 't', 'r', 'i', 'n', 'g'}
+ - it works for an empty list:
+ t = {}
+ for k, v in f {} do
+ t[k] = v
+ end
+ expect(t).to_equal {}
+
+
+- describe require:
+ - before:
+ f = M.require
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.require(string, ?string, ?string, ?string)')
+
+ - it diagnoses non-existent module:
+ expect(f('module-not-exists', '', '')).to_raise 'module-not-exists'
+ - it diagnoses module too old:
+ expect(f('std', '9999', '9999')).
+ to_raise "require 'std' with at least version 9999,"
+ - it diagnoses module too new:
+ expect(f('std', '0', '0')).
+ to_raise "require 'std' with version less than 0,"
+ - context when the module version is compatible:
+ - it returns the module table:
+ expect(f('std', '0', '9999')).to_be(require 'std')
+ - it places no upper bound by default:
+ expect(f('std', '0')).to_be(require 'std')
+ - it places no lower bound by default:
+ expect(f 'std').to_be(require 'std')
+ - it uses _VERSION when version field is nil:
+ expect(luaproc [[
+ package.loaded['poop'] = {_VERSION='41.1'}
+ f = require 'std'.require
+ print(f('poop', '41', '9999')._VERSION)
+ ]]).to_succeed_with '41.1\n'
+ - context with semantic versioning:
+ - before:
+ std = require 'std'
+ ver = std.version
+ std.version = '1.2.3'
+ - after:
+ std.version = ver
+ - it diagnoses module too old:
+ expect(f('std', '1.2.4')).
+ to_raise "require 'std' with at least version 1.2.4,"
+ expect(f('std', '1.3')).
+ to_raise "require 'std' with at least version 1.3,"
+ expect(f('std', '2.1.2')).
+ to_raise "require 'std' with at least version 2.1.2,"
+ expect(f('std', '2')).
+ to_raise "require 'std' with at least version 2,"
+ expect(f('std', '1.2.10')).
+ to_raise "require 'std' with at least version 1.2.10,"
+ - it diagnoses module too new:
+ expect(f('std', nil, '1.2.2')).
+ to_raise "require 'std' with version less than 1.2.2,"
+ expect(f('std', nil, '1.1')).
+ to_raise "require 'std' with version less than 1.1,"
+ expect(f('std', nil, '1.1.2')).
+ to_raise "require 'std' with version less than 1.1.2,"
+ expect(f('std', nil, '1')).
+ to_raise "require 'std' with version less than 1,"
+ - it returns modules with version in range:
+ expect(f('std')).to_be(std)
+ expect(f('std', '1')).to_be(std)
+ expect(f('std', '1.2.3')).to_be(std)
+ expect(f('std', nil, '2')).to_be(std)
+ expect(f('std', nil, '1.3')).to_be(std)
+ expect(f('std', nil, '1.2.10')).to_be(std)
+ expect(f('std', '1.2.3', '1.2.4')).to_be(std)
+ - context with several numbers in version string:
+ - before:
+ std = require 'std'
+ ver = std.version
+ std.version = 'standard library for Lua 5.3 / 41.0.0'
+ - after:
+ std.version = ver
+ - it diagnoses module too old:
+ expect(f('std', '42')).to_raise()
+ - it diagnoses module too new:
+ expect(f('std', nil, '40')).to_raise()
+ - it returns modules with version in range:
+ expect(f('std')).to_be(std)
+ expect(f('std', '1')).to_be(std)
+ expect(f('std', '41')).to_be(std)
+ expect(f('std', nil, '42')).to_be(std)
+ expect(f('std', '41', '42')).to_be(std)
+
+
+- describe ripairs:
+ - before:
+ f = M.ripairs
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.ripairs(table)')
+
+ - it returns a function, the table and a number:
+ fn, t, i = f {1, 2, 3}
+ expect({type(fn), t, type(i)}).to_equal {'function', {1, 2, 3}, 'number'}
+ - it iterates over the array part of a table:
+ t, u = {1, 2, 3; a=4, b=5, c=6}, {}
+ for i, v in f(t) do
+ u[i] = v
+ end
+ expect(u).to_equal {1, 2, 3}
+ - it returns elements in reverse order:
+ t, u = {'one', 'two', 'five'}, {}
+ for _, v in f(t) do
+ u[#u + 1] = v
+ end
+ expect(u).to_equal {'five', 'two', 'one'}
+ - it respects __len metamethod:
+ t = {}
+ for i, v in f(__index) do
+ t[i] = v
+ end
+ expect(t).to_equal {'a', ' ', 's', 't', 'r', 'i', 'n', 'g'}
+ t = {}
+ for _, v in f(__index) do
+ t[#t + 1] = v
+ end
+ expect(t).to_equal {'g', 'n', 'i', 'r', 't', 's', ' ', 'a'}
+ - it works with the empty list:
+ t = {}
+ for k, v in f {} do
+ t[k] = v
+ end
+ expect(t).to_equal {}
+
+
+- describe rnpairs:
+ - before:
+ f = M.rnpairs
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.rnpairs(table)')
+
+ - it returns a function, the table and a number:
+ fn, t, i = f {1, 2, nil, nil, 3}
+ expect({type(fn), t, type(i)}).
+ to_equal {'function', {1, 2, nil, nil, 3}, 'number'}
+ - it iterates over the array part of a table:
+ t, u = {1, 2, nil, nil, 3; a=4, b=5, c=6}, {}
+ for i, v in f(t) do
+ u[i] = v
+ end
+ expect(u).to_equal {1, 2, nil, nil, 3}
+ - it returns elements in reverse order:
+ t, u, i = {'one', 'two', nil, nil, 'five'}, {}, 1
+ for _, v in f(t) do
+ u[i], i = v, i + 1
+ end
+ expect(u).to_equal {'five', nil, nil, 'two', 'one'}
+ - it respects __len metamethod:
+ t = {}
+ for _, v in f(setmetatable({[2]=false}, {__len=function(self) return 4 end})) do
+ t[#t + 1] = tostring(v)
+ end
+ expect(table.concat(t, ',')).to_be 'nil,nil,false,nil'
+ - it works with the empty list:
+ t = {}
+ for k, v in f {} do
+ t[k] = v
+ end
+ expect(t).to_equal {}
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/spec/string_spec.yaml b/Data/BuiltIn/Libraries/lua-stdlib/spec/string_spec.yaml
new file mode 100644
index 0000000..2fa47f2
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/spec/string_spec.yaml
@@ -0,0 +1,549 @@
+# General Lua Libraries for Lua 5.1, 5.2 & 5.3
+# Copyright (C) 2011-2018 stdlib authors
+
+before:
+ base_module = 'string'
+ this_module = 'std.string'
+ global_table = '_G'
+
+ extend_base = {'__concat', '__index',
+ 'caps', 'chomp', 'escape_pattern', 'escape_shell',
+ 'finds', 'format', 'ltrim',
+ 'numbertosi', 'ordinal_suffix', 'pad',
+ 'prettytostring', 'rtrim', 'split',
+ 'tfind', 'trim', 'wrap'}
+
+ M = require(this_module)
+ getmetatable('').__concat = M.__concat
+ getmetatable('').__index = M.__index
+
+specify std.string:
+- before:
+ subject = 'a string \n\n'
+
+- context when required:
+ - context by name:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by=this_module}).
+ to_equal {}
+ - it does not touch the core string table:
+ expect(show_apis {added_to=base_module, by=this_module}).
+ to_equal {}
+ - it contains apis from the core string table:
+ expect(show_apis {from=base_module, not_in=this_module}).
+ to_contain.a_permutation_of(extend_base)
+
+ - context via the std module:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by='std'}).
+ to_equal {}
+ - it does not touch the core string table:
+ expect(show_apis {added_to=base_module, by='std'}).
+ to_equal {}
+
+- describe ..:
+ - it concatenates string arguments:
+ target = 'a string \n\n another string'
+ expect(subject .. ' another string').to_be(target)
+ - it stringifies non-string arguments:
+ argument = {'a table'}
+ expect(subject .. argument).to_be(subject .. '{1="a table"}')
+ - it stringifies nil arguments:
+ argument = nil
+ expect(subject .. argument).
+ to_be(string.format('%s%s', subject, require 'std.normalize'.str(argument)))
+ - it does not perturb the original subject:
+ original = subject
+ newstring = subject .. ' concatenate something'
+ expect(subject).to_be(original)
+
+
+- describe caps:
+ - before:
+ f = M.caps
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.caps(string)')
+
+ - it capitalises words of a string:
+ target = 'A String \n\n'
+ expect(f(subject)).to_be(target)
+ - it changes only the first letter of each word:
+ expect(f 'a stRiNg').to_be 'A StRiNg'
+ - it is available as a string metamethod:
+ expect(('a stRiNg'):caps()).to_be 'A StRiNg'
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject)
+ expect(subject).to_be(original)
+
+
+- describe chomp:
+ - before:
+ target = 'a string \n'
+ f = M.chomp
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.chomp(string)')
+
+ - it removes a single trailing newline from a string:
+ expect(f(subject)).to_be(target)
+ - it does not change a string with no trailing newline:
+ subject = 'a string '
+ expect(f(subject)).to_be(subject)
+ - it is available as a string metamethod:
+ expect(subject:chomp()).to_be(target)
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject)
+ expect(subject).to_be(original)
+
+
+- describe escape_pattern:
+ - before:
+ magic = {}
+ meta = '^$()%.[]*+-?'
+ for i = 1, string.len(meta) do
+ magic[meta:sub(i, i)] = true
+ end
+ f = M.escape_pattern
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.escape_pattern(string)')
+
+ - context with each printable ASCII char:
+ - before:
+ subject, target = '', ''
+ for c = 32, 126 do
+ s = string.char(c)
+ subject = subject .. s
+ if magic[s] then
+ target = target .. '%'
+ end
+ target = target .. s
+ end
+ - 'it inserts a % before any non-alphanumeric in a string':
+ expect(f(subject)).to_be(target)
+ - it is available as a string metamethod:
+ expect(subject:escape_pattern()).to_be(target)
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject)
+ expect(subject).to_be(original)
+
+
+- describe escape_shell:
+ - before:
+ f = M.escape_shell
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.escape_shell(string)')
+
+ - context with each printable ASCII char:
+ - before:
+ subject, target = '', ''
+ for c = 32, 126 do
+ s = string.char(c)
+ subject = subject .. s
+ if s:match('[][ ()\\\'"]') then
+ target = target .. '\\'
+ end
+ target = target .. s
+ end
+ - 'it inserts a \\ before any shell metacharacters':
+ expect(f(subject)).to_be(target)
+ - it is available as a string metamethod:
+ expect(subject:escape_shell()).to_be(target)
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject)
+ expect(subject).to_be(original)
+ - 'it diagnoses non-string arguments':
+ if typecheck then
+ expect(f()).to_raise('string expected')
+ expect(f {'a table'}).to_raise('string expected')
+ end
+
+
+- describe finds:
+ - before:
+ subject = 'abcd'
+ f = M.finds
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.finds(string, string, ?int, ?boolean|:plain)')
+
+ - context given a complex nested list:
+ - before:
+ target = {{1, 2; capt={'a', 'b'}}, {3, 4; capt={'c', 'd'}}}
+ - it creates a list of pattern captures:
+ expect({f(subject, '(.)(.)')}).to_equal({target})
+ - it is available as a string metamethod:
+ expect({subject:finds('(.)(.)')}).to_equal({target})
+ - it creates an empty list where no captures are matched:
+ target = {}
+ expect({f(subject, '(x)')}).to_equal({target})
+ - it creates an empty list for a pattern without captures:
+ target = {{1, 1; capt={}}}
+ expect({f(subject, 'a')}).to_equal({target})
+ - it starts the search at a specified index into the subject:
+ target = {{8, 9; capt={'a', 'b'}}, {10, 11; capt={'c', 'd'}}}
+ expect({f('garbage' .. subject, '(.)(.)', 8)}).to_equal({target})
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject, '...')
+ expect(subject).to_be(original)
+
+
+- describe format:
+ - before:
+ subject = 'string=%s, number=%d'
+
+ f = M.format
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.format(string, ?any*)')
+
+ - it returns a single argument without attempting formatting:
+ expect(f(subject)).to_be(subject)
+ - it is available as a string metamethod:
+ expect(subject:format()).to_be(subject)
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject)
+ expect(subject).to_be(original)
+
+
+- describe ltrim:
+ - before:
+ subject = ' \t\r\n a short string \t\r\n '
+
+ f = M.ltrim
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.ltrim(string, ?string)')
+
+ - it removes whitespace from the start of a string:
+ target = 'a short string \t\r\n '
+ expect(f(subject)).to_equal(target)
+ - it supports custom removal patterns:
+ target = '\r\n a short string \t\r\n '
+ expect(f(subject, '[ \t\n]+')).to_equal(target)
+ - it is available as a string metamethod:
+ target = '\r\n a short string \t\r\n '
+ expect(subject:ltrim('[ \t\n]+')).to_equal(target)
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject, '%W')
+ expect(subject).to_be(original)
+
+
+- describe numbertosi:
+ - before:
+ f = M.numbertosi
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.numbertosi(number|string)')
+
+ - it returns a number using SI suffixes:
+ target = {'1e-9', '1y', '1z', '1a', '1f', '1p', '1n', '1mu', '1m', '1',
+ '1k', '1M', '1G', '1T', '1P', '1E', '1Z', '1Y', '1e9'}
+ subject = {}
+ for n = -28, 28, 3 do
+ m = 10 *(10 ^ n)
+ table.insert(subject, f(m))
+ end
+ expect(subject).to_equal(target)
+ - it coerces string arguments to a number:
+ expect(f '1000').to_be '1k'
+
+
+- describe ordinal_suffix:
+ - before:
+ f = M.ordinal_suffix
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.ordinal_suffix(int|string)')
+
+ - it returns the English suffix for a number:
+ subject, target = {}, {}
+ for n = -120, 120 do
+ suffix = 'th'
+ m = math.abs(n) % 10
+ if m == 1 and math.abs(n) % 100 ~= 11 then
+ suffix = 'st'
+ elseif m == 2 and math.abs(n) % 100 ~= 12 then
+ suffix = 'nd'
+ elseif m == 3 and math.abs(n) % 100 ~= 13 then
+ suffix = 'rd'
+ end
+ table.insert(target, n .. suffix)
+ table.insert(subject, n .. f(n))
+ end
+ expect(subject).to_equal(target)
+ - it coerces string arguments to a number:
+ expect(f '-91').to_be 'st'
+
+
+- describe pad:
+ - before:
+ width = 20
+
+ f = M.pad
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.pad(string, int, ?string)')
+
+ - context when string is shorter than given width:
+ - before:
+ subject = 'short string'
+ - it right pads a string to the given width with spaces:
+ target = 'short string '
+ expect(f(subject, width)).to_be(target)
+ - it left pads a string to the given negative width with spaces:
+ width = -width
+ target = ' short string'
+ expect(f(subject, width)).to_be(target)
+ - it is available as a string metamethod:
+ target = 'short string '
+ expect(subject:pad(width)).to_be(target)
+
+ - context when string is longer than given width:
+ - before:
+ subject = "a string that's longer than twenty characters"
+ - it truncates a string to the given width:
+ target = "a string that's long"
+ expect(f(subject, width)).to_be(target)
+ - it left pads a string to given width with spaces:
+ width = -width
+ target = 'an twenty characters'
+ expect(f(subject, width)).to_be(target)
+ - it is available as a string metamethod:
+ target = "a string that's long"
+ expect(subject:pad(width)).to_be(target)
+
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject, width)
+ expect(subject).to_be(original)
+
+
+- describe prettytostring:
+ - before:
+ f = M.prettytostring
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.prettytostring(?any, ?string, ?string)')
+
+ - it renders nil exactly like system tostring:
+ expect(f(nil)).to_be(tostring(nil))
+ - it renders booleans exactly like system tostring:
+ expect(f(true)).to_be(tostring(true))
+ expect(f(false)).to_be(tostring(false))
+ - it renders numbers exactly like system tostring:
+ n = 8723643
+ expect(f(n)).to_be(tostring(n))
+ - it renders functions exactly like system tostring:
+ expect(f(f)).to_be(tostring(f))
+ - it renders strings with format '%q' styling:
+ s = 'a string'
+ expect(f(s)).to_be(string.format('%q', s))
+ - it renders empty tables as a pair of braces:
+ expect(f {}).to_be('{\n}')
+ - it renders an array prettily:
+ a = {'one', 'two', 'three'}
+ expect(f(a, '')).
+ to_be '{\n[1] = "one",\n[2] = "two",\n[3] = "three",\n}'
+ - it renders a table prettily:
+ t = {one=true, two=2, three={3}}
+ expect(f(t, '')).
+ to_be '{\none = true,\nthree =\n{\n[1] = 3,\n},\ntwo = 2,\n}'
+ - it renders table keys in table.sort order:
+ t = {one=3, two=5, three=4, four=2, five=1}
+ expect(f(t, '')).
+ to_be '{\nfive = 1,\nfour = 2,\none = 3,\nthree = 4,\ntwo = 5,\n}'
+ - it renders keys with invalid symbol names in long hand:
+ t = {_=0, word=0, ['?']=1, ['a-key']=1, ['[]']=1}
+ expect(f(t, '')).
+ to_be '{\n["?"] = 1,\n["[]"] = 1,\n_ = 0,\n["a-key"] = 1,\nword = 0,\n}'
+
+
+- describe rtrim:
+ - before:
+ subject = ' \t\r\n a short string \t\r\n '
+
+ f = M.rtrim
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.rtrim(string, ?string)')
+
+ - it removes whitespace from the end of a string:
+ target = ' \t\r\n a short string'
+ expect(f(subject)).to_equal(target)
+ - it supports custom removal patterns:
+ target = ' \t\r\n a short string \t\r'
+ expect(f(subject, '[ \t\n]+')).to_equal(target)
+ - it is available as a string metamethod:
+ target = ' \t\r\n a short string \t\r'
+ expect(subject:rtrim('[ \t\n]+')).to_equal(target)
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject, '%W')
+ expect(subject).to_be(original)
+
+
+- describe split:
+ - before:
+ target = {'first', 'the second one', 'final entry'}
+ subject = table.concat(target, ', ')
+
+ f = M.split
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.split(string, ?string)')
+
+ - it falls back to '%s+' when no pattern is given:
+ expect(f(subject)).
+ to_equal {'first,', 'the', 'second', 'one,', 'final', 'entry'}
+ - it returns a one-element list for an empty string:
+ expect(f('', ', ')).to_equal {''}
+ - it makes a table of substrings delimited by a separator:
+ expect(f(subject, ', ')).to_equal(target)
+ - it returns n+1 elements for n separators:
+ expect(f(subject, 'zero')).to_have_size(1)
+ expect(f(subject, 'c')).to_have_size(2)
+ expect(f(subject, 's')).to_have_size(3)
+ expect(f(subject, 't')).to_have_size(4)
+ expect(f(subject, 'e')).to_have_size(5)
+ - it returns an empty string element for consecutive separators:
+ expect(f('xyzyzxy', 'yz')).to_equal {'x', '', 'xy'}
+ - it returns an empty string element when starting with separator:
+ expect(f('xyzyzxy', 'xyz')).to_equal {'', 'yzxy'}
+ - it returns an empty string element when ending with separator:
+ expect(f('xyzyzxy', 'zxy')).to_equal {'xyzy', ''}
+ - it returns a table of 1-character strings for '' separator:
+ expect(f('abcdef', '')).to_equal {'', 'a', 'b', 'c', 'd', 'e', 'f', ''}
+ - it is available as a string metamethod:
+ expect(subject:split ', ').to_equal(target)
+ expect(('/foo/bar/baz.quux'):split '/').
+ to_equal {'', 'foo', 'bar', 'baz.quux'}
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject, 'e')
+ expect(subject).to_be(original)
+ - it takes a Lua pattern as a separator:
+ expect(f(subject, '%s+')).
+ to_equal {'first,', 'the', 'second', 'one,', 'final', 'entry'}
+
+
+- describe tfind:
+ - before:
+ subject = 'abc'
+
+ f = M.tfind
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.tfind(string, string, ?int, ?boolean|:plain)')
+
+ - it creates a list of pattern captures:
+ target = {1, 3, {'a', 'b', 'c'}}
+ expect({f(subject, '(.)(.)(.)')}).to_equal(target)
+ - it creates an empty list where no captures are matched:
+ target = {nil, nil, {}}
+ expect({f(subject, '(x)(y)(z)')}).to_equal(target)
+ - it creates an empty list for a pattern without captures:
+ target = {1, 1, {}}
+ expect({f(subject, 'a')}).to_equal(target)
+ - it starts the search at a specified index into the subject:
+ target = {8, 10, {'a', 'b', 'c'}}
+ expect({f('garbage' .. subject, '(.)(.)(.)', 8)}).to_equal(target)
+ - it is available as a string metamethod:
+ target = {8, 10, {'a', 'b', 'c'}}
+ expect({('garbage' .. subject):tfind('(.)(.)(.)', 8)}).to_equal(target)
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject, '...')
+ expect(subject).to_be(original)
+
+
+- describe trim:
+ - before:
+ subject = ' \t\r\n a short string \t\r\n '
+
+ f = M.trim
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.trim(string, ?string)')
+
+ - it removes whitespace from each end of a string:
+ target = 'a short string'
+ expect(f(subject)).to_equal(target)
+ - it supports custom removal patterns:
+ target = '\r\n a short string \t\r'
+ expect(f(subject, '[ \t\n]+')).to_equal(target)
+ - it is available as a string metamethod:
+ target = '\r\n a short string \t\r'
+ expect(subject:trim('[ \t\n]+')).to_equal(target)
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject, '%W')
+ expect(subject).to_be(original)
+
+
+- describe wrap:
+ - before:
+ subject = 'This is a collection of Lua libraries for Lua 5.1 ' ..
+ 'and 5.2. The libraries are copyright by their authors 2000' ..
+ '-2015 (see the AUTHORS file for details), and released und' ..
+ 'er the MIT license (the same license as Lua itself). There' ..
+ ' is no warranty.'
+
+ f = M.wrap
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.string.wrap(string, ?int, ?int, ?int)')
+
+ - it inserts newlines to wrap a string:
+ target = 'This is a collection of Lua libraries for Lua 5.1 a' ..
+ 'nd 5.2. The libraries are\ncopyright by their authors 2000' ..
+ '-2015 (see the AUTHORS file for details), and\nreleased un' ..
+ 'der the MIT license (the same license as Lua itself). Ther' ..
+ 'e is no\nwarranty.'
+ expect(f(subject)).to_be(target)
+ - it honours a column width parameter:
+ target = 'This is a collection of Lua libraries for Lua 5.1 a' ..
+ 'nd 5.2. The libraries\nare copyright by their authors 2000' ..
+ '-2015 (see the AUTHORS file for\ndetails), and released un' ..
+ 'der the MIT license (the same license as Lua\nitself). The' ..
+ 're is no warranty.'
+ expect(f(subject, 72)).to_be(target)
+ - it supports indenting by a fixed number of columns:
+ target = ' This is a collection of Lua libraries for L' ..
+ 'ua 5.1 and 5.2. The\n libraries are copyright by th' ..
+ 'eir authors 2000-2015 (see the\n AUTHORS file for d' ..
+ 'etails), and released under the MIT license\n (the ' ..
+ 'same license as Lua itself). There is no warranty.'
+ expect(f(subject, 72, 8)).to_be(target)
+ - context given a long unwrapped string:
+ - before:
+ target = ' This is a collection of Lua libraries for Lua 5' ..
+ '.1 and 5.2.\n The libraries are copyright by their author' ..
+ 's 2000-2015 (see\n the AUTHORS file for details), and rel' ..
+ 'eased under the MIT\n license (the same license as Lua it' ..
+ 'self). There is no\n warranty.'
+ - it can indent the first line differently:
+ expect(f(subject, 64, 2, 4)).to_be(target)
+ - it is available as a string metamethod:
+ expect(subject:wrap(64, 2, 4)).to_be(target)
+ - it does not perturb the original subject:
+ original = subject
+ newstring = f(subject, 55, 5)
+ expect(subject).to_be(original)
+ - it diagnoses indent greater than line width:
+ expect(f(subject, 10, 12)).to_raise('less than the line width')
+ expect(f(subject, 99, 99)).to_raise('less than the line width')
+ - it diagnoses non-string arguments:
+ if have_typecheck then
+ expect(f()).to_raise('string expected')
+ expect(f {'a table'}).to_raise('string expected')
+ end
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/spec/table_spec.yaml b/Data/BuiltIn/Libraries/lua-stdlib/spec/table_spec.yaml
new file mode 100644
index 0000000..d9fed0c
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/spec/table_spec.yaml
@@ -0,0 +1,589 @@
+# General Lua Libraries for Lua 5.1, 5.2 & 5.3
+# Copyright (C) 2011-2018 stdlib authors
+
+before: |
+ base_module = 'table'
+ this_module = 'std.table'
+ global_table = '_G'
+
+ extend_base = {'clone', 'clone_select', 'depair', 'empty',
+ 'enpair', 'insert', 'invert', 'keys', 'maxn',
+ 'merge', 'merge_select', 'new',
+ 'pack', 'project', 'remove', 'size', 'sort',
+ 'unpack', 'values'}
+
+ M = require(this_module)
+
+
+specify std.table:
+- context when required:
+ - context by name:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by=this_module}).
+ to_equal {}
+ - it does not touch the core table table:
+ expect(show_apis {added_to=base_module, by=this_module}).
+ to_equal {}
+ - it contains apis from the core table table:
+ expect(show_apis {from=base_module, not_in=this_module}).
+ to_contain.a_permutation_of(extend_base)
+
+ - context via the std module:
+ - it does not touch the global table:
+ expect(show_apis {added_to=global_table, by='std'}).to_equal {}
+ - it does not touch the core table table:
+ expect(show_apis {added_to=base_module, by='std'}).to_equal {}
+
+
+- describe clone:
+ - before:
+ subject = {k1={'v1'}, k2={'v2'}, k3={'v3'}}
+ withmt = setmetatable(M.clone(subject), {'meta!'})
+
+ f = M.clone
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.clone(table, [table], ?boolean|:nometa)')
+
+ - it does not just return the subject:
+ expect(f(subject)).not_to_be(subject)
+ - it does copy the subject:
+ expect(f(subject)).to_equal(subject)
+ - it only makes a shallow copy of field values:
+ expect(f(subject).k1).to_be(subject.k1)
+ - it does not perturb the original subject:
+ target = {k1=subject.k1, k2=subject.k2, k3=subject.k3}
+ copy = f(subject)
+ expect(subject).to_equal(target)
+ expect(subject).to_be(subject)
+
+ - context with metatables:
+ - it copies the metatable by default:
+ expect(getmetatable(f(withmt))).to_be(getmetatable(withmt))
+ - it treats non-table arg2 as nometa parameter:
+ expect(getmetatable(f(withmt, ':nometa'))).to_be(nil)
+ - it treats table arg2 as a map parameter:
+ expect(getmetatable(f(withmt, {}))).to_be(getmetatable(withmt))
+ - it supports 3 arguments with nometa as arg3:
+ expect(getmetatable(f(withmt, {}, ':nometa'))).to_be(nil)
+
+ - context when renaming some keys:
+ - it renames during cloning:
+ target = {newkey=subject.k1, k2=subject.k2, k3=subject.k3}
+ expect(f(subject, {k1 = 'newkey'})).to_equal(target)
+ - it does not perturb the value in the renamed key field:
+ expect(f(subject, {k1 = 'newkey'}).newkey).to_be(subject.k1)
+
+
+- describe clone_select:
+ - before:
+ subject = {k1={'v1'}, k2={'v2'}, k3={'v3'}}
+ withmt = setmetatable(M.clone(subject), {'meta!'})
+
+ f = M.clone_select
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.clone_select(table, [table], ?boolean|:nometa)')
+
+ - it does not just return the subject:
+ expect(f(subject)).not_to_be(subject)
+ - it copies the keys selected:
+ expect(f(subject, {'k1', 'k2'})).to_equal({k1={'v1'}, k2={'v2'}})
+ - it does copy the subject when supplied with a full list of keys:
+ expect(f(subject, {'k1', 'k2', 'k3'})).to_equal(subject)
+ - it only makes a shallow copy:
+ expect(f(subject, {'k1'}).k1).to_be(subject.k1)
+ - it does not perturb the original subject:
+ target = {k1=subject.k1, k2=subject.k2, k3=subject.k3}
+ copy = f(subject, {'k1', 'k2', 'k3'})
+ expect(subject).to_equal(target)
+ expect(subject).to_be(subject)
+
+ - context with metatables:
+ - it treats non-table arg2 as nometa parameter:
+ expect(getmetatable(f(withmt, ':nometa'))).to_be(nil)
+ - it treats table arg2 as a map parameter:
+ expect(getmetatable(f(withmt, {}))).to_be(getmetatable(withmt))
+ expect(getmetatable(f(withmt, {'k1'}))).to_be(getmetatable(withmt))
+ - it supports 3 arguments with nometa as arg3:
+ expect(getmetatable(f(withmt, {}, ':nometa'))).to_be(nil)
+ expect(getmetatable(f(withmt, {'k1'}, ':nometa'))).to_be(nil)
+
+
+- describe depair:
+ - before:
+ t = {'first', 'second', third = 4}
+ l = M.enpair(t)
+
+ f = M.depair
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.depair(list of lists)')
+
+ - it returns a primitive table:
+ expect(objtype(f(l))).to_be 'table'
+ - it works with an empty table:
+ expect(f {}).to_equal {}
+ - it is the inverse of enpair:
+ expect(f(l)).to_equal(t)
+
+
+- describe empty:
+ - before:
+ f = M.empty
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.empty(table)')
+
+ - it returns true for an empty table:
+ expect(f {}).to_be(true)
+ expect(f {nil}).to_be(true)
+ - it returns false for a non-empty table:
+ expect(f {'stuff'}).to_be(false)
+ expect(f {{}}).to_be(false)
+ expect(f {false}).to_be(false)
+
+
+- describe enpair:
+ - before:
+ t = {'first', 'second', third = 4}
+ l = M.enpair(t)
+
+ f = M.enpair
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.enpair(table)')
+
+ - it returns a table:
+ expect(objtype(f(t))).to_be 'table'
+ - it works for an empty table:
+ expect(f {}).to_equal {}
+ - it turns a table into a table of pairs:
+ expect(f(t)).to_equal {{1, 'first'}, {2, 'second'}, {'third', 4}}
+ - it is the inverse of depair:
+ expect(f(t)).to_equal(l)
+
+
+- describe insert:
+ - before:
+ f, badarg = init(M, this_module, 'insert')
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.insert(table, [int], any)')
+
+ examples {
+ ['it diagnoses more than 2 arguments with no pos'] = function()
+ pending '#issue 76'
+ expect(f({}, false, false)).to_raise(badarg(3))
+ end
+ }
+ examples {
+ ['it diagnoses out of bounds pos arguments'] = function()
+ expect(f({}, 0, 'x')).to_raise 'position 0 out of bounds'
+ expect(f({}, 2, 'x')).to_raise 'position 2 out of bounds'
+ expect(f({1}, 5, 'x')).to_raise 'position 5 out of bounds'
+ end
+ }
+
+ - it returns the modified table:
+ t = {}
+ expect(f(t, 1)).to_be(t)
+ - it append a new element at the end by default:
+ expect(f({1, 2}, 'x')).to_equal {1, 2, 'x'}
+ - it fills holes by default:
+ expect(f({1, 2, [5]=3}, 'x')).to_equal {1, 2, 'x', [5]=3}
+ - it respects __len when appending:
+ t = setmetatable({1, 2, [5]=3}, {__len = function() return 42 end})
+ expect(f(t, 'x')).to_equal {1, 2, [5]=3, [43]='x'}
+ - it moves other elements up if necessary:
+ expect(f({1, 2}, 1, 'x')).to_equal {'x', 1, 2}
+ expect(f({1, 2}, 2, 'x')).to_equal {1, 'x', 2}
+ expect(f({1, 2}, 3, 'x')).to_equal {1, 2, 'x'}
+ - it inserts a new element according to pos argument:
+ expect(f({}, 1, 'x')).to_equal {'x'}
+
+
+- describe invert:
+ - before:
+ subject = {k1=1, k2=2, k3=3}
+
+ f = M.invert
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.invert(table)')
+
+ - it returns a new table:
+ expect(f(subject)).not_to_be(subject)
+ - it inverts keys and values in the returned table:
+ expect(f(subject)).to_equal {'k1', 'k2', 'k3'}
+ - it is reversible:
+ expect(f(f(subject))).to_equal(subject)
+ - it seems to copy a list of 1..n numbers:
+ subject = {1, 2, 3}
+ expect(f(subject)).to_copy(subject)
+
+
+- describe keys:
+ - before:
+ subject = {k1=1, k2=2, k3=3}
+
+ f = M.keys
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.keys(table)')
+
+ - it returns an empty list when subject is empty:
+ expect(f {}).to_equal {}
+ - it makes a list of table keys:
+ cmp = function(a, b)
+ return a < b
+ end
+ expect(M.sort(f(subject), cmp)).to_equal {'k1', 'k2', 'k3'}
+ - it does not guarantee stable ordering:
+ subject = {}
+ -- is this a good test? there is a vanishingly small possibility the
+ -- returned table will have all 10000 keys in the same order...
+ for i = 10000, 1, -1 do
+ table.insert(subject, i)
+ end
+ expect(f(subject)).not_to_equal(subject)
+
+
+- describe maxn:
+ - before:
+ f = M.maxn
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.maxn(table)')
+
+ - it returns the largest numeric key of a table:
+ expect(f {'a', 'b', 'c'}).to_be(3)
+ expect(f {1, 2, 5, a=10, 3}).to_be(4)
+ - it works with an empty table:
+ expect(f {}).to_be(0)
+ - it ignores holes:
+ expect(f {1, 2, [5]=3}).to_be(5)
+ - it ignores __len metamethod:
+ t = setmetatable({1, 2, [5]=3}, {__len = function() return 42 end})
+ expect(f(t)).to_be(5)
+
+
+- describe merge:
+ - before: |
+ -- Additional merge keys which are moderately unusual
+ t1 = {k1={'v1'}, k2='if', k3={'?'}}
+ t2 = {['if']=true, [{'?'}]=false, _='underscore', k3=t1.k1}
+ t1mt = setmetatable(M.clone(t1), {'meta!'})
+ target = {}
+ for k, v in pairs(t1) do
+ target[k] = v
+ end
+ for k, v in pairs(t2) do
+ target[k] = v
+ end
+
+ f, badarg = init(M, this_module, 'merge')
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.merge(table, table, [table], ?boolean|:nometa)')
+
+ examples {
+ ['it diagnoses more than 2 arguments with no pos'] = function()
+ pending '#issue 76'
+ expect(f({}, {}, ':nometa', false)).to_raise(badarg(4))
+ end
+ }
+
+ - it does not create a whole new table:
+ expect(f(t1, t2)).to_be(t1)
+ - it does not change t1 when t2 is empty:
+ expect(f(t1, {})).to_be(t1)
+ - it copies t2 when t1 is empty:
+ expect(f({}, t1)).to_copy(t1)
+ - it merges keys from t2 into t1:
+ expect(f(t1, t2)).to_equal(target)
+ - it gives precedence to values from t2:
+ original = M.clone(t1)
+ m = f(t1, t2) -- Merge is destructive, do it once only.
+ expect(m.k3).to_be(t2.k3)
+ expect(m.k3).not_to_be(original.k3)
+ - it only makes a shallow copy of field values:
+ expect(f({}, t1).k1).to_be(t1.k1)
+
+ - context with metatables:
+ - it copies the metatable by default:
+ expect(getmetatable(f({}, t1mt))).to_be(getmetatable(t1mt))
+ expect(getmetatable(f({}, t1mt, {'k1'}))).to_be(getmetatable(t1mt))
+ - it treats non-table arg3 as nometa parameter:
+ expect(getmetatable(f({}, t1mt, ':nometa'))).to_be(nil)
+ - it treats table arg3 as a map parameter:
+ expect(getmetatable(f({}, t1mt, {}))).to_be(getmetatable(t1mt))
+ expect(getmetatable(f({}, t1mt, {'k1'}))).to_be(getmetatable(t1mt))
+ - it supports 4 arguments with nometa as arg4:
+ expect(getmetatable(f({}, t1mt, {}, ':nometa'))).to_be(nil)
+ expect(getmetatable(f({}, t1mt, {'k1'}, ':nometa'))).to_be(nil)
+
+ - context when renaming some keys:
+ - it renames during merging:
+ target = {newkey=t1.k1, k2=t1.k2, k3=t1.k3}
+ expect(f({}, t1, {k1 = 'newkey'})).to_equal(target)
+ - it does not perturb the value in the renamed key field:
+ expect(f({}, t1, {k1 = 'newkey'}).newkey).to_be(t1.k1)
+
+
+- describe merge_select:
+ - before: |
+ -- Additional merge keys which are moderately unusual
+ tablekey = {'?'}
+ t1 = {k1={'v1'}, k2='if', k3={'?'}}
+ t1mt = setmetatable(M.clone(t1), {'meta!'})
+ t2 = {['if']=true, [tablekey]=false, _='underscore', k3=t1.k1}
+ t2keys = {'if', tablekey, '_', 'k3'}
+ target = {}
+ for k, v in pairs(t1) do
+ target[k] = v
+ end
+ for k, v in pairs(t2) do
+ target[k] = v
+ end
+
+ f, badarg = init(M, this_module, 'merge_select')
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.merge_select(table, table, [table], ?boolean|:nometa)')
+
+ examples {
+ ['it diagnoses more than 2 arguments with no pos'] = function()
+ pending '#issue 76'
+ expect(f({}, {}, ':nometa', false)).to_raise(badarg(4))
+ end
+ }
+
+ - it does not create a whole new table:
+ expect(f(t1, t2)).to_be(t1)
+ - it does not change t1 when t2 is empty:
+ expect(f(t1, {})).to_be(t1)
+ - it does not change t1 when key list is empty:
+ expect(f(t1, t2, {})).to_be(t1)
+ - it copies the named fields:
+ expect(f({}, t2, t2keys)).to_equal(t2)
+ - it makes a shallow copy:
+ expect(f({}, t1, {'k1'}).k1).to_be(t1.k1)
+ - it copies exactly named fields of t2 when t1 is empty:
+ expect(f({}, t1, {'k1', 'k2', 'k3'})).to_copy(t1)
+ - it merges keys from t2 into t1:
+ expect(f(t1, t2, t2keys)).to_equal(target)
+ - it gives precedence to values from t2:
+ original = M.clone(t1)
+ m = f(t1, t2, t2keys) -- Merge is destructive, do it once only.
+ expect(m.k3).to_be(t2.k3)
+ expect(m.k3).not_to_be(original.k3)
+
+ - context with metatables:
+ - it copies the metatable by default:
+ expect(getmetatable(f({}, t1mt))).to_be(getmetatable(t1mt))
+ expect(getmetatable(f({}, t1mt, {'k1'}))).to_be(getmetatable(t1mt))
+ - it treats non-table arg3 as nometa parameter:
+ expect(getmetatable(f({}, t1mt, ':nometa'))).to_be(nil)
+ - it treats table arg3 as a map parameter:
+ expect(getmetatable(f({}, t1mt, {}))).to_be(getmetatable(t1mt))
+ expect(getmetatable(f({}, t1mt, {'k1'}))).to_be(getmetatable(t1mt))
+ - it supports 4 arguments with nometa as arg4:
+ expect(getmetatable(f({}, t1mt, {}, ':nometa'))).to_be(nil)
+ expect(getmetatable(f({}, t1mt, {'k1'}, ':nometa'))).to_be(nil)
+
+
+- describe new:
+ - before:
+ f = M.new
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.new(?any, ?table)')
+
+ - context when not setting a default:
+ - before: default = nil
+ - it returns a new table when nil is passed:
+ expect(f(default, nil)).to_equal {}
+ - it returns any table passed in:
+ t = {'unique table'}
+ expect(f(default, t)).to_be(t)
+
+ - context when setting a default:
+ - before:
+ default = 'default'
+ - it returns a new table when nil is passed:
+ expect(f(default, nil)).to_equal {}
+ - it returns any table passed in:
+ t = {'unique table'}
+ expect(f(default, t)).to_be(t)
+
+ - it returns the stored value for existing keys:
+ t = f('default')
+ v = {'unique value'}
+ t[1] = v
+ expect(t[1]).to_be(v)
+ - it returns the constructor default for unset keys:
+ t = f('default')
+ expect(t[1]).to_be 'default'
+ - it returns the actual default object:
+ default = {'unique object'}
+ t = f(default)
+ expect(t[1]).to_be(default)
+
+
+- describe pack:
+ - before:
+ unpack = unpack or table.unpack
+ t = {'one', 'two', 'five', n=3}
+ f = M.pack
+ - it creates an empty table with no arguments:
+ expect(f()).to_equal {n=0}
+ - it creates a table with arguments as elements:
+ expect(f('one', 'two', 'five')).to_equal(t)
+ - it is the inverse operation to unpack:
+ expect(f(unpack(t))).to_equal(t)
+ - it saves the tuple length in field n:
+ expect(f(1, 2, 5).n).to_be(3)
+ expect(f('', false, nil).n).to_be(3)
+ expect(f(nil, nil, nil).n).to_be(3)
+
+
+- describe project:
+ - before:
+ l = {
+ {first = false, second = true, third = true},
+ {first = 1, second = 2, third = 3},
+ {first = '1st', second = '2nd', third = '3rd'},
+ }
+
+ f = M.project
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.project(any, list of tables)')
+
+ - it returns a table:
+ expect(objtype(f('third', l))).to_be 'table'
+ - it works with an empty table:
+ expect(f('third', {})).to_equal {}
+ - it projects a table of fields from a table of tables:
+ expect(f('third', l)).to_equal {true, 3, '3rd'}
+ - it projects fields with a falsey value correctly:
+ expect(f('first', l)).to_equal {false, 1, '1st'}
+
+
+- describe remove:
+ - before:
+ f = M.remove
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.remove(table, ?int)')
+
+ examples {
+ ['it diagnoses out of bounds pos arguments'] = function()
+ expect(f({1}, 0)).to_raise 'position 0 out of bounds'
+ expect(f({1}, 3)).to_raise 'position 3 out of bounds'
+ expect(f({1}, 5)).to_raise 'position 5 out of bounds'
+ end
+ }
+
+ - it returns the removed element:
+ t = {'one', 'two', 'five'}
+ expect(f({'one', 2, 5}, 1)).to_be 'one'
+ - it removes an element from the end by default:
+ expect(f {1, 2, 'five'}).to_be 'five'
+ - it ignores holes:
+ t = {'second', 'first', [5]='invisible'}
+ expect(f(t)).to_be 'first'
+ expect(f(t)).to_be 'second'
+ - it respects __len when defaulting pos:
+ t = setmetatable({1, 2, [43]='invisible'}, {__len = function() return 42 end})
+ expect(f(t)).to_be(nil)
+ expect(f(t)).to_be(nil)
+ expect(t).to_equal {1, 2, [43]='invisible'}
+ - it moves other elements down if necessary:
+ t = {1, 2, 5, 'third', 'first', 'second', 42}
+ expect(f(t, 5)).to_be 'first'
+ expect(t).to_equal {1, 2, 5, 'third', 'second', 42}
+ expect(f(t, 5)).to_be 'second'
+ expect(t).to_equal {1, 2, 5, 'third', 42}
+ expect(f(t, 4)).to_be 'third'
+ expect(t).to_equal {1, 2, 5, 42}
+
+
+- describe size:
+ - before: |
+ -- - 1 - ------- 2 ------- -- 3 --
+ subject = {'one', {{'two'}, 'three'}, four=5}
+
+ f = M.size
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.size(table)')
+
+ - it counts the number of keys in a table:
+ expect(f(subject)).to_be(3)
+ - it counts no keys in an empty table:
+ expect(f {}).to_be(0)
+
+
+- describe sort:
+ - before:
+ subject = {5, 2, 4, 1, 0, 3}
+ target = {0, 1, 2, 3, 4, 5}
+ cmp = function(a, b) return a < b end
+
+ f = M.sort
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.sort(table, ?function)')
+
+ - it sorts elements in place:
+ f(subject, cmp)
+ expect(subject).to_equal(target)
+ - it returns the sorted table:
+ expect(f(subject, cmp)).to_equal(target)
+
+
+- describe unpack:
+ - before:
+ t = {'one', 'two', 'five'}
+ f = M.unpack
+ - it returns nil for an empty table:
+ expect(f {}).to_be(nil)
+ - it returns numeric indexed table elements:
+ expect({f(t)}).to_equal(t)
+ - it respects __len metamethod:
+ function two(t)
+ return setmetatable(t, {__len=function() return 2 end})
+ end
+ expect(pack(f(two {})).n).to_be(2)
+ expect(pack(f(two(t))).n).to_be(2)
+ - it returns holes in numeric indices as nil:
+ expect({f {nil, 2}}).to_equal {[2] = 2}
+ expect({f {nil, nil, 3}}).to_equal {[3] = 3}
+ expect({f {1, nil, nil, 4}}).to_equal {1, [4] = 4}
+ - it is the inverse operation to pack:
+ expect({f(M.pack('one', 'two', 'five'))}).to_equal(t)
+
+
+- describe values:
+ - before:
+ subject = {k1={1}, k2={2}, k3={3}}
+
+ f = M.values
+
+ - context with bad arguments:
+ badargs.diagnose(f, 'std.table.values(table)')
+
+ - it returns an empty list when subject is empty:
+ expect(f {}).to_equal {}
+ - it makes a list of table values:
+ cmp = function(a, b) return a[1] < b[1] end
+ expect(M.sort(f(subject), cmp)).to_equal {{1}, {2}, {3}}
+ - it does guarantee stable ordering:
+ subject = {}
+ -- is this a good test? or just requiring an implementation quirk?
+ for i = 10000, 1, -1 do
+ table.insert(subject, i)
+ end
+ expect(f(subject)).to_equal(subject)
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/stdlib-git-1.rockspec b/Data/BuiltIn/Libraries/lua-stdlib/stdlib-git-1.rockspec
new file mode 100644
index 0000000..504aee8
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/stdlib-git-1.rockspec
@@ -0,0 +1,51 @@
+local _MODREV, _SPECREV = 'git', '-1'
+
+package = 'stdlib'
+version = _MODREV .. _SPECREV
+
+description = {
+ summary = 'General Lua Libraries',
+ detailed = [[
+ stdlib is a library of modules for common programming tasks,
+ including list and table operations, and pretty-printing.
+ ]],
+ homepage = 'http://lua-stdlib.github.io/lua-stdlib',
+ license = 'MIT/X11',
+}
+
+source = (function(gitp)
+ if gitp then
+ return {
+ url = 'git://github.com/lua-stdlib/lua-stdlib.git',
+ }
+ else
+ return {
+ url = 'http://github.com/lua-stdlib/lua-stdlib/archive/v' .. _MODREV .. '.zip',
+ dir = 'lua-stdlib-' .. _MODREV,
+ }
+ end
+end)(_MODREV == 'git')
+
+dependencies = {
+ 'lua >= 5.1, < 5.4',
+ 'std._debug',
+ 'std.normalize >= 2.0',
+}
+
+if _MODREV == 'git' then
+ dependencies[#dependencies + 1] = 'ldoc'
+end
+
+build = {
+ type = 'builtin',
+ modules = {
+ std = 'lib/std/init.lua',
+ ['std._base'] = 'lib/std/_base.lua',
+ ['std.debug'] = 'lib/std/debug.lua',
+ ['std.io'] = 'lib/std/io.lua',
+ ['std.math'] = 'lib/std/math.lua',
+ ['std.package'] = 'lib/std/package.lua',
+ ['std.string'] = 'lib/std/string.lua',
+ ['std.table'] = 'lib/std/table.lua',
+ },
+}
diff --git a/Data/BuiltIn/Libraries/lua-stdlib/string.lua b/Data/BuiltIn/Libraries/lua-stdlib/string.lua
new file mode 100644
index 0000000..6ad9014
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/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/table.lua b/Data/BuiltIn/Libraries/lua-stdlib/table.lua
new file mode 100644
index 0000000..7bda608
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-stdlib/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)
diff --git a/Data/Libraries/GameLab/Editor/Window/SplitWindow.lua b/Data/Libraries/GameLab/Editor/Window/SplitWindow.lua
index 1419d34..acdaa9d 100644
--- a/Data/Libraries/GameLab/Editor/Window/SplitWindow.lua
+++ b/Data/Libraries/GameLab/Editor/Window/SplitWindow.lua
@@ -15,7 +15,8 @@ Splitter.Ctor = function(self, value)
self.value = value -- [0-1] 位置
end
-local ESplitMode = GameLab.GlobalEnum("GameLab.Editor.Window.ESplitMode", {
+local ESplitMode = GameLab.GlobalEnum( "GameLab.Editor.Window.ESplitMode",
+{
"Horizontal", -- 水平划分
"Vertical", -- 垂直划分
})
diff --git a/Data/Scripts/EditorApplication.lua b/Data/Scripts/EditorApplication.lua
index 5614e2a..76c4f3d 100644
--- a/Data/Scripts/EditorApplication.lua
+++ b/Data/Scripts/EditorApplication.lua
@@ -11,6 +11,7 @@ local Debug = GameLab.Debug
local Window = GameLab.Editor.Window
local GL = GameLab.Engine.GL
local delegate = GameLab.Delegate
+local tuple = GameLab.Containers.Tuple
local app = GameLab.Editor.EditorApplication.New()
@@ -65,6 +66,8 @@ _G["default_font"] = font
local v = GameLab.Engine.Math.Vector2(10.002, 2.334)
+local t = tuple(1,2,3)
+
while true do
app:OnStep()
diff --git a/Data/boot.lua b/Data/boot.lua
index 566468a..ff8e93f 100644
--- a/Data/boot.lua
+++ b/Data/boot.lua
@@ -24,6 +24,7 @@ end
-- load gamelab modules
require "GameLab"
+require "GameLab.Containers"
require "GameLab.Utils"
require "GameLab.Events"
require "GameLab.Engine"