diff --git a/.autom4te.cfg b/.autom4te.cfg new file mode 100644 index 0000000..f47eaee --- /dev/null +++ b/.autom4te.cfg @@ -0,0 +1,4 @@ +# Disable the autom4te.cache directory. +begin-language: "Autoconf-without-aclocal-m4" +args: --no-cache +end-language: "Autoconf-without-aclocal-m4" diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 8dfc7ca..0000000 --- a/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -/COPYING -/ChangeLog -/Makefile.in -/Makefile -/aclocal.m4 -/autom4te.cache -/build-aux -/configure -/config.log -/config.status -/luarocks -/luarocks-config.lua -/release-notes-* -/stdlib-*.tar.gz -/*.rockspec diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..e65925a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,157 @@ +language: c + +env: + global: + - _COMPILE="libtool --mode=compile --tag=CC gcc" + - _CFLAGS="-O2 -Wall -DLUA_COMPAT_ALL -DLUA_COMPAT_5_2 -DLUA_USE_LINUX" + - _INSTALL="libtool --mode=install install -p" + - _LINK="libtool --mode=link --tag=CC gcc" + - _LIBS="-lm -Wl,-E -ldl -lreadline" + + - prefix=/usr/local + - bindir=$prefix/bin + - incdir=$prefix/include + - libdir=$prefix/lib + + - _inst=$TRAVIS_BUILD_DIR/_inst + - luadir=$_inst/share/lua + - luaexecdir=$_inst/lib/lua + matrix: + - LUA=lua5.3 + - LUA=lua5.2 + - LUA=lua5.1 + - LUA=luajit + + +before_install: + # Put back the links for libyaml, which are missing on recent Travis VMs + - test -f /usr/lib/libyaml.so || + sudo find /usr/lib -name 'libyaml*' -exec ln -s {} /usr/lib \; + - sudo apt-get install help2man + + # Fetch Lua sources. + - cd $TRAVIS_BUILD_DIR + - 'if test lua5.3 = "$LUA"; then + curl http://www.lua.org/ftp/lua-5.3.0.tar.gz | tar xz; + cd lua-5.3.0; + fi' + - 'if test lua5.2 = "$LUA"; then + curl http://www.lua.org/ftp/lua-5.2.4.tar.gz | tar xz; + cd lua-5.2.4; + fi' + - 'if test lua5.1 = "$LUA"; then + curl http://www.lua.org/ftp/lua-5.1.5.tar.gz | tar xz; + cd lua-5.1.5; + fi' + + # Unpack, compile and install Lua. + - 'if test luajit = "$LUA"; then + curl http://luajit.org/download/LuaJIT-2.0.3.tar.gz | tar xz; + cd LuaJIT-2.0.3; + make && sudo make install; + for header in lua.h luaconf.h lualib.h lauxlib.h luajit.h lua.hpp; do + if test -f /usr/local/include/luajit-2.0/$header; then + sudo ln -s /usr/local/include/luajit-2.0/$header /usr/local/include/$header; + fi; + done; + else + for src in src/*.c; do + test src/lua.c = "$src" || test src/luac.c = "$src" || eval $_COMPILE $_CFLAGS -c $src; + done; + eval $_LINK -o lib$LUA.la -version-info 0:0:0 -rpath $libdir *.lo; + sudo mkdir -p $libdir; + eval sudo $_INSTALL lib$LUA.la $libdir/lib$LUA.la; + + eval $_COMPILE $_CFLAGS -c src/lua.c; + eval $_LINK -static -o $LUA lua.lo lib$LUA.la $_LIBS; + sudo mkdir -p $bindir; + eval sudo $_INSTALL $LUA $bindir/$LUA; + + sudo mkdir -p $incdir; + for header in lua.h luaconf.h lualib.h lauxlib.h lua.hpp; do + if test -f src/$header; then + eval sudo $_INSTALL src/$header $incdir/$header; + fi; + done; + fi' + + # Fetch LuaRocks. + - cd $TRAVIS_BUILD_DIR + - 'git clone https://github.com/keplerproject/luarocks.git luarocks-2.2.0' + - cd luarocks-2.2.0 + - git checkout v2.2.0 + + # Compile and install luarocks. + - if test luajit = "$LUA"; then + ./configure --lua-suffix=jit; + else + ./configure; + fi + - 'make build && sudo make install' + + # Tidy up file droppings. + - cd $TRAVIS_BUILD_DIR + - rm -rf lua-5.3.0 lua-5.2.3 lua-5.1.5 LuaJIT-2.0.3 luarocks-2.2.0 + + +install: + # Use Lua 5.3 compatible rocks, where available. + - 'for rock in ansicolors ldoc specl""; do + if test -z "$rock"; then break; fi; + if luarocks list | grep "^$rock$" >/dev/null; then continue; fi; + sudo luarocks install --server=http://rocks.moonscript.org/manifests/gvvaughan $rock; + done' + + # Fudge timestamps on release branches. + - 'if test -f configure; then + test -f aclocal.m4 && touch aclocal.m4; + sleep 1; touch Makefile.in; + sleep 1; test -f config.h.in && touch config.h.in; + sleep 1; touch configure; + fi' + + # Build from rockspec, forcing uninstall of older luarocks installed + # above when testing the git rockspec, both for enforcing backwards + # compatibility by default, and for ease of maintenance. + - if test -f 'stdlib-41.2.2-1.rockspec'; then + sudo luarocks make 'stdlib-41.2.2-1.rockspec' LUA="$LUA"; + else + sudo luarocks make --force 'stdlib-git-1.rockspec' LUA="$LUA"; + fi + + # Clean up files created by root + - sudo git clean -dfx + - sudo rm -rf slingshot /tmp/ldoc + + +script: + # Reconfigure for in-tree test install. + - test -f configure || ./bootstrap --verbose + - ./configure --prefix="$_inst" --disable-silent-rules LUA="$LUA" + + # Verify luarocks installation. + - make installcheck || make installcheck V=1 + + # Verify local build. + - make + - make check || make check V=1 + + # Verify configured installation. + - make install prefix="$_inst" luadir="$luadir" luaexecdir="$luaexecdir" + - LUA_PATH="$luadir/?.lua;$luadir/?/init.lua;;" + LUA_CPATH="$luaexecdir/?.so;;" + make installcheck V=1 + + +# Run sanity checks on CI server, ignoring buggy automakes. +after_success: + - '{ _assign="="; + if grep local-checks-to-skip build-aux/sanity-cfg.mk >/dev/null; then + _assign="+="; + fi; + printf "local-checks-to-skip %s sc_vulnerable_makefile_CVE-2012-3386\n" "$_assign"; + } >> build-aux/sanity-cfg.mk' + - 'make syntax-check || : this will usually fail on the release branch' + +notifications: + slack: aspirinc:JyWeNrIdS0J5nf2Pn2BS1cih diff --git a/AUTHORS b/AUTHORS index 0cbc134..9a36600 100644 --- a/AUTHORS +++ b/AUTHORS @@ -6,9 +6,13 @@ be on it, please tell the mailing list (see README for the address). 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 and maintains the standard libraries project, -wrote many of the libraries, and integrated code from other authors. +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. @@ -21,8 +25,3 @@ private standard library. Johann Hibschman supplied the code on which math.floor and math.round were based. - -Diego Nehab wrote the original version of the mbox parser module. - -Gary V. Vaughan contributed table key support to the tree module, and -the memoize implementation. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..ec03e04 --- /dev/null +++ b/COPYING @@ -0,0 +1,27 @@ +This software comprises files that are copyright their respective +authors (see the AUTHORS file for details), and distributed under +the terms of the MIT license (the same license as Lua itself), +unless noted otherwise in the body of that file. + +==================================================================== +Copyright (C) 2002-2015 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/ChangeLog b/ChangeLog new file mode 100644 index 0000000..d56eb2e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,7370 @@ +2015-03-08 Gary V. Vaughan + + Release version 41.2.0 + * NEWS.md: Record release date. + + configury: bump release revision to 41.2.0. + * configure.ac (AC_INIT): Bump release revision to 41.2.0. + * .travis.yml: Regenerate. + + io: don't leak extra results from implementation details. + * lib/std/io.lua (dirname): Return parenthesised results from + internal gsub calls to prevent unexpected additional values + leaking. + * NEWS.md (Bug fixes): Update. + + doc: don't tell LDoc that resulterror is called argerror! + + debug: deprecate `toomanyargmsg` for `extramsg_toomany`. + * specs/debug_spec.yaml (extramsg_toomany): Add examples for + correct behaviours of new api. + * lib/std/debug.lua (toomanyargmsg): Deprecate. + (extramsg_toomany): New function, satisfies specified behaviours. + * lib/std/container.lua: Use extramsg_toomany api. + * NEWS.md (New features, Deprecations): Update. + + debug: refactor and export debug.resulterror. + * specs/debug_spec.yaml (resulterror): Add examples of behaviours + for resulterror. + * lib/std/base.lua (raise): New function factored out of... + (argerror): ...this. Adjust accordingly. + * lib/std/debug.lua (resulterror): Rewrite over base.raise. + Move out of _DEBUG.argcheck guard. + Export as resulterror. + * NEWS.md (New features): Update. + + debug: export formaterror as extramsg_mismatch. + * specs/debug_spec.yaml (extramsg_mismatch): Add examples for + correct behaviours of extramsg_mismatch. + * lib/std/debug.lua (formaterror): Rename from this... + (extramsg_mismatch): ...to this. Move out of _DEBUG.argcheck + guard. + (prototype, concat): Move above position of first call in file. + * NEWS.md (New features): Update. + + debug: accept "bool" as a typename alias for "boolean". + * specs/debug_spec.yaml: Add examples for behaviour when given + "bool" in lieu of "boolean". + * lib/std/debug.lua (formaterror, checktype): Accept "bool" as an + alias for "boolean" in a type specification. + * NEWS.md (Bug fixes): Update. + + debug: skip `self` typecheck when fname contains a colon. + * specs/debug_spec.yaml (argscheck): Specify behaviours for colon + delimited function name. + * lib/std/debug.lua (argscheck): Drop `self` argument before + checking types of remaining arguments when the function name + contains a colon. + * NEWS.md (New features): Update. + + debug: export a new function to parse type table. + Close #91 + * lib/std/debug.lua (normalize): Rename from this... + (typesplit): ...to this. Move to outer scope, so it will be + visible even if _DEBUG.argcheck is false. + ((markdots, permute, projectuniq): Move to outer scope. + (parsetypes): New function. + (parsetypes, typesplit): Export as public APIs. + * specs/debug_spec.yaml (extendbase): Add parsetypes and + typesplit. + + debug: improve argscheck patterns. + * lib/std/debug.lua (args_pat, argscheck): Improve patterns. + + slingshot: sync with upstream, for git rockspec fixes. + * slingshot: Sync with upstream. + * stdlib-git-1.rockspec: Regenerate. + + debug: maintain stack frame depth parity with LuaJIT. + * lib/std/debug.lua (getfenv): Be careful not to let the LuaJIT + optimizer eliminate a stack frame, and mess up the depth count. + + slingshot: sync with upstream, for Lua 5.2.4 update. + * slingshot: Sync with upstream. + * .travis.yml: Regenerate. + + debug: adjust numeric args for getfenv wrapper for Lua 5.1. + * lib/std/debug.lua (getfenv): When calling core getfenv from + our wrapper, make sure to add one to numeric args to compensate + for the extra stack frame. + +2015-02-27 Gary V. Vaughan + + debug: export portable getfenv and setfenv implementations. + * specs/debug_spec.yaml (getfenv, setfenv): Add examples of + behaviour of these functions. + * lib/std/debug.lua (getfenv): Fix a bug that prevented + unwrapping of functables on Lua 5.1 and LuaJIT. + (getfenv, setfenv): Export as public APIs. + * NEWS.md (New features): Update. + + functional: callable returns falsey for nil argument. + * specs/functional_spec.yaml (callable): Specify correct behaviour + for uncallable argument. + * lib/std/functional.lua (callable): Don't raise an argument error + for a nil argument, just return nil like any other uncallable. + * NEWS.md (Bugs fixed): Update. + + functional: add examples to show reduce handling nil arguments. + * specs/functional_spec.yaml (reduce): Add examples to show + behaviours with nil arguments. + +2015-02-27 Reuben Thomas + + std/io.lua: adjust catdir's return to 1 value + +2015-02-26 Gary V. Vaughan + + functional: ensure map propagates nil arguments. + * specs/functional_spec.yaml (map): Add examples to show + behaviours with nil arguments. + * lib/std/functional.lua (map): Detect iterator arity when + collecting result table. + * NEWS.md (Bug fixes): Update. + + functional: ensure filter propagates nil arguments. + * specs/functional_spec.yaml (filter): Add example to show + behaviour with nil arguments. + * lib/std/functional.lua (filter): Detect iterator arity when + collecting result table. + * NEWS.md (Bug fixes): Update. + + functional: ensure collect propagates nil arguments. + * specs/functional_spec.yaml (collect): Add examples to show + behaviours with nil arguments. + * lib/std/functional.lua (collect): Use npairs as a default + iterator. + Detect iterator arity when collecting result table. + * NEWS.md (Bug fixes): Update. + (Incompatible changes): Note change of default iterator. + + functional: ensure compose propagates nil arguments. + * specs/functional_spec.yaml (compose): Add examples to show + behaviours with nil arguments. + * lib/std/functional.lua (compose): Simplify. + * NEWS.md (Bug fixes): Update. + + functional: ensure bind propagates nil arguments. + * specs/functional_spec.yaml (bind): Add example to show + behaviours with nil arguments. + * lib/std/functional.lua (bind): Simplify. + * NEWS.md (Bug fixes): Update. + + std: new npairs and rnpairs iterators. + * specs/std_spec.yaml (npairs, rnpairs): Specify correct + behaviour for these new iterators. + * lib/std/debug.lua (argpairs): Move from here... + * lib/std/base.lua (npairs): ...to here. + (rnpairs): New function. + * lib/std.lua.in (npairs, rnpairs): Reexport from here. + * NEWS.md (New features): Update. + + std: fix argscheck declaration for std.getmetamethod. + * lib/std.lua.in (getmetamethod): This function works on anything + the Lua can add a metatable to. Adjust LDocs and argscheck + declaration to support that. + * specs/std_spec.yaml (getmetamethod): Adjust badargs parameters + accordingly. + * NEWS.md (Bug fixes): Update. + +2015-02-26 Gary V. Vaughan + + Merge pull request #96 from lua-stdlib/fix-require-version + base: require does not work with non-string module version fields + +2015-02-26 Gary V. Vaughan + + maint: fix .gitignore problems. + Close #97. + * .gitignore: Some versions of git disallow !re-include inside a + directory that has been excluded. Add a trailing '/*' to support + those versions. + (/build-aux/config.ld.in): Re-include this source file. + (/doc/config.ld): Remove exclusion of no longer generated file, + now that config.ld is also kept in build-aux. + Reported by Reuben Thomas + +2015-02-25 Reuben Thomas + + base: require does not work with non-string module version fields + Simple fix: run “tostring” on the version field. + + It may be desired to refuse certain types. The motivating case here is + that std.optparse has version set to number 0. + +2015-02-06 Gary V. Vaughan + + list: std.ireverse is not a full replacement for list.reverse. + * lib/std/list.lua (reverse): Describe a full replacement + with functional.compose in deprecation warning message. + Reported by Reuben Thomas + +2015-01-31 Gary V. Vaughan + + maint: post-release administrivia. + * NEWS: Add header line for next release. + * .prev-version: Record previous version. + * ./local.mk (old_NEWS_hash): Auto-update. + + Release version 41.1.1 + * NEWS.md: Record release date. + + configury: bump release version to 41.1.1. + * configure.ac (AC_INIT): Bump release version to 41.1.1. + * .travis.yml: Regenerate. + + std: fix an infinite loop in std.barrel() with Lua 5.3. + Close #94 + * lib/std/base.lua (maxn): This function is called from any + function wrapped by debug.argscheck, which calls debug.diagnose. + Calling std.barrel monkey-patches the global pairs function, + so we can't call that from here, otherwise it will in turn use + the monkey-patched pairs, which calls debug.diagnose to check + for argument errors before the original maxn call has finished. + Reported by Reuben Thomas + +2015-01-30 Gary V. Vaughan + + doc: add missing parameter LDoc for debug:markdots. + * lib/std/debug.lua (markdots): Add missing LDocs for v parameter. + + maint: post-release administrivia. + * NEWS: Add header line for next release. + * .prev-version: Record previous version. + * ./local.mk (old_NEWS_hash): Auto-update. + + Release version 41.1.0 + * NEWS.md: Record release date. + + package: normalize leaves valid /../.. sequences unmolested. + * specs/package_spec.yaml (normalize): Add examples of correct + behaviour with multiple .. directories. + * lib/std/package.lua (normalize): Detect redundant .. components + correctly, and only remove them when it's safe to do so. + * NEWS.md (Bug fixes): Update. + + slingshot: sync with upstream. + * slingshot: Sync with upstream. + * .travis.yml: Regenerate. + + configury: bump release version to 41.1.0. + * configure.ac (AC_INIT): Bump release version to 41.1.0. + + table: new unpack method that behaves consistently across Lua versions. + * specs/table_spec.yaml (unpack): Specify behaviours for unpack, + especially when dealing with holes. + * lib/std/base.lua (unpack): Wrapper for the core function, but + defaulting the final index argument to table.maxn. + * lib/std/table.lua (unpack): Export from here. Add LDocs. + * lib/std/debug.lua, lib/std/functional.lua, lib/std/list.lua: Use + safer base.unpack. + * specs/spec_helper.lua (maxn, unpack): Make safe versions + available to spec examples. + * specs/debug_spec.yaml: Simplify id implementation. + * NEWS.md (New features): Update. + + debug: unpack to maxn (t) for equivalence between Lua versions. + luajit alone stops unpacking at the first nil valued index, so + we have to pass explicit limits to make it behave like Lua 5.1 + through 5.3. + * lib/std/debug.lua (argscheck): unpack from 1 to maxn (results). + * specs/debug_spec.yaml (argscheck): Adjust id implementation to + calculate maximum numeric index and unpack all results up to + that value. + + debug: support variant return type lists with argscheck. + Sometimes we need to say `returns an int or nil,errmsg', the + syntax is `=> int or nil, string`, because `=> ?int, string` will + wrongly accept, say, `3, "oh noes!"`. + * specs/debug_spec.yaml (argscheck): Specify behaviours with + variant return type lists. + * lib/std/debug.lua (argscheck): Parse all variants and add them + to the accepted type list permutations table. + Update LDocs. + * NEWS.md (New features): Update. + +2015-01-29 Gary V. Vaughan + + refactor: store parameter dots property against each permuted list. + Rather than deciding whether a typelist has continuation dots in + the last position once for the entire table of permuted type match + lists, add it to each permutation with continuation dots. + * lib/std/debug.lua (markdots): Simplify. + (permute): Call it for each new permutation row. + (projectuniq): New function to select and de-deduplicate all + values from the table of permuted type lists at a given index. + (diagnose): Rewrite to work with new format permutations. + (argscheck): Simplify accordingly. + (match): Remove allargs parameter, and always check all arguments. + (merge, HAS_DOTS): Remove. No longer used. + +2015-01-28 Gary V. Vaughan + + refactor: clean up maxvalues calculation in debug.argscheck. + * lib/std/debug.lua (stripellipsis): Remove. + (markdots): New function encapsulating stripellipsis, maxvalue + calculation and optional normalization. + (argscheck): Simplify accordingly. + + debug: argscheck supports return type checking. + Close #89 + * specs/spec_helper.lua (badargs.result): Fallback implementation + until next Specl release. + (init): Use it to return a `badresult` function. + * specs/debug_spec.yaml: Specifiy behaviours with return type + checking. + * lib/std/debug.lua (toomanyargmsg): Factor this... + (toomanymsg): ...into this more general function. + (M): Adjust accordingly. + (resulterror): New function modelled after argerror, but tailored + for result type mismatch error reporting. + (has_dots): Symbol to name use of math.huge for denoting an + ellipsis was stripped from the type list. + (stripellipsis): New function. + (diagnose): Refactored to take a vtable of arguments instead of an + ever longer list of parameters. + (argscheck): Build a vtable to call diagnose for argument type + checking. + Add another diagnose() with a separate vtable for result type + checking. + Update LDocs. + * NEWS.md (New features): Update. + +2015-01-27 Gary V. Vaughan + + refactor: separate argscheck inner wrapper into its own function. + * lib/std/debug.lua (empty): New function. + (diagnose): New function... + (argscheck): ...factored out of here. + +2015-01-26 Gary V. Vaughan + + tree: don't argcheck metamethods. + Lua will not call metamethods unless the metatables are compatible, + so don't waste time rechecking the argument types. + * lib/std/tree.lua (__index, __newindex): Remove superfluous + argscheck invocations. + + set: don't argcheck metamethods. + Lua will not call metamethods unless the metatables are compatible, + so don't waste time rechecking the argument types. + * lib/std/set.lua (__add, __sub, __mul, __div, __le, __lt) + (__tostring): Remove superfluous argscheck invocations. + + debug: more accurate too many arguments diagnostics. + Close #76 + * specs/debug_spec.yaml (argscheck): Specify behaviour when + arguments match by skipping bracketed optional parameters, but + for one unmatched argument at the end. + * lib/std/debug.lua (permutations): Rename function from this + noun... + (permute): ...to this verb. + (argscheck): Use the matching permutation to decide whether + too many arguments were passed, unless the last parameter has + an ellipsis denoting any number of matching arguments are allowed. + * NEWS.md (Bug fixes): Update. + + debug: process [final|parm|types] without nil substitution hack. + * specs/debug_spec.yaml (argscheck): Specify behaviours for + bracketed final parameter more thoroughly. + * lib/std/debug.lua (argscheck): Remove hack of replacing + brackets in bracketed last parameter by 'or nil'; we really + run permutations without a bracketed final parameter now. + (permutations): Simplify accordingly; we don't need all the + sentinel gunk to track final argument nils now. + (normalize): Simplify, and fix a related bug where nils could + get dropped from the type list by a double increment. + * lib/std/functional.lua (bind): This really is a '?any...', + requiring explicit nils, and not a [any...] optional argument. + * specs/debug_spec.yaml: Adjust badargs calls as necessary. + * specs/io_spec.yaml (writelines): Now that this implementation + is a step ahead of the current Specl release generator, manually + write out bad argument examples. + * NEWS.md: Update. + + debug: argscheck accepts bracketed final parameter. + * specs/debug_spec.yaml (argscheck): Add examples for bracketed + final parameters. + * lib/std/debug.lua (argscheck): Handle bracketed final parameter + and satisfy new specs. Adjust all callers accordingly. + * NEWS.md (New features): Update. + + debug: use trailing `...` instead of `*` with argscheck. + * specs/debug_spec.yaml (argscheck): Add specifications for + continuation argument behavious with `...`. + * lib/std/debug.lua (argscheck): Use `...` instead of `*` as the + syntax for optional repeats of the final parameter type. Adjust + all callers. + * NEWS.md (Incompatible changes): Update. + + refactor: change `types` symbol name to `argtypes`. + * lib/std/debug.lua (argscheck): Change `types` symbol name to + `argtypes`. + +2015-01-25 Gary V. Vaughan + + debug: argcheck requires leading ? for argtypes, to match specl. + Close #90 + * specs/debug_spec.yaml (argcheck): Change to leading `?` over + previous trailing `?` style. + * lib/std.lua.in, lib/std/container.lua, lib/std/debug.lua, + lib/std/functional.lua, lib/std/io.lua, lib/std/list.lua, + lib/std/math.lua, lib/std/package.lua, lib/std/string.lua, + lib/std/table.lua, lib/std/tree.lua: Adjust accordingly. + + strbuf: document and test functional copying style. + Close #85 + * lib/std/strbuf.lua: Improve LDocs. + * specs/strbuf_spec.yaml: Add example of functional copying. + + strbuf: support lazy stringification and concat anything. + * specs/strbuf_spec.yaml: Specify correct behaviours, and add + lazy stringification example. + * lib/std/strbuf.lua (__concat): Simply insert a reference. + (__tostring): Make a table of stringified elements, and concat + those instead of the object elements proper. + * NEWS.md: Update. + + strbuf: allow concatenation of StrBuf objects. + * specs/strbuf_spec.yaml: Specify new behaviours. + * lib/std/strbuf.lua (concat): New function, handles concatenation + of StrBuf objects as well as strings. + * NEWS.md: Update. + + strbuf: deprecate strbuf.tostring. + * specs/strbuf_spec.yaml: Specify deprecation messages from + strbuf.tostring. + * lib/std/strbuf.lua (StrBuf.tostring): Deprecate. + * NEWS.md: Update. + + refactor: rearrange strbuf.lua idiomatically. + * lib/std/strbuf.lua: Rearrange declarations idiomatically. + + maint: support strict mode. + Close #92 + * lib/std/base.lua (loadstring): Use rawget to fetch loadstring + from _G, to bypass strict mode checks on Lua > 5.2 where + _G.loadstring is nil. + * lib/std/debug.lua (getfenv): Likewise, for Lua > 5.2 where + there is no _G.fgetenv. + * lib/std/debug_init/init.lua (_DEBUG): Likewise, for cases where + caller did not preload _G._DEBUG. + * NEWS.md: Updated. + Reported by Gergely Risko + +2015-01-19 Gary V. Vaughan + + std: barrel and monkey_patch return module table. + Closes #93 + In order for things like `std = require "std".barrel ()` to work + as documented, these methods must return their parent module + table, i.e. in this case, the `std` table. + * specs/io_spec.yaml (monkey_patch): Specify correct behaviour. + * specs/math_spec.yaml (monkey_patch): Likewise. + * specs/string_spec.yaml (monkey_patch): Likewise. + * specs/table_spec.yaml (money_patch): Likewise. + * specs/std_spec.yaml (barrel, monkey_patch): Likewise. + * lib/std.lua.in (barrel, monkey_patch): Return parent module + able. + * lib/std/io.lua, lib/std/math.lua, lib/std/string.lua, + lib/std/table.lua (monkey_patch): Likewise. + * NEWS.md (Bug fixes): Update. + Reported by Reuben Thomas + +2015-01-14 Gary V. Vaughan + + slingshot: sync with upstream for working Lua 5.3.0 final support. + * slingshot: Sync with upstream. + * .travis.yml: Regenerate. + + slingshot: sync with upstream for Lua 5.3.0 final support. + * slingshot: Sync with upstream. + * .travis.yml: Regenerate. + +2015-01-14 Gergely Risko + + base: remove spurious case reference from base module. + * lib/std/base.lua: Remove spurious reference to case from + export table. That function has long since moved to std.functional. + +2015-01-03 Gary V. Vaughan + + maint: post-release administrivia. + * NEWS: Add header line for next release. + * .prev-version: Record previous version. + * ./local.mk (old_NEWS_hash): Auto-update. + + Release version 41.0.0 + * NEWS.md: Record release date. + + maint: don't use math.pow, which some Lua 5.3 builds don't support. + When compiled without -DLUA_COMPAT_5_2, Lua 5.3 removes a good + chunk of the math library, including math.pow. + * lib/std.lua.in (eval): Use math.min in LDocs usage. + * specs/functional_spec.yaml (bind, curry, eval): Instead of + math.pow, use math.min where sensible, otherwise std.operator.pow. + * specs/operator_spec.yaml (pow): Compare with result calculated + using ^ infix operator. + * specs/std_spec.yaml (eval): Use math.min over math.pow. + + std: ireverse only operates on the proper sequence part. + * lib/std/base.lua (ireverse): Ignore any holes in the argument + table, returning only the reversed proper sequence part. + * NEWS.md: Update. + + std: make ripairs respect contiguous integer keys like ipairs. + * lib/std/base.lua (ripairs): Start from lowest contiguous integer + key with a nil value, and iterate backwards to 1. + * NEWS.md: Update. + + std: revert std.ipairs to return all contiguous key pairs 1..n. + Now that Lua 5.3 returned to an ipairs implementation that returns + contiguous key-value pairs from 1..n, where n is the last non-nil + integer key, stdlib can do the same for simpler compliance with + all supported Lua versions. + * lib/std/base.lua (ipairs): Simplify accordingly. + * lib/std.lua.in (ipairs): Update LDocs accordingly. + * NEWS.md: Update. + + maint: commit git rockspec to master branch. + * .gitignore: Allow git rockspecs. + * stdlib-git-1.rockspec: New file. + + configury: add ansicolors to travis_extra_rocks. + * bootstrap.conf (buildreq): Bump Specl requirement to 14.1.0. + (travis_extra_rocks): Add ansicolors for color specl output. + * .travis.yml: Regenerate. + + maint: add 5.3 to compatibility statement. + * README.md: Add 5.3 to compatibility statement. + * specs/std_spec.yaml (std.version): Update accordingly. + + maint: update copyrights. + * COPYING, README.md, bootstrap.conf, configure.ac, local.mk, + specs/optparse_spec.yaml, specs/string_spec.yaml: Add 2015 to + copyright statement. + + rockspec: Lua 5.4 and higher not yet supported. + * rockspec.conf (dependencies): Add lua < 5.4 constraint. + + io: pass io.die message as an error() string. + * lib/std/io.lua (warnfmt): New function factored out of warn(). + Adjust callers. + (die): Pass result of warnfmt to error instead of writing directly + to stderr. + * specs/io_spec.yaml, specs/optparse_spec.yaml: Adjust + specifications accordingly. + * NEWS.md: Update. + + specs: support Lua 5.3 variants of core error messages. + * specs/container_spec.yaml, specs/debug_spec.ymal: Also accept + slightly different error message formats from Lua 5.3 core. + + slingshot: sync with upstream, for Lua 5.3.0 compatibility. + * slingshot: Sync with upstream. + * bootstrap: Update from slingshot. + * NEWS: Move from here... + * NEWS.md: ...to here. Reformat as Markdown and update. + * local.mk (old_NEWS_hash): Update. + * .gitignore: Add NEWS. + * .travis.yml: Regenerate. + +2014-12-29 Gary V. Vaughan + + string: consistent numbertosi output in Lua 5.3. + * lib/std/string.lua (numbertosi): Be careful to output an + integer even when handling a number ('double') in Lua 5.3. + + maint: 5.3 uses load instead of loadstring. + * lib/std/base.lua (loadstring): Set to load if loadstring is not + available. + * lib/std/functional.lua (loadstring): Likewise. + + maint: preliminary Lua 5.3.0 compatibility. + * configure.ac (AX_PROG_LUA): Accept Lua 5.3 interpreters. + * lib/std/io.lua (setmetatable): Define locally to + debug.setmetatable for resetting FILE* metatable in given + namespace. + * lib/std/base.lua, lib/std/debug.lua, lib/std/functional.lua, + lib/std/list.lua, lib/std/package.lua (unpack): Set to either + table.unpack or _G.unpack to satisfy Lua 5.1, 5.2 and 5.3. + * specs/spec_helper.lua (unpack): Set appropriately for all + supported Lua releases. + * NEWS: Update. + +2014-12-24 Gary V. Vaughan + + specs: use correct Specl > 13 arity for bad argument errors. + Now that Specl's badargs module is generating examples with + the correct messages, we can now say 'no more than 0 arguments'. + * lib/std/debug.lua (toomanyargmsg): Return singular 'argument' + in error message only for exactly 1 expected argument, otherwise + plural. + * bootstrap.conf (buildreq): Bump specl requirement. + + refactor: rename operator.deref to operator.get. + * specs/operator_spec.yaml (deref): Rename from this... + (get): ...to this. + * lib/std/operator.lua (deref): Rename from this... + (get): ...to this. + Adjust all callers. + * NEWS: Update. + + functional: generalize reduce to any table. + * specs/functional_spec.yaml (reduce): Specify optional iterator + argument defaulting to std.pairs. + Specify requirement for std.ielems when ignoring reduced table + keys. + * specs/operator_spec.yaml (set): Specify behaviours for a new + table element setting operator. + * specs/container_spec.yaml: Use appropriate iterator functions + for new reduce API. + * lib/std/functional.lua (reduce): Diagnose optional iterator + argument to std.pairs. + (fold): Copy old reduce implementation into deprecated call. + * lib/std.lua.in (barrel): Adjust accordingly. + * lib/std/base.lua (reduce): Default optional iterator argument + to std.pairs. + Pass all iterator results to accumulator function. + Adjust all clients. + * lib/std/operator.lua (set): Implement according to new specs. + * NEWS: Update. + + functional: improve lambda expressiveness. + * specs/functional_spec.yaml: Specify new behaviours with omitted + optional '=', and '_' alias argument. + * lib/std/functional.lua (lambda): Strip more useless whitespace + in parsing to improve memoize cache hits. + Support omitting leading '=' when first non-whitespace of lambda + string is '_'. + Add '_' alias to '_1' for lambda string compiled expressions. + Update LDocs. + + doc: show release version in LDocs column headings. + * build-aux/config.ld.in (project): Add release version. + +2014-12-21 Gary V. Vaughan + + string: don't return spurious additional values. + * lib/std/base.lua (escape_pattern): Wrap gsub return value in + parens to strip all but the first string return value. + * lib/std/string.lua (caps, chomp, escape_shell, ltrim, rtrim) + (trim): Likewise. + + io: add dirname function again. + * specs/io_spec.yaml (dirname): Specify behaviour of a new + function to discard the final path separator in a string and all + that follows. + * lib/std/io.lua (M.dirname): Implement it. + * NEWS: Update. + + vector: remove unneeded module. + On second thoughts, this is not necessary. Adding methods to core + table would give us the API benefits, and posix.curses.chstr is a + much easier way of handling arrays of attributed strings. + * lib/std/vector.lua: Remove. + * build-aux/config.ld.in (file): Remove lib/std/vector.lua. + * local.mk (dist_luastd_DATA): Likewise. + (dist_classes_DATA): Remove doc/classes/std.vector.html. + * specs/vector_spec.yaml: Remove. + * specs/string_spec.yaml (render): Remove vector examples. + * specs/specs.mk (specl_SPECS): Remove specs/vector_spec.yaml. + * NEWS: Remove vector references. + +2014-12-16 Gary V. Vaughan + + configury: adopt semantic versioning. + * configure.ac (AC_INIT): Set version to 41.0.0. + +2014-11-05 Gary V. Vaughan + + maint: fix bitrot in README. + * README.md: Update. + +2014-10-05 Gary V. Vaughan + + specs: simplify bad argument checking with specl.badargs. + * specs/spec_helper.lua (badargs): Expose specl.badargs to the + example execution environment. + (badarg, init): Remove. Superseded by badargs.init. + * specs/debug_spec.yaml, specs/string_spec.yaml: Use badargs.init + instead of init. + * specs/functional_spec.yaml, specs/io_spec.yaml, + specs/list_spec.yaml, specs/math_spec.yaml, + specs/package_spec.yaml, specs/std_spec.yaml, + specs/table_spec.yaml: Use badargs.diagnose to write bad argument + diagnostics examples automatically. + * bootstrap.conf (buildreq): Use moonscript rocks server URLs. + + travis: remove temporary specl-git-1.rockspec dependency. + * .travis.yml (script): Regenerate from travis.yml.n. + +2014-10-01 Gary V. Vaughan + + slingshot: sync with upstream for SPECL_ENV improvements. + * slingshot: Sync with upstream. + * specs/specs.mk: Simplify accordingly. + * specs/spec_helper.lua (package.path): Set according to slingshot + recommendations. + * bootstrap.conf: Require latest specl, and tidy accordingly. + + functional: fill unbound arguments in order with bind. + * lib/std/functional (bind): When filling unbound arguments, be + sure to traverse the remaining arguments *in* order! + + debug: improve argscheck parsing patterns. + * lib/std/debug.lua (argscheck): Strip leading and trailing + whitespace from argument type list. + Allow commas without trailing whitespace. + Allow periods in `fname` pattern. + + travis: reformat .travis.yml. + * .travis.yml: Reformat. + + travis: use specl-git-1.rockspec from specl git master branch. + * .travis.yml (script): Adjust specl-git-1.rockspec URL. + + refactor: rationalize deprecation interfaces. + Tracking one deprecation warning per deprecated function is + fiddly and confusing. Change the semantics to this: by + default calling deprecated API fires a warning message; turn + off the messages with `_DEBUG.deprecate = false`; elide + deprecated APIs entirely with `_DEBUG.deprecate = true`. + Easy and useful :) + * lib/std/debug_init/init.lua (_ARGCHECK): Remove. Adjust all + callers to use _DEBUG.argcheck instead. + (_DEBUG): Always a table, with fields initialised according to + global _DEBUG if necessary. Simplify all callers accordingly. + * lib/std/debug.lua (setcompat, getcompat): Remove. + (DEPRECATIONMSG): If _DEBUG.deprecate is nil, always return a + deprecation message, otherwise the empty string. + (DEPRECATED): If _DEBUG.deprecate is truthy, don't return a + function at all. + (_setdebug): New private function. A reliable way to jigger the + _DEBUG table contents, without worrying about nested specl + environments. + * specs/spec_helper.lua (setdebug): Import std.debug._setdebug + into the outermost execution environment. + * specs/debug_spec.yaml (extend_base): Add _setdebug. + Adjust other behaviour specs to match saner _DEBUG.deprecate + semantics. + * specs/functional_spec.yaml, specs/list_spec.yaml, + specs/string_spec.yaml, specs/table_spec.yaml: Adjust deprecation + warning behaviour examples to match new semantics. + * NEWS: Update. + +2014-09-04 Gary V. Vaughan + + tree: use argscheck on exported apis. + * lib/std/tree.lua: Move LDocs and add argschecks to returned + Tree object function declarations. + + set: check for strict Set type arguments. + Since we have type checking to help maintain correctness, don't + undermine ourselves and add complexity by adding type coercions + and looser type safety. When we're using Set's, passing a table + instead of a Set is probably an error, so treat it as one! + * specs/set_spec.yaml: Remove specifications for type coercions. + * lib/std/set.lua: Remove type coercions and require strict Set + arguments everywhere. + Add type checks to metamethods. + * NEWS: Update. + + strbuf: use argscheck on exported apis. + * lib/std/strbuf.lua: Move LDocs and add argschecks to returned + StrBuf object declarations. + * HACKING: Update. + + set: use argscheck on exported apis. + * lib/std/set.lua: Move LDocs and add argschecks to returned Set + object declaration. + * HACKING: Add items about best practices for `debug.argscheck`. + +2014-09-03 Gary V. Vaughan + + refactor: simplify primitive std.set functions. + * lib/std/set.lua (insert, delete): Return results of calling + rawset. + + refactor: simplify string.render and clients. + * lib/std/base.lua (render): Use `cb` suffix for callback + parameter names. + Rename j and w locals to k_ and v_, as they represent the values + of k and v resp. from the previous iteration. + Remove unrelated Haskell comments. + * lib/std/string.lua (prettytostring): Likewise. + * lib/std/container.lua (__tostring): Manually unroll render + invocation with base.tostring functions plugged in. + +2014-09-02 Gary V. Vaughan + + refactor: factor away std.container.__pairs. + Object pairs iteration shouldn't rely on slow in-order traversal, + we only care about key order when printing. + * lib/std/container.lua (__pairs): Remove. By the time this is + called, private keys are already moved into the metatable, and + the client would be using ipairs and okeys for ordered traversal. + (__tostring): Use ipairs and okeys here, because we do care about + ordering. + + std: use numeric keys first ordering when stringifying a table. + Closes #81. + * specs/table_spec.yaml (okeys): Specify behaviours of a new + ordered keys function. + * lib/std/container.lua (__pairs): Factor key sorting algorithm + from here... + * lib/std/base.lua (keysort): New function. ...to here. + (okeys): Use keysort to extract a sorted key list from a table. + * lib/std/table.lua (okeys): Re-export from here. + * lib/std/base.lua (render): Simplify accordingly, and always + sort keys nicely as a pleasant side-effect. + * NEWS: Update. + +2014-09-01 Gary V. Vaughan + + doc: Use LDoc @object type for std.vector. + Closes #65. + * lib/std/vector.lua: Improve documentation. + +2014-08-31 Gary V. Vaughan + + doc: use LDoc @object type for std.tree. + * lib/std/tree.lua: Improve documentation. + + doc: use LDoc @object type for std.strbuf. + * lib/std/strbuf.lua: Improve documentation. + + doc: use LDoc @object type for std.list + * lib/std/list.lua: Improve documentation. + + refactor: upgrade OptionParser implementation to a std.object. + * lib/std/optparse.lua (OptionParser): Reimplement as a std.object, + and simplify accordingly. + + doc: use LDoc @object type for std.set. + * lib/std/set.lua: Improve documentation. + + doc: use LDoc @object type for std.container. + * lib/std/container.lua: Improve documentation. + + doc: use LDoc @object type for std.object. + * build-aux/config.ld.in (object): New type for documenting + objects. + * lib/std/object.lua: Improve documentation. + + doc: add prototype chains to object LDocs. + * lib/std/list.lua, lib/std/set.lua, lib/std/strbuf.lua, + lib/std/tree.lua, lib/std/vector.lua: LDocument prototype chains. + + doc: simplify and clarify container and object LDocs. + * lib/std/object.lua, lib/std/container.lua: Simplify and clarify + LDocs. + + doc: fix a broken cross-reference. + * lib/std/container.lua (std.container): Now that we're not + documenting inherited methods, reference std.object.__call from + the prototype object. + * HACKING: Document rationale for slimming LDocs in this way. + + debug: argcheck accepts objects as valid table type arguments. + Closes #84. + Really, table is the base type of object (or strictly speaking, + container), and its almost always desirable to be able to pass + objects to functions that operate on tables. + * specs/debug_spec.yaml (argcheck): Specify this behaviour when + an object argument is given where a table is required. + * lib/std/debug.lua (argcheck): Accept any object argument + where a table parameter is expected. + +2014-08-30 Gary V. Vaughan + + table: deprecate totable. + Closes #74. + * specs/container_spec.yaml (tablification): Remove specs. + * specs/object_spec.yaml (copy): Use this new function instead of + `totable`. + * specs/object_spec.yaml, specs/set_spec.yaml, + specs/strbuf_spec.yaml, specs/tree_spec.yaml (__totable): Remove. + * specs/table_spec.yaml (totable): Deprecate this function. + * specs/spec_helper.lua (totable): Remove. + * lib/std/table.lua (totable): Likewise. + * lib/std/base.lua (totable): Remove implementation. + * lib/std.lua.in (barrel): Remove "table.totable". + * lib/std/container.lua (__totable): Remove. + (__pairs): Return elements in order, omitting private elements. + (__tostring): Use it to concatenate elements in order. + (__call, instantiate, mapfields): Iterate with `next` to fetch + raw elements, rather than ordered object __pairs elements. + * lib/std/object (__totable): Remove reference. + * lib/std/set.lua (__totable): Remove. + (__tostring): Fetch keys for display using std.pairs. + * lib/std/string.lua (pickle): Likewise. + * NEWS: Update. + +2014-08-29 Gary V. Vaughan + + doc: overhaul LDocs for std.table. + * lib/std/table.lua: Tidy up LDocs, and add @usage examples. + +2014-08-28 Gary V. Vaughan + + table: add remove for orthogonality with insert. + * specs/table_spec.yaml (remove): Specify expected behaviour. + * lib/std/table.lua (remove): Implement specified behaviors. + * NEWS: Update. + + refactor: modules can only require std.base and std.debug. + * lib/std/table.lua (totable): Move implementation from here... + * lib/std/base.lua (totable): ...to here. + * lib/std/string.lua: Import core implementation from std.base + instead of argcheck wrapper from std.table. + * lib/std/io.lua (dirsep, catfile): Move implementations from + here... + * lib/std/string.lua (escape_pattern): ...here... + * lib/std/table.lua (invert): ... and here... + * lib/std/base.lua (dirsep, catfile, invert): ...to here. + * lib/std/package.lua: Import core implementations from std.base + instead eof argcheck wrappers from user interface files. + * lib/std/list.lua, lib/std/tree.lua (func): Remove spurious + require. + * lib/std/set.lua, lib/std/tree.lua, lib/std/vector.lua + (container): Fold into Container prototype constructor. + * lib/std/list.lua, lib/std/strbuf.lua (object): Fold into + Object prototype constructor. + * lib/std/string.lua (strbuf): Fold into StrBuf prototype + constructor. + * lib/std/math.lua (debug): Fold into X definition. + * HACKING: Add a description of how to use `require` + idiomatically in stdlib source. + + table: ensure there is always a maxn function. + * specs/table_spec.yaml (maxn): Specify behaviour of maxn. + (len): Add missing specifications. + * specs/std_spec.yaml (barrel): Add maxn to list of monkey_patch + apis from std.table. + * lib/std/base.lua (maxn): Use core table.maxn if available, or + else define our own. + * lib/std/container.lua, lib/std/debug.lua: Use it! + * lib/std/table.lua (maxn): Export it. + * NEWS: Update. + + refactor: make all monkey_patch functions work the same. + * specs/std_spec.yaml, specs/io_spec.yaml, specs/math_spec.yaml, + specs/string_spec.yaml, specs/table_spec.yaml (monkey_patch): + Specify injection of all exported apis into the given namespace. + * lib/std/base.lua (copy): Support an optional `dest` argument. + (merge): Like copy, but don't overwrite pre-existing entries at + the same key. + * lib/std.lua.in, lib/std/io.lua, lib/std/math.lua, + lib/std/string.lua, lib/std/table.lua: Use merge and copy to + simplify tracking and injecting monkey_patches. + * specs/std_specl.yaml (barrel): Specify behaviour of running all + monkey_patch functions, and recreating the legacy global api by + additionally injecting those functions. + * lib/std.lua.in (barrel): Update to meet tighter specifications. + * HACKING: Note about global hygiene and use of monkey_patch (). + * NEWS: Update. + +2014-08-27 Gary V. Vaughan + + package: tidy up LDocs, and remove unnecessary M references. + * lib/std/package.lua (pathsub, find): Remove spurious `M.` + prefixes. + (mappath_callback): Rename from this... + (mappathcb): ...to this. + * HACKING: Add LDoc style notes. + + functional: remove std.operator expansions from lambda. + There's no good reason to clog up the lambda functable with a copy + of std.operator, when we can just pass the operators without + interposing lambda if we need to. + * lib/std/functional.lua (lambda): Remove std.operator expansions. + * NEWS: Update. + + operator: more RSI-reducing operator function name changes. + * specs/operator_spec.yaml (cons, length): Remove - just pass + `table.pack` or `table.len` instead, resp. + (["and"], ["or"], ["not"]): Rename these... + (conj, disj, neg): ...to these. + * lib/std/operator.lua: Likewise. + Give full and proper LDocs for each function. + * lib/std/functional.lua (M.op): Update deprecation redirections. + * NEWS: Update. + + refactor: reorder function definitions in base.lua. + * lib/std/base.lua: Rather than mostly random order, subject to + interdepencies, put functions in asciibetical order as far as + possible while avoiding forward declarations. + * HACKING: Update. + + table: diagnose insert out of bounds arguments on all Lua. + * specs/table_spec.yaml (insert): Adjust errors to include out of + bounds position. + * lib/std/debug.lua (argerror): Move implementation from here... + * lib/std/base.lua (argerror): ...to here. + (insert): Use it to diagnose out of bounds arguments. + * NEWS: Update. + + table: add missing specs for table.insert, and correct argtypes. + * specs/table_spec.yaml (insert): Specify behaviours. + * lib/std/table.lua (insert): Don't double import. + (M): Don't allow nil valued final argument. + + table: new insert method. + * specs/table_spec.yaml (insert, len): Specify behaviours. + * lib/std/base.lua (insert, last): New functions that + respect `__len` when calculating table length. + (len): Use callable to extract __len metamethod. + * HACKING: New file to document coding style and design choices. + * lib/std/base.lua, lib/std/container.lua, lib/std/debug.lua, + lib/std/io.lua, lib/std/list.lua, lib/std/optparse.lua, + lib/std/strbuf.lua, lib/std/string.lua, lib/std/table.lua, + lib/std/tree.lua, lib/std/vector.lua: Follow HACKING rules for + use of len and insert. + * NEWS: Update. + + refactor: assorted simplifications to std.table. + * lib/std/table.lua (merge_allfields): Use `nil` for unspecified + `map` argument, and when `nil` use a faster inner loop for + copying. + (merge_namedfields): Use `nil` for unspecified `keys` argument. + (clone): Unroll into export table. + (depair, keys): Use ipairs and dummy variable, rather than ielems. + (pack): Remove duplicate definition. + + refactor: remove arglen, duplicates table.maxn functionality. + * specs/debug_spec.yaml (arglen): Remove specifications. + * lib/std/debug.lua (arglen): Remove. + * lib/std/container.lua (M.__call), lib/std/debug.lua (match) + (argcheck): Change all callers to use table.maxn instead. + +2014-08-26 Gary V. Vaughan + + operator: use non-RSI inducing operator function names. + * lib/std/operator.lua ([".."], ["[]"], ["{}"], ["#"], ["+"]) + (["-"], ["*"], ["/"], ["%"], ["^"], ["=="], ["~="], ["<"], ["<="]) + ([">"], [">="]): Rename from these... + (concat, deref, cons, length, sum, diff, prod, quot, mod, pow, eq) + (neq, lt, lte, gt, gte): ...to these. + (['""']): Remove. Just pass the tostring function. + (["~"]): Remove. Just pass string.find. + Adjust all callers. + * NEWS: Update. + + functional: deprecate functional.op properly. + * specs/functional_spec.yaml (op): Specify deprecation warnings + when using old functional.op API. + * lib/std/functional.lua (M.op): Deprecate old APIs. + * NEWS: Update. + + refactor: modernize std/debug.lua. + * lib/std/debug.lua: Reorder declarations and LDocs to match + latest style. + + std: commit missed change to lib/std.lua.in. + * lib/std.lua.in (X): Update export call to argscheck. + + doc: improve LDoc usage examples in std.debug. + * lib/std/debug (DEPRECATIONMSG, DEPRECATED): Improve LDoc usage + examples. + + refactor: merge debug.argscheck and debug.export. + * lib/std/debug.lua (argscheck): Remove. + (export): Rename to argscheck. + Adjust all callers. + + refactor: merge lib/std/base files into std.base. + * lib/std/base/functional.lua (callable, collect, reduce): Move + from here... + * lib/std/base/string.lua (copy, render, split): ...here... + * lib/std/base/tree.lua (leaves): ...and here... + * lib/std/base.lua (callable, collect, reduce, copy, render) + (split, leaves): ...to here. + Adjust all callers. + * lib/std/base/functional.lua, lib/std/base/string.lua, + lib/std/base/tree.lua: Remove files. + * local.mk (luastdbasedir, dist_luastdbase_DATA): Remove. + + refactor: std.vector simplifications. + * lib/std/vector.lua: Fix usage examples to use `avector` instead + of `anvector`. + (set): Factor away use of debug.argscheck. + + refactor: simplify export implementation and api. + * specs/debug_spec.yaml (export): specify behaviours with + explicit arguments instead of introspection. + * lib/std/debug.lua (export): expect explicit declaration string + with argument types, and inner function. + (whatpath, getinfo): Remove unused introspection functions. + * lib/std.lua.in, lib/std/base/string.lua, lib/std/functional.lua, + lib/std/io.lua, lib/std/list.lua, lib/std/math.lua, + lib/std/package.lua, lib/std/string.lua, lib/std/table.lua: + Move LDocs and export declarations to module table constructors. + + refactor: remove obsolete __ipairs specs. + stdlib no longer supports __ipairs metamethods. + * specs/vector_spec.yaml (__ipairs): Remove. + + list: remove workaround for old module metadata layout. + * lib/std/list.lua (transpose): Remove workaround for old module + data layout. + + refactor: simplify functional.memoize. + * lib/std/functional.lua (memoize): Remove spurious require and + associated comment. + +2014-08-25 Gary V. Vaughan + + refactor: use toomanyargmsg function instead of toomanyarg_fmt string. + * lib/std/debug.lua (toomanyarg_fmt): Remove. + (toomanyargmsg): A replacement function that returns the formatted + string. Adjust all callers. + * specs/debug_spec.yaml (extend_base): Adjust accordingly. + + refactor: factor getcompat and setcompat out of debug api. + * specs/debug_spec.yaml (getcompat, setcompat): Remove. + * lib/std/debug.lua (DEPRECATIONMSG): Use getcompat and setcompat + internally, returning an empty string if necessary. + (DEPRECATED): Don't use getcompat or setcompat now that + DEPRECATIONMSG does that. + (M): Remove getcompat and setcompat. + * lib/std/functional.lua (bind): Simplify. + + refactor: move DEPRECATED, export et.al. from `base` to `debug`. + * specs/base_spec.yaml (export, DEPRECATED): Move from here... + * specs/debug_spec.yaml (export, DEPRECATED): ...to here. + * specs/base_spec.yaml: Remove unused file. + * specs/specs.mk (specl_SPECS): Remove specs/base_spec.yaml. + * lib/std/base.lua (DEPRECATED, DEPRECATIONMSG, argcheck) + (argerror, arglen, argscheck, export, getcompat, setcompat) + (toomanyarg_fmt): Move from here... + * lib/std/debug.lua (DEPRECATED, DEPRECATIONMSG, argcheck) + (argerror, arglen, argscheck, export, getcompat, setcompat) + (toomanyarg_fmt): ...to here. Adjust all callers. + * lib/std/base.lua (argpairs, checktype, concat, formaterror) + (getfenv, getinfo, match, merge, normalize, permutations) + (setfenv, whatpath): Move from here... + * lib/std/debug.lua (argpairs, checktype, concat, formaterror) + (getfenv, getinfo, match, merge, normalize, permutations) + (setfenv, whatpath): ...to here, but elide their definitions + if _DEBUG.argcheck is false, or equivalent. + * NEWS: Update. + + refactor: break std.debug dependency on std.functional and std.string. + * specs/debug.spec (say): Specify usage of std.tostring. + * lib/std/debug.lua (tabify): Remove. + (say) Manually unroll functional compose sequence, formerly + knows as tabify. + (trace): Manually unroll and consequently remove string.rep call. + + base: support module name override with std.base.export. + * lib/std/base/list.lua (compare): Move from here... + * lib/std/base.lua (compare): ...to here. + * lib/std/base/list.lua: Remove. + * local.mk (dist_luastdbase_DATA): Remove lib/std/base/list.lua. + * lib/std/list.lua (M.compare): Provide explicit module name + argument. + * lib/std/base.lua (export): If an explicit module name was + passed, use that instead of reverse engineering it from the + source file containing an exported function. + +2014-08-24 Gary V. Vaughan + + base: remove export table metadata. + * lib/std/base.lua: Remove export table metadata. + + refactor: simplify std.require, and improve error diagnostics. + Closes #78. + * specs/std_spec.yaml (require): Specify better diagnostics on + failure. + * lib/std/list.lua (compare): Move from here... + * lib/std/base/list.lua (compare): ...to here. + * local.mk (dist_luastdbase_DATA): Add lib/std/base/list.lua. + * lib/std/base.lua: Break dependency on "std.list". + (require): Use base.list.compare directly, and show verbose + diagnostics on failure. + (module_version, version_to_list): Remove; no longer used. + * NEWS: Update. + + refactor: remove spurious comments from base.lua. + * lib/std/base.lua: Remove spurious comments. + +2014-08-23 Gary V. Vaughan + + maint: fix a typo in std.lua.in. + * lib/std.lua.in (export): Import this symbol correctly. + + refactor: simplify use of export by looking up local functions. + Prior to this changeset, export worked by creating an argument + checking wrapper function when called with an inner function, a + destination table containing metadata and the argument spec + string. The metadata leaked out into the library interface, as + well as other assorted clunkiness. Clean up and simplify the + whole thing. + * specs/spec_helper.lua (badarg): Adjust to produce consolidated + error messages. + * specs/functional_spec.yaml, specs/list_spec.yaml, + specs/std_spec.yaml: Remove element `1` from expected entries in + export table, now that metadata isn't leaked. + * specs/base_spec.yaml (DEPRECATED, export): Remove argument + checking specifications. + * lib/std/base.lua (getinfo): New functions. Use `debug.getinfo` + to lookup a local function in the given scope given only its + name. + (whatpath): Cross reference the package.path and function source + file from `debug.getinfo` to reverse engineer the module path + passed to require that was used to load this function. + (export): Remove all vestiges of hard-coded module metadata from + the destination table, and passing of an inner function; instead + look everything up using introspection and the new functions + above. Don't stash the result in a table parameter, return it. + Adjust all callers. + (copy, render, split): Move from here... + * lib/std/base/string.lua (copy, render, split): New file. ...to + here, so that base.whatpath reports the correct module path. + * local.mk (dist_luastdbase_DATA): Add lib/std/base/string.lua. + * lib/std/container.lua, lib/std/debug.lua, + lib/std/functional.lua, lib/std/io.lua, lib/std/list.lua, + lib/std/math.lua, lib/std/package.lua, lib/std/string.lua, + lib/std/table.lua (M): Remove metadata, rebuild after exported + functions' local declarations. + * NEWS: Update. + +2014-08-21 Gary V. Vaughan + + functional: have callable return the function rather than a bool. + * specs/functional_spec.yaml (callable): Adjust to check that + return values are the actual function. + * lib/std/base/functional.lua (callable): Return the actual + function. + + specs: break dependency on M[1], M[2] in badarg. + That is, polluting the exported module tables with the module + name (M = {"std.base"}) and object name (M = {"std.list", "List"}) + for argument error message text generation is ugly. + * specs/spec_helper.lua (badarg): Require an explicit module + name arg, and use it instead of relying on M[1]. + * specs/base_spec.yaml, specs/debug_spec.yaml, + specs/functional_spec.yaml, specs/io_spec.yaml, + specs/list_spec.yaml, specs/math_spec.yaml, + specs/package_spec.yaml, specs/std_spec.yaml, + specs/string_spec.yaml, specs/table_spec.yaml: Pass the explicit + module name arg to badarg calls. + * specs/list_spec.yaml: The goal is not to output different + error messages when called with ':' syntax than with '.' syntax. + Move module function argument check specifications into higher + scope; remove object method argument check specifications + entirely. + +2014-08-18 Gary V. Vaughan + + maint: prepare lib/std/list.lua for deprecation-apocalypse! + Almost half of lib/std/list.lua is only there to take care of + warning about deprecated usage. + * lib/std/list.lua: Group all deprecation support code in one + huge block ready for quick and easy annihilation in due course. + + refactor: move list.depair and list.enpair into std.table. + * specs/table_spec.yaml (depair, enpair): Specify full behaviours. + * specs/list_spec.yaml (depair, enpair): Specify deprecation + warnings. + * lib/std/list.lua (depair, enpair): Move from here... + * lib/std/table.lua (depair, enpair): ...to here. + * NEWS: Update. + +2014-08-18 Gary V. Vaughan + + Revert "list: exchange parameter order for list.cons." + This reverts commit 1b290ee40f638b03a4dd4b3c4f5b8faa6cdd2479. + + + Conflicts: + lib/std/list.lua + specs/list_spec.yaml + +2014-08-18 Gary V. Vaughan + + maint: disable specl rock version check by bootstrap. + Specl 13 is not released yet, but we're relying on some fixes + it has. + * bootstrap.conf (buildreq): Disable specl temporarily. + + travis: use unreleased specl rockspec. + * .travis.yml (script): Use specl-git-1.rockspec from github. + + refactor: move `list.project` to `table.project`. + * specs/table_spec.yaml (project): Specify behaviours of project. + * specs/list_spec.yaml (project): Specify deprecation warnings. + * lib/std/list.lua (project): Move from here... + * lib/std/table.lua (project): ...to here. + * NEWS: Update. + + refactor: move `list.shape` to `table.shape`. + * specs/table_spec.yaml (shape): Specify full behaviours. + * specs/list_spec.yaml (shape): Specify deprecation warnings. + * lib/std/list.lua (shape): Move from here... + * lib/std/table.lua (shape): ...to here. + * NEWS: Update. + +2014-08-18 Gary V. Vaughan + + refactor: move list.flatten to table.flatten. + * specs/list_spec.yaml (flatten): Specify deprecation warnings. + * specs/functional_spec.yaml (flatten): Move from here... + * specs/table_spec.yaml (flatten): ...to here. + * lib/std/list.lua (flatten): Move from here... + * lib/std/table.lua (fatten): ...to here. + * lib/std/functional.lua (collect): Move core from here... + * lib/std/base/functional.lua (collect): ...to here. + + * specs/list_spec.yaml (flatten): Deprecated properly. + * lib/std/list.lua (flatten): Wrap object method in deprecation + warning. + +2014-08-18 Gary V. Vaughan + + list: deprecate filter in favour of functional.filter. + * specs/list_spec.yaml (filter): Specify deprecation messages. + * lib/std/list.lua (filter): Deprecated. + * NEWS: Update. + + list: properly deprecate `list.map` to match NEWS. + * specs/list_spec.yaml (map): Deprecated properly. + * lib/std/base/functional.lua (map): Move from here... + * lib/std/functional.lua (map): ...back to here. + * lib/std/list.lua (map): Reinstate deprecated version of this + function locally, for simplicity. + + doc: document list.cons arguments in the correct order. + * lib/std/list.lua (cons): Fix LDoc to display parameters in the + correct order. + + list: export all apis for automatic argument type checking. + * specs/list_spec.yaml: Specify argument checking of all apis. + * lib/std/functional.lua (map): Move core function from here... + * lib/std/base/functional.lua (map): ...to here. + * lib/std/list.lua (project, map, map_with, transpose): Use it. + (m): Collect exported object methods here. + (List.__index): Set to m. + + refactor: simplify deprecation specs. + * lib/std/base.lua (DEPRECATED): Use `name` as the key into the + table for recording whether each deprecation message has been + output yet... it's more likely to be unique than the inner + function address, which might be shared between a module function + and object method. + * specs/functional_spec.yaml, specs/list_spec.yaml, + specs/string_spec.yaml, specs/table_spec.yaml: Simplify + specifications for deprecation warning. + + specs: fix os.execute thinko. + * specs/spec_helper.lua (LUA): No need to repeat ourselves by + looking for lua in the PATH with which and again at runtime. + + configury: use static specs/spec_helper.lua. + No need to generate this file just to substitute @LUA@, just + use os.getenv "LUA" in a static file instead. + * specs/spec_helper.lua.in: Move from here... + * specs/spec_helper.lua: New file. ...to here. + (LUA): Set from LUA environment, or call `which lua` or just rely + on path search for "lua". + * .gitignore: Remove specs/spec_helper.lua. + * configure.ac (AC_CONFIG_FILES): Remove specs/spec_helper.lua + generator. + * specs/specs.mk (specs_path, spec-check-local): Remove. + (SPECL_ENV): Simplify. + (EXTRA_DIST): Adjust. + * specs/io_spec.yaml (process_files, readlines, slurp) + (writelines): Don't rely on specs/spec_helper.lua being in the + builddir, in case of VPATH builds. + + travis: add .slackid for slack notifications. + * .slackid: New file. + * .travis.yml: Regenerate. + + slingshot: sync with upstream, for slack notifications. + * slingshot: Sync with upstream. + * .travis.yml: Regenerate. + + maint: remove stale files from .gitignore. + * .gitignore: Remove unused /m4/ax_compare_version.m4. + +2014-08-17 Gary V. Vaughan + + refactor: use to_raise matcher alias consistently. + Slingshot sanity checks flag strings with an initial capital + following 'error' as bad style. Rather than switch off that + check, use a matcher alias. + * build-aux/sanity-cfg.mk (sc_error_message_uppercase): Remove + spec-files from regexp. + specs/base_spec.yaml, specs/container_spec.yaml, + specs/debug_spec.yaml, specs/functional_spec.yaml, + specs/io_spec.yaml, specs/list_spec.yaml, specs/math_spec.yaml, + specs/optparse_spec.yaml, specs/package_spec.yaml, + specs/std_spec.yaml, specs/string_spec.yaml, + specs/table_spec.yaml, specs/tree_spec.yaml, + specs/vector_spec.yaml: s/to_error/to_raise/ + s/not_to_raise ()/not_to_raise "any error"/ + + refactor: simplify argument error specifications. + * specs/spec_helper.lua.in (toomanyarg): Remove. + (badarg): Now file local, and treats one or two numeric args as + a `too many arguments` error request. + (init): Prebind badarg module and function names. + * lib/std/base.lua: Add M[1] for error message matching. + * specs/container_spec.yaml (construction): Unroll non-generated + bad argument error messages. + * specs/base_spec.yaml, specs/debug_spec.yaml, + specs/functional_spec.yaml, specs/io_spec.yaml, + specs/list_spec.yaml, specs/math_spec.yaml, + specs/package_spec.yaml, specs/std_spec.yaml, + specs/string_spec.yaml, specs/table_spec.yaml: Simplify argument + error specifications. + +2014-08-16 Gary V. Vaughan + + base: support zero argument exports. + * specs/base_spec.yaml (export): Remove specification for zero + argument export error. + * lib/std/base.lua (export): Remove zero argument error. + * specs/base_spec.yaml (export): Specify argument checking of + zero argument exports. + * lib/std/base.lua (export): Support zero arguments. + + base: ensure export errors report callsite in stack trace. + * specs/base_spec.yaml (export): Finish and simplify mkstack(). + Specify callsite line numbers in export errors. + * lib/std/base.lua (formaterror): Allow expectedtypes to be a + string. + (export): Use it to generate error messages. + (export): Set levels correctly for correct callsite reporting. + +2014-08-15 Gary V. Vaughan + + refactor: move list.flatten to functional.flatten. + * lib/std/list.lua (flatten): Deprecate. + * lib/std/functional.lua (flatten): Export from here. + * specs/list_spec.yaml, specs/functional_spec.yaml: Adjust. + * NEWS: Update. + + refactor: merge most of std.base.functional back into functional. + * lib/std/base/functional.lua: Remove comment about needing to + wait until deprecated access points are gone before merging. + (foldl, foldr, memoize, nop): Move from here... + * lib/std/functional.lua (foldl, foldr, memoize, nop): ...to here. + * lib/std/list.lua (foldl, foldr): Keep a file local copy of + these functions to satisfy deprecated access points. + + refactor: share leaves implementation from new std.base.tree. + * lib/std/base.lua (leaves): Move from here... + * lib/std/base/tree.lua (leaves): New file. ...to here. + * local.mk (dist_luastdbase_DATA): Add lib/std/base/tree.lua. + * lib/std/io.lua, lib/std/list.lua, lib/std/tree.lua: Adjust. + + refactor: use new functional apis in std.tree. + * lib/std/tree.lua (reduce, operator): Use these... + (fold, op): ...instead of these. + + functional: new zip and zip_with replace list transpose and zip_with. + * specs/functional_spec.yaml (zip, zip_with): Specify behaviour + of new general zip and zip_with apis. + * lib/std/functional.lua (zip, zip_with): New functions. + * lib/std/list.lua (transpose, zip_with): Deprecate. + * specs/list_spec.yaml (transpose, zip_with): Specify deprecation + warning behaviours. + * NEWS: Update. + +2014-08-14 Gary V. Vaughan + + list: deprecate list.map. + * specs/list_spec.yaml (map): Specify output of deprecation + warning on first call. + * specs/debug_spec.yaml (debug, say): Use functional.map for + mkwrap instead of deprecated list.map. + * lib/std/list.lua (map): Deprecate. + * NEWS: Update. + + functional: support default iterators where possible. + * specs/functional_spec.yaml (collect, filter, map): Specify + behaviours when iterator argument is omitted. + * lib/std/functional.lua (collect): Default iterator to ipairs. + (filter, map): Default iterator to pairs. + * NEWS: Update. + + functional: new callable module function. + * specs/functional_spec.yaml (callable): Specify correct behaviour + for new callable function. + * lib/std/functional.lua (iscallable): Move from here... + * lib/std/base/functional.lua (callable): ...to here. Adjust all + callers. + * lib/std/functional.lua (M.callable): Reexport as a public api. + * NEWS: Update. + + functional: improve LDocs for consistency and clarity. + * lib/std/functional.lua: Be consistent with parameter names in + all functions. + Be consistent with usage example formats. + Be consistent with lambda string quoting in examples. + +2014-08-13 Gary V. Vaughan + + functional: replace list.map_with using new functional.map_with. + * specs/functional_spec.yaml (map_with): Specify behaviour of + improved map_with implementation that handles tables. + * lib/std/functional.lua (map_with): Improved implementation of + `list.map_with`. + * lib/std/list.lua (map_with): Deprecate. + * specs/list_spec.yaml (map_with): Adjust accordingly. + * NEWS: Update. + + list: factor out manual argument checking. + * lib/std/list.lua (depair, map_with, project, transpose) + (zip_with): Use new "container of thing" support in export type- + lists to replace manual checks. + (_ARGCHECK): Remove. No longer used. + * specs/list_spec.yaml (depair, map_with, project, transpose) + (zip_with): Adjust error expectations accordingly. + + debug: support "container of homogenous_thing" in argcheck. + * specs/debug_spec.yaml (argcheck): Specify behaviours when + checking for combinations of "List of table" variations. + * lib/std/base.lua (formaterror): Add optional index parameter, + and diagnose errors that use it with new "type at index N" + format; otherwise, simplify "List of table" strings in + expectedtypes to just "List" when the error is in the outer + type matching. + (checktype): Abstracted out of `argcheck`. + (argcheck): Simplify accordingly. + Detect and diagnose element mismatches with "List of table" + expected types. + (export): Likewise. + (typeof): Factored out entirely. + + list: update LDocs. + * lib/std/list.lua: Add @function and @static keywords. + + list: base.export module functions for improved argchecks. + * lib/std/list.lua (_functions): Rename from this... + (M): ...to this. Add a module name entry for export. + * specs/list_spec.yaml (exported_apis): Adjust accordingly. + (append, compare, concat, filter, flatten, map, project, rep) + (sub, tail, transpose, zip_with): Modernize badarg error + specifications. Specify "too many arguments" error behaviours. + * lib/std/list.lua (cons): Store in M; adjust getcompat/setcompat + id. + (map_with, project, transpose, zip_with): Use export for improved + argchecks. + (append, compare, concat, depair, enpair, filter, flatten, map) + (rep, shape, sub, tail): Likewise. Remove manual argcheck calls. + + specs: specify `std.list` apis. + * specs/list_spec.yaml (std.list): Add specs for exported apis, + and global table hygiene. + + string: remove unused local. + * lib/std/string.lua (render): Remove unused local. + + refactor: split out base functions for `std.functional`. + * lib/std/base.lua (foldl, foldr, memoize, nop, reduce): Move + from here... + * lib/std/base/functional.lua (foldl, foldr, memoize, nop) + (reduce): New file. ...to here. + * lib/std/functional.lua, lib/std/list.lua: Adjust imports + accordingly. + * local.mk (luastdbasedir, dist_luastdbase_DATA): Install new + file correctly. + + refactor: move list.foldl and list.foldr to std.functional. + Move the documented location for foldl and foldr from list.lua + to functional.lua, modernizing specs as we go. Keep the old + access points, with a deprecation warning. + * lib/std/list.lua (foldl, foldr): Move from here... + * lib/std/base.lua (foldl, foldr): ...to here. + * lib/std/functional.lua (reduce): Move from here... + * lib/std/base.lua (reduce): ...to here, where foldl and foldr + can use it. + * specs/list_spec.yaml (foldl, foldr): Copy from here... + * specs/functional_spec.yaml (foldl, foldr): ...to here. + * specs/base_spec.yaml (before): Don't depend on the location of + implementation of nop. + * NEWS: Update. + + travis: add slack notifications. + * .travis.yml (notifications): Add slack. + +2014-08-11 Gary V. Vaughan + + refactor: move lambda back to std.functional. + * lib/std.lua.in (lambda): Move from here... + * lib/std/base.lua (lamba): ...and here... + * lib/std/functional.lua (lambda): ...to here. + * specs/functional_spec.yaml, specs/std_spec.yaml: Adjust + accordingly. + * lib/std/base.lua (tostring): Unroll lambda calls. + * lib/std/operator.lua ("#"): Move implementation from here... + * lib/std/base.lua (len): ...to here. Adjust all callers. + (_len): Remove. + (operator): Remove unused require statement + * lib/std/list.lua (transpose): Unroll lambda call. + * NEWS: Update. + + functional: new `cond` function. + * specs/functional_spec.yaml (cond): Specify behaviour of a new + cond function. + * lib/std/functional.lua (cond): Satisfy specified behaviours. + * NEWS: Update. + + functional: process non-function branch values with case. + * specs/functional_spec.yaml (case): Specify behaviours with new + functable and non-callable branch values. + * lib/std/functional.lua (case): Call functables as if they were + regular functions, and return non-callable values directly. + * NEWS: Update. + + specs: add exported api specification to functional_spec.yaml. + * specs/functional_spec.yaml (std.functional): Specify exported + apis. + + refactor: functional.eval issues a deprecation warning. + * specs/functional_spec.yaml (eval): Specify deprecation warning. + * lib/std/functional.lua (eval): Deprecated. + * NEWS: Update. + + refactor: move memoize back to std.functional. + * specs/std_spec.yaml (memoize): Move from here... + * specs/functional_spec.yaml (memoize): ...to here. + * lib/std.lua.in (memoize): Move from here... + * lib/std/functional.lua (memoize): ...to here. + * NEWS: Update. + + refactor: move case back to std.functional. + * specs/std_spec.yaml (case): Move from here... + * specs/functional_spec.yaml (case): ...to here. + * lib/std/base.lua (case): Move implementation from here.. + * lib/std.lua.in (case): ...and argcheck wrapper from here... + * lib/std/functional.lua (case): ...to here. + * lib/std/package.lua (path_sub): Decouple from std.functional by + comparing manually rather than using functional.case. + +2014-08-07 Gary V. Vaughan + + functional: rename fold to reduce. + * specs/functional_spec.yaml (fold): Remove argument checking + specifications. + Add deprecation warning specification. + (reduce): Specify identical behaviour to old fold api. + * lib/std/functional.lua (fold): Rename from this... + (reduce): ...to this. + (fold): Show a deprecation warning on first use. + * specs/container_spec.yaml (construction): Adjust. + * lib/std/list.lua (foldl, foldr): Use functional.reduce instead + of deprecated functional.fold. + * lib/std.lua.in (barrel): Install _G.fold from + `std.functional.reduce`. + * specs/std_spec.yaml (barrel): Adjust. + * NEWS: Update. + + functional: map supports key:value remapping functions. + * specs/functional_spec.yaml (map): Specify behaviour of passing + all iteration return values to mapping function; and remapping + when mapping function returns a key:value pair. + * lib/std/functional.lua (map): Collect all iteration return + values and propagate them to the mapping callback function. + If there are two values returned from the callback, treat them as + a key and value for setting in the results table. + * NEWS: Update. + + functional: fold supports multi-return iterators. + * specs/functional_spec.yaml (fold): Specify behaviour with + iterators that return multiple values. + * lib/std/functional.lua (fold): Collect all values returned by + iterator and operate on the last one of those. + * specs/container_spec.yaml (construction): Fold dereferences + automatically, no need to manually dereference any more. + * NEWS: Update. + + doc: add LDoc Type section for callback function signatures. + * lib/std.lua.in (normalizecb): Document signature of memoize + callback function. + (memoize): Set type of *normalize* argument to new `normalizecb` + signature. + * lib/std/functional.lua (predicate): Document signature for a + predicate function. + (filter): Set type of *p* argument to new `predicate` signature. + * lib/std/io.lua (fileprocessor): Document signature of + process_files callback function. + (process_files): Set type of *fn* to new `fileprocessor` function. + * lib/std/string.lua (opentablecb, closetablecb, elementcb) + (paircb, separatorcb): Document signature for render callback + functions. + (render): Set type of callback functions accordingly. + * lib/std/table.lua (comparator): Document signature of sort + comparison function. + (sort): Set type of *c* argument to new `comparator` signature. + + functional: filter supports multi-parameter predicates. + * specs/functional_spec.yaml (filter): Specify behaviours when + called with an iterator that returns multiple values, and + predicate that accepts multiple parameters. + * lib/std/functional.lua (filter): Collect all results from + iterator function, and propagate them all to the predicate call. + * NEWS: Update. + + functional: collect creates tables from multi-return iterators. + * specs/functional_spec.yaml (collect): Differentiate behaviours + of calling collect with single return versus multiple return + iterators. + * lib/std/functional.lua (collect): Inject new elements into the + collected values table using key:value pairs when the iterator + returns more than one value. + * NEWS: Update. + + maint: bump copyright years. + * README.md: Bump copyright years to include 2014. + + maint: Update AUTHORS file. + * AUTHORS: Update. + + maint: use fully qualified api names in all deprecation messages. + * specs/list_spec.yaml (elems, index_key, index_value, relems) + (reverse, :depair, :map_with, :transpose, :zip_with): Specify + fully qualified api name in deprecation message. + * specs/string_spec.yaml (assert, require_version, tostring): + Likewise. + * specs/table_spec.yaml (clone_rename, metamethod, ripairs): + Likewise. + * lib/std/list.lua (elems, index_key, index_value, relems) + (reverse, :depair, :map_with, :transpose, :zip_with): Add `std.` + prefix to deprecation messages. + * lib/std/string.lua (assert, require_version, tostring): + Likewise. + * lib/table.lua (clone_rename, metamethod, ripairs): Likewise. + + functional: report bind api deprecations correctly. + * specs/functional.yaml (bind): Specify deprecation warning + behaviour when called with the legacy multi-argument parameter + passing. + * lib/std/functional.lua (bind): Use new getcompat/setcompat + internal apis to report deprecation of legacy calling convention. + + base: unwrap functables before calling debug.setenv on Lua 5.1. + * lib/std/base.lua (setfenv): Unwrap functables unconditionally, + before delegating to `debug.setfenv` or Lua 5.2 emulation. + + base: propagate environments through export argcheck wrappers. + * lib/std/base.lua (debug): Rename from this... + (debug_init): ...to this. + (_ARGCHECK, _DEBUG): Adjust accordingly. + (getfenv, setfenv): Compatibility functions for Lua 5.2. + (export): When returning argchecking wrapper function, propagate + the wrapper's function environment to the inner function. + * specs/base_spec.yaml (export): Adjust mkmagic not to rely on + out-of-scope MAGIC table. + +2014-08-05 Gary V. Vaughan + + list: exchange parameter order for list.cons. + Closes #72. + * specs/list_spec.yaml (cons): Modernize specifications. + Specify deprecation warning behaviour when calling cons with + arguments in legacy order. + * lib/std/list.lua (cons): If arguments appear to be in the + wrong order, issue a deprecation warning and rewrite them into + the correct order. + * NEWS: Update. + + refactor: break apart base.DEPRECATED for component reuse. + * lib/std/base.lua (DEPRECATIONMSG, getcompat, setcompat): New + functions, factored out of... + (DEPRECATED): ...here. Simplify accordingly. + + refactor: use std.ipairs and std.pairs everywhere internally. + * lib/std/container.lua, lib/std/functional.lua, lib/std/io.lua, + lib/std/list.lua, lib/std/optparse.lua, lib/std/package.lua, + lib/std/set.lua, lib/std/string.lua, lib/std/table.lua, + lib/std/tree.lua, lib/std/vector.lua: Import and use `base.ipairs` + and `base.pairs` everywhere + * NEWS: Update. + + specs: capture list.map_with deprecation warning. + * specs/list_spec.yaml (map_with): Modernize specifications for + argument checking, split module function and object method + specifications, and capture deprecation warning for list:map_with + on first invocation. + + std: remove __ipairs support in favour of 1..#t iteration. + * specs/std_spec.yaml (ipairs, ireverse, ripairs): Adjust + specifications to verify treatment of __len metamethod, and + ignore __ipairs metamethod. + * lib/base.lua (ipairs): Using __len if available, or # operator + otherwise, iterate over elments 1..#t. + (unwrap__ipairs): Remove. Simplifications above make this + function superfluous. + (ripairs, ireverse): Adjust accordingly. + * lib/std/vector.lua (core_metatable.__ipairs) + (alien_functions.__ipairs): Remove. + * lib/std.lua.in (ipairs, ireverse, ripairs): Update LDocs. + * NEWS: Update. + +2014-08-04 Gary V. Vaughan + + refactor: factor away math.pow. + Upcoming Lua 5.3 deprecates `math.pow` in favour of the `^` + operator. We can easily eliminate stdlib's references to + `math.pow` for future compatibility. + * lib/std/operator.lua ("^"): Use `^` rather than `math.pow`. + * lib/std/functional.lua (bind, fold): Use "^" lambda function + instead of `math.pow` in LDocs. + +2014-08-02 Gary V. Vaughan + + refactor: simplify deprecation management. + Closes #73. + * specs/base_spec.yaml (DEPRECATED): Specify behaviours of an + improved internal deprecation API. + * specs/list_spec.yaml (elems, index_key, index_value, relems) + (reverse): Specify default deprecation behaviours. + * specs/string_spec.yaml (assert, require_version, tostring): + Likewise. + * specs/table_spec.yaml (clone_rename, metamethod, ripairs): + Likewise. + * lib/std/debug.lua (_DEBUG): Document new compat field. + * lib/std/base.lua (_DEBUG): Set from debug_init.lua. + (deprecate): Rename from this... + (DEPRECATED): ...to this, and improve API. + * lib/std/functional.lua (bind): Use it to mark the old bind + API as deprecated since release 39. + * lib/std/list.lua (elems, index_key, index_value, relems) + (reverse): Collect in a new section and deprecate with the + improved API. + * lib/std/string.lua (assert, require_version, tostring): + Likewise. + * lib/std/table.lua (clone_rename, metamethod, ripairs): + Likewise. + * build-aux/sanity-cfg.mk (exclude_file_name_regexp): Don't choke + on error specs in specs/list_spec.yaml + * NEWS (Deprecations): Collect deprecated API NEWS for this + release. + + string: reference totable correctly in string.pickle. + Closes #79. + * lib/std/string.lua (totable): Set to table.totable. + Reported by Simon Cozens. + + list: specify shape method behaviours. + * specs/list_spec.yaml (shape): Specify method behaviours. + + list: specify list.zip_with, and fix revealed bugs. + * specs/list_spec.yaml (zip_with): Specify behaviour of zip_with + method. + * lib/std/list.lua (zip_with): Call list.map with correctly + * ordered arguments. + * NEWS: Update. + + list: specify list.transpose, and fix revealed bugs. + * specs/list_spec.yaml (transpose): Specify behaviour of transpose + method. + * lib/std/list.lua (transpose): Handle empty list argument. + Call list.map with correctly ordered arguments. + * NEWS: Update. + + maint: reinstate specl package.path workaround for luarocks bug. + Now that we're supporting LuaRocks' `init.lua suffixed filenames + get installed to an init installation directory` bug again, we + must adjust Specl's in-tree package.path to accommodate. + * specs/spec_helper.lua.in (package.path): Add "lib/?/init.lua". + * local.mk (std_path): Likewise + + specs: don't rely on `_G.arg[-1]:match "/lua[0-9.]*$"` + When `specs/optparse_spec.yaml` came over from Specl, I forgot to + upgrade the direct `hell.spawn` invocations to nicely abstracted + `spec_helper.lua:luaproc` calls. + * specs/optparse_spec.yaml (parser): Replace hell.spawn calls + with luaproc calls, so that Lua interpreter is set correctly. + +2014-07-31 Gary V. Vaughan + + slingshot: sync with upstream for upload and moonscript support. + * slingshot: Sync with upstream. + * bootstrap.conf (slingshot_files): Delete removed + ax_compare_version.m4. + * README.md (Installation): Show moonscript rocks repo. + * .travis.yml: Regenerate. + +2014-07-29 Gary V. Vaughan + + std: `std.require` now matches last dot-delimited version number. + * specs/std_spec.yaml (require): Specify behaviours when a version + string contains more than one substring with dot-delimited digits. + * lib/std/base.lua (module_version): Anchor the version matching + pattern at the end of the string. + * lib/std.lua.in (require): Improve LDocs accordingly. + * NEWS: Update. + + maint: reinstate LuaRocks init install bug workaround. + Latest LuaRocks still has the bug where Lua source files ending + in `init.lua` are installed to a subdirectory. Put back the + workaround I removed prematurely. + * lib/std/debug_init.lua: Move from here... + * lib/std/debug_init/init.lua: ...to here. + * local.mk (dist_luastd_DATA): Remove lib/std/debug_init.lua. + (dist_luastddebug_DATA): Add lib/std/debug_init/init.lua. + +2014-07-25 Gary V. Vaughan + + spec: modernize and normalize std specs. + * specs/std_spec.yaml: Reduce redundancy, and update to modern + style with fully argchecked apis. + * lib/std.lua.in (barrel): Scribble deprecated functions into + global namespace. + + doc: improve LDocs for std.lua. + * lib/std.lua.in: Tidy up and normalize LDocs. + + refactor: move `table.metamethod` to `std.getmetamethod`. + Be more in keeping with the style of core Lua. + * lib/std/table.lua (metamethod): Deprecate. + * lib/std.lua.in (getmetamethod): Export from here instead. + * specs/table_spec.yaml, specs/std_spec.yaml: Adjust accordingly. + * NEWS: Update. + + refactor: move `std.string.tostring` to `std.tostring`. + * lib/std/string.lua (render, tostring): Move from here... + * lib/std/base.lua (render, tostring): ...to here, with argchecks + removed. + * lib/std/string.lua (render): Re-export base.render from here. + (tostring): Re-export base.tostring with a deprecation notice. + * lib/std.lua.in (tostring): Re-export base.tostring from here. + (memoize): Simplify accordingly. + * specs/debug_spec.yaml, specs/string_spec.yaml, + specs/std_spec.yaml: Adjust accordingly. + * NEWS: Update. + + refactor: relocate std.lua contents to std.base and std. + * specs/lua_spec.yaml: Remove. All specs moved from here... + * specs/std_spec.yaml: ...to here. + * specs/specs.mk (specl_SPECS): Remove specs/lua_spec.yaml. + * lib/std/operator.lua (getmetamethod): Remove to break a + require loop. + * lib/std/lua.lua (assert, case, elems, eval, ielems, ipairs) + (ireverse, lambda, memoize, pairs, require, ripairs): Move from + here... + * lib/std/base.lua (assert, case, elems, eval, ielems, ipairs) + (ireverse, lambda, memoize, pairs, require, ripairs): ...to here, + removing argchecks... + * lib/std.lua.in (assert, case, elems, eval, ielems, ipairs) + (ireverse, lambda, memoize, pairs, require, ripairs): ...and + re-export from here with argcheck wrappers. + (barrel, monkey_patch): Adjust accordingly. + * lib/std/functional.lua (filter, fold, map): Adjust LDocs. + (case, eval, memoize): Re-export with argcheck wrappers. + * lib/std/string.lua (assert, require_version): Deprecate. + * build-aux/config.ld.in (file): Remove lib/std/lua.lua. + * local.mk (dist_luastd_DATA): Remove lib/std/lua.lua. + * specs/functional_spec.yaml (fold): Adjust require imports. + * NEWS: Update. + +2014-07-24 Gary V. Vaughan + + refactor: move `table.ripairs` to `lua.ripairs`. + * specs/table_spec.yaml (ripairs): Specify deprecation warning + on first use. + * specs/lua_spec.yaml (ripairs): Specify all behaviours for + ripairs. + * lib/std/base.lua (ripairs): Shared core functionality for + ripairs, respecting `__ipairs` metamethod even on Lua 5.1. + * lib/std/table.lua (ripairs): Use it, with a deprecation + warning on first use. + * lib/std/lua.lua (ripairs): Re-export it from here with full + argchecks. + * NEWS: Update. + +2014-07-23 Gary V. Vaughan + + refactor: replace `list.reverse` with `lua.ireverse`. + * specs/lua_spec.yaml (ireverse): Specify behaviour of new + ireverse function. + * specs/list_spec.yaml (relems, reverse): Specify new deprecated + behaviours of these functions. + * lib/std/list.lua (relems, reverse): Deprecated. + * lib/std/base.lua (ireverse): New `__ipairs` aware generator of + new reversed array-part of any table. + * lib/std/lua.lua (ireverse): Export `base.ireverse`. + (monkey_patch): Inject ireverse into given namespace. + * specs/std_spec.yaml (monkey_patch): Adjust accordingly. + * NEWS: Updated. + +2014-07-21 Gary V. Vaughan + + operator: break a require loop. + * lib/std/base.lua: Remove unused `require "std.operator"` to + break a require loop. + + operator: make '#' operator Lua 5.1 compatible. + * specs/operator_spec.yaml (#): Specify behaviour of # operator. + * lib/std/operator.lua (#): If there's a `__len` metamethod, call + it manually before falling back to actual `#` operator. + +2014-07-18 Gary V. Vaughan + + doc: improve render LDocs @usage examples. + * lib/std/string.lua (render, render_separator): Improve LDocs + @usage examples. + + doc: add missing @function to prettytostring LDocs. + * lib/std/string.lua (prettytostring): Add missing @function. + + refactor: move assert and require from std.string to std.lua. + * specs/string_spec.yaml (assert, require): Move from here... + * specs/lua_spec.yaml (assert, require): ...to here. + * lib/std/string.lua (assert, module_version, require, + version_to_list): Move from here... + * lib/std/lua.lua (assert, module_version, require, + version_to_list): ...to here. + * lib/std/string.lua (assert): Propagate invocations to std.lua, + with a deprecation warning. + * NEWS: Update. + + refactor: decouple std.lua from other modules. + * lib/std/lua.lua (wrapiterator): Move from here... + * lib/std/base.lua (wrapiterator): ...to here. + (ielems): Non-argchecked implementation. + * lib/std/lua.lua (ielems): Use it. + * lib/std/debug.lua, lib/std/list.lua, lib/std/set.lua, + lib/std/tree.lua: Use base.ielems internally. + * lib/std/functional.lua (case, eval, lambda, memoize): Load + std.lua on demand when these functions are called rather than + depending on it at require time. + * lib/std/table.lua: Remove unused std.lua requirement. + + lua: add a monkey_patch function. + * specs/lua_spec.yaml (monkey_patch): Specify behaviour of lua + monkey_patch function. + * lib/std/lua.lua (monkey_patch): Install lua functions into the + given namespace. + * lib/std/std.lua.in (monkey_patch): Add lua.monkey_patch + invocation. + (barrel): Remove double injection of `std.lua` functions. + * specs/std_spec.yaml (barrel): Add new 'std.lua' functions. + +2014-07-17 Gary V. Vaughan + + Merge branch 'waffle-iron-master' + +2014-07-17 Making GitHub Delicious. + + add waffle.io badge + +2014-07-17 Gary V. Vaughan + + lua: support __ipairs and __pairs metamethods on Lua 5.1. + * specs/lua_spec.yaml (ipairs, pairs): Specify portable behaviour + for new functions. + * lib/std/lua.lua (ipairs, pairs): New functions that support + __ipairs and __pairs metamethods, even on Lua 5.1. + (ielems, elems): Improve LDocs and argchecks. + * specs/lua_spec.yaml (elems, ielems): Adjust error message specs. + * NEWS: Update. + + debug: argcheck accepts a List object for a list parameter. + * specs/debug_spec.yaml (argcheck): Remove specifications for + mismatch errors between list parameters and List arguments. + * lib/std/base.lua (argcheck): Accept an empty List object for a + + refactor: simplify list.flatten implementation. + * lib/std/list.lua (flatten): Simplify. + + container: don't rewrap existing modulefunction functables. + * lib/std/container.lua (modulefunction): When re-exporting + module functions from another module, don't wrap inside another + functable. + +2014-07-16 Gary V. Vaughan + + list: deprecate list.elems module function. + * lib/std/list.lua (elems): Deprecate. + * specs/list_spec.yaml (elems): Specify deprecation warning + behaviour. + + refactor: consolidate and speed-up ielems and elems functions. + * specs/table_spec.yaml (elems, ielems): Move from here... + * specs/lua_spec.yaml (elems, ielems): ...to here. + * specs/functional_spec.yaml (fold): Adjust ielems import. + * lib/std/base.lua (ielems): Remove. + * lib/std/table.lua (elems, ielems): Remove. + * lib/std/lua.lua (elems, ielems): Wrap ipairs and pairs, taking + care to honor __ipairs and __pairs metamethods, for a noticeable + speedup. + * lib/std/debug.lua, lib/std/list.lua, lib/std/tree.lua: Adjust + ielems imports and examples. + * build-aux/sanity-cfg.mk (sc_error_message_uppercase): Add + specs/lua_spec.yaml. + * NEWS: Update. + +2014-07-14 Gary V. Vaughan + + refactor: move language features to a new `std.lua` module. + * specs/functional_spec.yaml (case, eval, lambda, memoize): Move + from here... + * specs/lua_spec.yaml: New file. ...to here. + * specs/std_spec.yaml: Adjust accordingly. + * specs/specs.mk (specl_SPECS): Add specs/lua_spec.yaml. + * build-aux/config.ld.in (file): Add lib/std/lua.lua. + * lib/std/base.lua (lambda): Move from here... + * lib/std/lua.lua (lambda): New file. ...to here. + * local.mk (dist_luastd_DATA): Add lib/std/lua.lua. + * lib/std/functional.lua (case, eval, lambda, memoize): Move from + here... + * lib/std/lua.lua (case, eval, lambda, memoize): ...to here. + * lib/std/string.lua (pickle): Adjust LDocs eval cross reference. + * NEWS: Update. + +2014-07-11 Gary V. Vaughan + + maint: revert automatic lambda string compilation. + * lib/std/base.lua (lambda): Core Lua APIs require actual + functions, so to be useful for passing lambda strings to core + APIs and stdlib APIs alike, return a raw function rather than a + functable. + (argcheck): Don't accept a compilable lambda string where a + function argument is expected. + * lib/std/debug.lua (lambda, argcheck): Adjust LDocs. + * lib/std/functional.lua: Likewise. + (bind, case, collect, compose, curry, filter, fold, map) + (memoize): Remove lambda argument compilation. + * lib/std/io.lua (process_files): Likewise. + * lib/std/list.lua (filter, foldl, foldr, map, map_with) + (zip_with): Likewise. + * lib/std/package.lua (mappath): Likewise. + * lib/std/string.lua (render): Likewise. + * lib/std/table.lua (sort): Likewise. + * specs/debug_spec.yaml, specs/functional_spec.yaml, + specs/io_spec.yaml, specs/list_spec.yaml, + specs/package_spec.yaml, specs/string_spec.yaml, + specs/table_spec.yaml: Adjust lambda specs. + * NEWS: Update. + + specs: don't load std.object for std.list spec examples. + * specs/spec_helper.lua.in (prototype): Copied from + lib/std/base.lua. + * specs/list_spec.yaml: Don't load 'std.object', use prototype + from spec_helper. + + base: provide better errors from exported object methods. + * specs/base_spec.yaml (export): Specify behaviour of exported + object methods. + * lib/std/base.lua (export): Use separator ':' between module name + and method name when dealing with methods, as opposed to '.' when + dealing with functions. + Count method arguments starting at '0' for self in error messages. + * build-aux/sanity-cfg.mk (sc_error_message_uppercase): Add + specs/base_spec.yaml. + +2014-07-10 Gary V. Vaughan + + doc: object and container _functions fields are optional. + * lib/std/container.lua (Container): _functions field is optional. + * lib/std/object.lua (Object): Likewise. + + doc: clarify use of compiled lambda strings. + * lib/std/functional.lua (Lambda): Add LDocs cross-references, + and a usage example with call field. + (lambda): Fix usage example not to show calling core Lua + table.sort with a functable! + + refactor: simplify std.base.lambda. + * lib/std/base.lua (lambda): Use `unpack` unconditionally. + Save lambda string in Lambda object. + + doc: add LDocs for functional.lambda return functables. + * lib/std/functional.lua (Lambda): Add LDocs. + (lambda): Document return type correctly. + + doc: improve debug module LDocs. + * lib/std/debug.lua (_DEBUG): Document default field values. + (argerror): Document interaction between function and lambda + strings. + + doc: improve functional module LDocs. + * lib/std/functional.lua: Improve module header LDocs. + + functional: make nop an official functional method. + * specs/functional_spec.yaml (nop): Specify behaviour of new nop + module method. + * specs/spec_helper.lua.in (nop): Remove one-off nop declaration. + * specs/string_spec.yaml (finds): Specify in-situ nop. + * specs/base_spec.yaml (std.base): Likewise. + * lib/std/base.lua (nop): Declare an official nop function. + * lib/std/functional.lua (nop): Re-export std.base.nop. + * NEWS: Update. + +2014-07-09 Gary V. Vaughan + + maint: clean up NEWS. + * NEWS: Update bitrotted recent entries to match reality. + + std: accept lamda strings as an alternative to functions. + * specs/functional_spec.yaml, specs/io_spec.yaml, + specs/list_spec.yaml, specs/package_spec.yaml, + specs/string_spec.yaml, specs/table_spec.yaml: Specify behaviours + of API calls that accept functions when given a lambda string + instead. + * lib/std/functional.lua (Lambda, lambda): Move from here... + * lib/std/base.lua (Lambda, lamba): ...to here. + (Lambda): Save arguments in table fields. + (argcheck): Accept a valid lambda string in lieu of a Lua + function. + * lib/std/table.lua (sort): When passed a lambda string, pass the + associated function to core table.sort. + * lib/std/functional.lua (bind, case, collect, curry, filter) + (fold, map, memoize): Accept lamda strings in lieu of Lua + functions. + * lib/std/io.lua (process_files): Likewise. + * lib/std/list.lua (filter, foldl, foldr, map, map_with) + (zip_with): Likewise. + * lib/std/string.lua (render): Likewise. + * NEWS: Update. + + specs: add specifications for std.operator. + * specs/operator_spec.yaml: New file. Specify behaviours for + operator functions. + * specs/specs.mk (specl_SPECS): Add specs/operator_spec.yaml. + +2014-07-08 Gary V. Vaughan + + refactor: factor functional.op into new std.operator module. + * lib/std/functional.lua (op): Move from here... + * lib/std/operator.lua (M): ...to here. + (M[".."], M["{}"], M[#"], M["~"], M["%"], M["^"]): New operators. + * local.mk (dist_luastd_DATA): Add lib/std/operator.lua. + * build-aux/config.ld.in (files): Likewise. + * local.mk (dist_module_DATA): Add std.operator.html. + * NEWS: Update. + +2014-07-07 Gary V. Vaughan + + functional: new lambda function. + Support compiling an anonymous Lua function from a "lambda string". + * specs/functional_spec.yaml (lambda): Specify behaviour for a + new lambda function. + * lib/std/functional.lua (lambda): Satisfy specification. + * NEWS: Update. + + functional: support multiple return values with memoize. + * specs/functional_spec.yaml (memoize): Specify behaviour when + passed a function with multiple return values. + * lib/std/functional.lua (memoize): Save return values from + wrapped function as a table, and unpack it when called again with + the same arguments. + * NEWS: Update. + +2014-07-04 Gary V. Vaughan + + doc: fix functional.op LDocs. + * lib/std/functional.lua (op): Workaround LDoc's inability to + render non-alphanumeric @field names. + + functional: support relational operators in op table. + * lib/std/functional.lua (op): Add `<`, `<=`, `>` and `>=`. + * NEWS: Update. + + specs: avoid tickling sc_error_message_uppercase sanity check. + * specs/spec_helper.lua.in (raise): An alias to the error matcher + to subvert matching `error` followed by `"[A-Z]` that prevents + make dist from completing. + * specs/container_spec.yaml (construction): Use the raise alias. + + reformat: order functional module functions asciibetically. + * lib/std/functional.lua: Reorder module functions asciibetically. + + specs: don't pass a Tree to object constructor. + * specs/tree_spec.yaml (construction): Don't pass a Tree to an + object constructor. + + refactor: use a function to export container module methods. + * specs/spec_helper.lua.in (badarg, toomanyarg): Format + appropriately when module name is not given. + * specs/container_spec.yaml (construction) + Update to latest style: + Add argcheck behaviour examples. + * lib/std/base.lua (olen): Rename from this... + (arglen): ...to this. + (M): Export arglen and toomanyarg_fmt. + * lib/std/container.lua (M): Add module name at element 1. + (__tostring, __totable): Reformat these... + (M.__tostring, M.__totable): ...as local table function + declarations. + (M.__call): When _ARGCHECK is not disabled, diagnose argument + type errors in table _init styl objects, to satisfy newly + specified behaviours. + (mapfields): Upgrade to base export declaration (for overhead + free argcheck calls with _DEBUG=false) and simplify accordingly. + +2014-07-03 Gary V. Vaughan + + maint: rename array to vector. + In mathematics "array" suggests the possibility of multiple + dimensions, and while one can simulate that with a std.array + of std.arrays, the name "vector" is a better fit for what this + class supports. + * lib/std/array.lua, specs/array_spec.yaml: Move from here... + * lib/std/vector.lua, specs/vector_spec.yaml: ...to here. + Rename symbols accordingly. + * build-aux/config.ld.in (file): Adjust accordingly. + * local.mk (dist_luastd_DATA, dist_classes_DATA): Likewise. + * specs/specs.mk (specl_SPECS): Likewise. + * specs/string_spec.yaml (render): Adjust Array using example to + Vector. + + refactor: use a function to export table apis. + * specs/table_spec.yaml (clone, clone_select, elems, empty) + (ielems, invert, keys, merge, merge_select, metamethod) + (monkey_patch, new, pack, ripairs, size, totable, values): + Update to latest style: + Add "too many argument" behaviour checks. + Simplify and standardise argument error message comparisons. + * lib/std/table.lua (M): Add module name at element 1. + (clone, clone_select, elems, empty, ielems, invert, keys) + (merge, merge_select, metamethod, monkey_patch, new, pack) + (ripairs, size, totable, values): Upgrade to base export + declarations (for overhead free argcheck calls with _DEBUG=false) + and simplify accordingly. + + maint: settle on calling std api calls `Module Functions`. + * lib/std/debug.lua, lib/std/io.lua, lib/std/package.lua, + lib/std/table.lua, lib/std/tree.lua: Consolidate comment section + header as "Module Functions.". + +2014-07-02 Gary V. Vaughan + + maint: don't flag `Lua` strings as invalid errors in spec-files. + * build-aux/sanity-cfg.mk (exclude_file_name_regexp): Add + specs/debug_spec.yaml. + + debug: finish support for nil arguments in exported functions. + Because luajit (legitimately) stops counting on the first nil + value in a list, where Lua 5.1 and 5.2 keep counting, we have + to do our own size calculations on argument vectors to be + consistent. + * lib/std/base.lua (olen): Return the largest integer key from a + table. + (match, export): Use it to ignore `nil` elements in the argument + list when counting the number of arguments. + + debug: support nil arguments in functions declared with export. + * lib/std/base.lua (opairs): Like ipairs, but does not stop at + the first nil value. + +2014-07-01 Gary V. Vaughan + + refactor: use a function to export string apis. + * specs/string_spec.yaml (__concat, __index, assert, caps, chomp) + (escape_pattern, escape_shell, finds, format, ltrim, monkey_patch) + (numbertosi, ordinal_suffix, pad, pickle, prettytostring, render) + (require, require_version, rtrim, split, tfind, tostring, trim) + (wrap): Update to latest style: + Add "too many argument" behaviour checks. + Simplify and standardise argument error message comparisons. + * lib/std/string.lua (M): Add module name at element 1. + (__concat, __index, assert, caps, chomp, escape_pattern) + (escape_shell, finds, format, ltrim, monkey_patch, numbertosi) + (ordinal_suffix, pad, pickle, prettytostring, render, require) + (require_version, rtrim, split, tfind, tostring, trim): Upgrade + to base.export declarations (for overhead free argcheck calls + with _DEBUG = false) and simplify accordingly. + + refactor: simplify caps, chomp, escape_pattern and escape_shell. + * lib/std/string.lua (caps, chomp, escape_pattern, escape_shell): + Remove extraneous parens around return argument. + Use Lua :-method call sugar to shorten gsub invocations. + + refactor: rename string.require_version to string.require. + * specs/string_spec.yaml (require): A copy of the require_version + specs. + (require_version): Also check for deprecation warning on first + use. + (monkey_patch): Check that new `require` function is written into + the given namespace. + * specs/std_spec.yaml (barrel): Likewise. + (monkey_patch): Check that the deprecated `require_version` is + still written into the global namespace. + * lib/std/string.lua (require_version): Rename from this... + (require): ...to this. + (require_version): A deprecated copy of `string.require`. + * NEWS: Update. + + refactor: simplify std.string.require_version. + * lib/std/string.lua (version_to_list, module_version): Factored + out of require_version, rather than defining new temporary local + functions on each invocation of require_version. + (require_version): Simplify accordingly. + + refactor: simplify std.string.tfind. + * lib/std/string.lua (tpack): Factored out of tfind, rather than + defining a new temporary local pack function on every invocation. + (tfind): Simplify accordingly. + + refactor: simplify std.string.format. + * lib/std/string.lua (format): Simplify. + + refactor: simplify std.string.assert. + * lib/std/string.lua (assert): Simplify. + + refactor: no need for the underscore in local _floor. + There's no clash between M.floor and local floor now we're using + `export` to declare api calls. + * lib/std/math.lua (_floor): Rename from this... + (floor): ...to this. Adjust all callers. + + refactor: use a function to export package apis. + * specs/package_spec.yaml (find, insert, mappath, normalize) + (remove): Update to latest style. + Add "too many argument" behaviour checks. + Simplify and standardise argument error message comparisons. + * lib/std/package.lua (M): Add module name at element 1. + (find, insert, mappath, normalize, remove): Upgrade to + base.export declarations (for overhead free argcheck calls with + _DEBUG = false) and simplify accordingly. + (package): Improve LDocs. + + specs: work around luajit argument counting gotcha. + Luajit truncates a variadic function call's argument list at + the first nil! + * specs/base_spec.yaml (export): Don't pass nil part way through + a variadic function call's argument list. + + base: support optional arguments in export type declarations. + The algorithm is approximately to collect every possible + permutation of argument type-spec list with and without any + optional arguments. An optional argument in the last position + must match the given type or nil, so that the permutation with + the final optional removed matches, allowing an uncaught + mismatched type at that position. Then we try to match the + actual arguments against each permutation until one passes, + otherwise diagnose the mismatch, reporting that any type + at the mismatched index from all permutations is required. + * specs/base_spec.yaml (export): Specify behaviours when called + with a declaration containing an optional argument wrappend in + square brackets. + * lib/std/base.lua (match, formaterror): New functions; factored + out of argcheck. + (copy, match, normalize, permutations): New functions; support + classification of matchable argument type-specs. + (export): Use them to implement optional arguments in export + type declarations to satisfy new specifications. + + doc: improve strict LDocs. + * lib/std/strict.lua: Improve LDocs. + + doc: correct parameter descriptions on debug.argscheck. + * lib/std/debug.lua (argscheck): Improve LDocs. + + specs: decouple spec_helper.lua from std.table and std.functional. + * specs/spec_helper.lua.in (bind): Use our own implmentation of + bind, otherwise if std.functional becomes unloadable, the whole + specl suite is unusable. + (totable): Likewise for std.table.totable. + (set): No need to rely on std.set, when a simple table index + dereference works equally well. + + refactor: use a function to export io apis. + * specs/io_spec.yaml (catdir, catfile, die, monkey_patch) + (process_files, readlines, shell, slurp, splitdir, warn) + (writelines): Update to latest style. + Add "too many argument" behaviour checks. + Simplify and standardise argument error message comparisons. + * lib/std/io.lua (M): Add module name at element 1. + (catdir, catfile, die, monkey_patch, process_files, readlines) + (shell, slurp, splitdir, warn, writelines): Upgrade to + base.export declarations (for overhead free argcheck calls with + _DEBUG = false) and simplify accordingly. + (catdir, catfile, die, monkey_patch, process_files, readlines) + (shell, slurp, splitdir, warn, writelines): Improve LDocs. + * NEWS: Update. + + debug: revert export of trace. + Both because the argument check wrapper is not properly tail-call + eliminated by Lua 5.1, and because as a core function callback + the function signature is fixed already, there's no good reason + to check arguments on debug.trace. + * specs/debug_spec.yaml (trace): Remove argument check behaviour + specifications. + * lib/std/debug.lua (trace): Remove the export wrapper. + +2014-06-30 Gary V. Vaughan + + debug: revert export of argerror, argcheck and argscheck. + Annoyingly, Lua 5.1 misses an obvious tail call elimination when + _DEBUG.argcheck is set, so the deep call to error gets the wrong + level, and reports argument errors in the wrong functions!! Rather + than uglify the code to remove the tail-calls and do a recount, + or add a fudge factor when Lua 5.1 is detected, it's cleaner to + remove the argchecking wrappers of the 3 affected functions -- at + least until we're ready to drop Lua 5.1 support entirely. + * specs/debug_spec.yaml (argerror, argcheck, argscheck): Mark the + argument checking behaviours as pending. + * lib/std/debug.lua (argerror, argcheck, argscheck): Comment out + the argument checking wrappers, and call the bare functions. + +2014-06-13 Gary V. Vaughan + + debug: improve argcheck list semantics. + Following the principle of least surprise, make a new `#list` + check type that has the same behaviour as `list` did previously, + for orthogonality with `#table`, and add a new `list` + implementation for orthogonality with `table`. + * specs/debug_spec.yaml (argcheck): Specify behaviours of `list` + and `#list` with new semantics. + * lib/std/debug.lua (argcheck): Update LDocs. + * lib/std/base.lua (argcheck): Count the elements of a `list` + and ensure that number is not greater than the result of the + length operator -- i.e. the table has no holes, and no non- + integer keys. + When building a type-mismatch error, write `empty list` where + appropriate. + * lib/std/debug.lua (argcheck): Make the 2nd argument a `#list`. + + refactor: use export function to simplify debug argchecks. + * lib/std/debug.lua (argcheck, argerror, argscheck, trace): Add + argument checking. + * specs/debug_spec.yaml: Adjust. + + refactor: use a string specifier instead of a table for export. + * specs/base_spec.yaml (export): Specify behaviours of export + function, particularly with argument errors. + * lib/std/base.lua (export): In place of an export name and a + table of expected argument types, parse a single `decl` argument + into an export name and table of argument types. + Support specified argument error behaviours. + * lib/std/math.lua (floor, monkey_patch, round): Simplify + accordingly. + * lib/std/functional.lua (bind, case, collect, compose, curry) + (eval, filter, fold, map, memoize): Likewise. + + refactor: use pipe-delimited strings for argcheck type lists. + * specs/debug_spec.yaml (argcheck, argscheck): Adjust for + pipe-delimited string instead of table of strings. + * lib/std/base (argcheck): Use base.split to make a table from + expected argument. + * lib/std/array.lua, lib/std/container.lua, lib/std/debug.lua, + lib/std/io.lua, lib/std/list.lua, lib/std/package.lua, + lib/std/string.lua, lib/std/table.lua: Adjust argcheck and + argscheck types argument accordingly. + + refactor: use a function to simplify bad argument specs. + * specs/spec_helper.lua.in (badarg, toomanyarg): Assemble a + suitable error string from arguments. + * specs/function_spec.yaml, specs/math_spec.yaml: Simplify + accordingly. + + refactor: use a function to export functional apis. + * lib/std/base.lua (argcheck): Accept "func" as an alias for + "function". + (export): When the last types element ends with "*", check type + of remaining unchecked args against it. + If there is no "*" mark in the types list, and more arguments are + passed than types entries, throw a "too many arguments" error. + Return the unwrapped function argument, so it can be captured + back into a local by the caller. + * specs/functional_spec.yaml (case, curry, eval, memoize): Check + these fixed argument functions throw a "too many arguments" error + when called with too many arguments. + * specs/math_spec.yaml (floor, monkey_patch, round): Likewise. + * lib/std/functional.lua (bind, case, collect, compose, curry) + (eval, filter, fold, map, memoize): Use base.export for + conditional argument checking. Simplify accordingly. + (functional): Rename from this... + (M): ...to this. + + refactor: use a function to export math apis. + * lib/std/base.lua (export): New function. Add a function to the + module export table, with or without argument checking as + appropriate. + * lib/std/math.lua (floor, monkey_path, round): Simplify + accordingly. + (M): Store the module prefix at index 1, for export argerror + calls. + * specs/spec_helper.lua.in (show_apis): Ignore module prefix at + index 1 of export table. + * specs/math_spec.yaml (round): Correct a typo in argerrors. + + list: deprecate index_key and index_value. + * specs/list_spec.yaml (index_key, index_value): Check that the + functions issue a depraction warning on first use. + * lib/std/list.lua (index_key, index_value): Add deprecation + warning with base.deprecate. + * NEWS (Incompatible changes): Update. + + table: add elems and ielems module functions. + * specs/table_spec.yaml (elems, ielems): Specify behaviour of + new iterators. + * lib/std/table.lua (elems): Iterate over all values of a table, + for orthogonality with std.list and std.set. + (ielems): Expose base.ielems in a type checking wrapper. + * NEWS: Update. + + doc: disable LDoc backtick references. + * build-aux/config.ld.in (backtick_references): Set to false, so + we can write fixed-width font words in LDoc comments. + +2014-06-10 Gary V. Vaughan + + list: prefer numeric comparison to asciibetical in compare. + Close #60. + * lib/std/list.lua (compare): If tonumber can make numbers out + of both arguments, use those results in preference to strings. + * specs/list_spec.yaml (compare): Specify behaviours with elements + that can be coerced to numbers. + * specs/string_spec.yaml (require_string): Remove pending #60 + commands. Add some more happy path examples. + +2014-06-09 Gary V. Vaughan + + refactor: differentiate module tables and prototype objects. + It turns out that documentation and code is much clearer when + we differentiate between `list` (the module table for `std.list`) + and `List` (the prototype List object), because that makes it + explicit whether we're calling a module function (`list.append`) + or performing an operation with the prototype (`List.clone {}`). + As a bonus, we gain a bit of speed by cloning the prototype + object from the module table, by virtue of not having a + `_functions` table to administer. + * lib/std/array.lua, lib/std/base.lua, lib/std/container.lua, + lib/std/debug.lua, lib/std/functional.lua, lib/std/io.lua, + lib/std/list.lua, lib/std/math.lua, lib/std/object.lua, + lib/std/optparse.lua, lib/std/set.lua, lib/std/strbuf.lua, + lib/std/string.lua, lib/std/table.lua, lib/std/tree.lua: Always + use the module table or prototype object as appropriate. Make + sure the LDocs don't contradict us. + + doc: add functional.memoize usage example. + * lib/std/functional.lua (memoize): Add a usage example. + + doc: fix some errors in functional usage docs. + * lib/std/functional.lua (collect): list.relems requires a List. + (map, filter, fold): list.elems requires a List. + + refactor: set local _ARGCHECK instead of dereferencing debug_init. + * lib/std/array.lua, lib/std/base.lua, lib/std/function.lua, + lib/std/io.lua, lib/std/list.lua, lib/std/package.lua, + lib/std/string.lua, lib/std/table.lua: Set local _ARGCHECK + instead of importing std.debug_init and dereferencing it all the + time. + + refactor: string.split is an argchecking base.split. + * lib/std/base.lua (split): Remove argument checking. + * lib/std/string.lua (split): Re-export base.split when we are + not argchecking, otherwise check types and call base.split when + successful. + + refactor: table.metamethod is an argchecking base.getmetamethod. + * lib/std/base.lua (metamethod): Rename from this... + (getmetamethod): ...to this, and remove argument checking. + Adjust export table. + * lib/std/table.lua (metamethod): Re-export base.getmetamethod + when we are not argchecking, otherwise check types and call + base.getmetamethod when successful. + * lib/std/object.lua (clone): Adjust. + * lib/std/string.lua (render): Likewise. + + refactor: list.elems is an argument checking base.ielem wrapper. + Functions in std.base are for internal use, and so all callers + have already validated arguments, so we shouldn't waste time + rechecking types on every call to base.elems. + Also this means list.elems can be strict about only accepting + List objects, and catch accidental table passing earlier. + * lib/std/base.lua (elems): Move from here... + (ielems): ...to here, and remove argument checking. Adjust + export table. + * lib/std/list.lua (elems): Re-export base.ielems when we are not + argchecking, otherwise check types and call base.ielems when + successful. + * lib/std/debug.lua (tabify): Use non-argchecked base.ielems. + * lib/std/list.lua (concat, depair, filter, foldl, map) + (map_with): Likewise. + * lib/std/set.lua (Set._init): Likewise. + * lib/std/table.lua (merge_namedfields): Likewise. + * lib/std/tree.lua (Tree.__index): Likewise. + * specs/functional_spec.yaml (fold): Use List objects + consistently. + * specs/list_spec.yaml (elems): Adjust error message + expectations. + + list: add argchecks. + * specs/list_spec.yaml (append, compare, concat, cons, depair) + (elems, enpair, filter, flatten, foldl, foldr, index_key) + (index_value, map, map_with, project, relems, rep, reverse) + (shape, sub, tail, traspose, zip_with): Specify behaviours for + missing or wrong type arguments. + 8 specs/object_spec.yaml: Decouple from list implementation + details. + * lib/std/list.lua (append, compare, concat, cons, depair) + (elems, enpair, filter, flatten, foldl, foldr, index_key) + (index_value, map, map_with, project, relems, rep, reverse) + (shape, sub, tail, traspose, zip_with): Check argument types + when not disabled by _DEBUG. + * build-aux/sanity-cfg.mk: Disable bogus failures when rejecting + uppercase error messages with lib/std/list.lua. + +2014-06-08 Gary V. Vaughan + + refactor: use list copying constructor to simplify list.append. + * lib/std/list.lua (append): Use implicit copy of argument object + constructor rather than slower __call constructor and explicit + unpack of argument elements. + + list: index_value and index_key return raw tables. + * specs/list_spec.yaml (index_key, index_value): Specify proper + behaviours. + * lib/std/list.lua (index_key, index_value): A non-contiguous + set of valid results cannot be represented as a std.list object, + so return a raw table. + +2014-06-07 Gary V. Vaughan + + doc: improve object LDocs, and add usage examples. + * lib/std/object.lua: Add usage examples to apis. + (clone): Normally we'd use the __call metamethod to clone from a + given object, so mark the LDocs for clone as @static because it + is primarily a module function. + (prototype): Mark the function documentation as @static, and add + equivalent method documentation. + + doc: add usage examples to container LDocs. + * lib/std/container.lua (__call, __tostring, __totable): Add + usage examples to LDocs. + + container: argcheck apis. + * lib/std/container.lua (mapfields, __call, __tostring) + (__totable): While it would be extremely convoluted to dig out + the functions behind these apis to call them with non-object + initial arguments, add type checking for completeness. + + doc: add usage examples to array LDocs. + * lib/std.array.lua: Add usage examples to LDocs. + + doc: move stringification functions to their own section. + * lib/std/string.lua (render, tostring, prettytostring, pickle): + Move to a new 'Stringification Functions' section. + + string: split on whitespace by default. + * specs/string_spec.yaml (split): Add an example with no explicit + split-pattern argument. + * lib/std/base.lua (split): Default split-pattern to `%s+` when + no argument provided. + * lib/std/string.lua (split): LDocs cite `%s+` as the default + pattern instead of `%s*`. + * NEWS: Update. + +2014-06-06 Gary V. Vaughan + + std: use argcheck instead of assert for type checking. + * specs/std_spec.yaml (barrel, monkey_patch): Make bad argument + examples more specific. + * lib/std.lua.in (barrel, monkey_patch): Use argcheck calls for + type checking insntead of assert. + + debug: support trailing "?" in place of separate "nil" in argcheck. + * specs/debug_spec.yaml (argcheck): Specify behaviour of trailing + "?" in type specifiers. + * lib/std/base.lua (argcheck): Strip trailing "?" from acceptable + argument types, appending a "nil" entry to the list if any "?" + is stripped. + * lib/std/base.lua, lib/std/debug.lua, lib/std/functional.lua, + lib/std/math.lua, lib/std/package.lua, lib/std/string.lua, + lib/std/table.lua: Adjust all callers to use trailing "?" instead + of separate explicit "nil" in type list. + + string: add argchecks and improve LDocs. + * specs/string_spec.yaml (assert, caps, chomp, escape_pattern) + (escape_shell, finds, format, ltrim, monkey_patch, numbertosi) + (ordinal_suffix, pad, prettytostring, render, require_version) + (rtrim, split, tfind, trim, wrap): Specify behaviours with bad + or missing arguments. + * lib/std/string.lua (assert, caps, chomp, escape_pattern) + (escape_shell, finds, format, ltrim, monkey_patch, numbertosi) + (ordinal_suffix, pad, prettytostring, render, require_version) + (rtrim, split, tfind, trim, wrap): Use argcheck or argscheck to + ensure argument types are validated when _DEBUG is not `false`. + Improve LDocs with @usage examples and parameter types. + * NEWS: Update. + + specs: add missing std.string specs. + * specs/string_spec.yaml (assert, pickle, render, require_version) + (tostring): Specify behaviour of these calls. + (require_version): Add pending examples for newly discovered + issue with non-numeric ordering. + +2014-06-05 Gary V. Vaughan + + string: fix number extraction in require_version. + * lib/std/string.lua (require_version): Change the match pattern + to actually extract the numeric part of the version string. + Also update LDocs to cite correct module._VERSION (with an under- + score). + * NEWS: Update. + + tree: remove std.object dependency. + No need to pull in all of std.object when loading std.tree. + * lib/std/tree.lua: Use base.prototype directly instead of via + re-exported object.prototype, which allows complete removal of + std.object dependency. + While we're here, use imported functions from locals to speed up + access a tiny bit. + + tree: remove std.list dependency requirement. + No need to pull in all of std.list when loading std.tree, just + for the foldl function. + * lib/std/tree.lua (__index): Use func.fold and base.elems from + existing required modules instead of list.foldl. + + string: remove unused string.__append metamethod. + The __append metamethod was added in commit 7a548ba, but only + ever had one client in the defunct std.lcs module which was + removed in commet 5f0a8af. + * lib/std/string.lua (__append): Remove. No longer required. + * specs/string_spec.yaml: Remove __append examples and references. + + refactor: put api and helper functions in sections. + * lib/std/array.lua, lib/std/container.lua, lib/std/io.lua, + lib/std/optparse.lua, lib/std/package.lua, lib/std/set.lua, + lib/std/string.lua, lib/std/table.lua, lib/std/tree.lua: To make + it clear that user-facing code ("api functions") need argument + checking, but internal code ("helper functions") does not, + explicitly separate those kinds of functions, and add some + block headers. + + array: remove duplicate nested argcheck, correctly this time. + * lib/std/array.lua: Where a function or metamethod is called via + `dispatch`, which already checks that the first argument is an + Array object, don't recheck type of self. If that leaves only + arguments of type "any" then don't spend any time checking + argument types at all. + + array: remove duplicate nested argcheck. + * lib/std/array.lua (dispatch): Remove duplicate nested argcheck. + The dispatched to functions already run a full argscheck, so no + need to check again here. + + maint: minimize overhead with argchecks disabled. + * lib/std/array.lua (__call): Wrap argchecking block in + `if debug._ARGCHECK`. + * lib/std/functional.lua (compose): Likewise. + * lib/std/io.lua (catdir, catfile): Likewise. + * lib/std/package.lua (normalize, insert): Likewise. + * lib/std/base.lua (split): Use argscheck instead of assert. + * lib/std/math.lua (monkey_patch): Likewise. + * specs/math_spec.yaml (monkey_patch): Adjust bad argument + behaviours. + + table: complete specs and improve LDocs. + * lib/std/table.lua (clone, clone_select, empty, invert, keys) + (merge, merge_select, monkey_patch, new, pack, ripairs, size) + (sort, totable, values): Add argcheck calls, and improve LDocs + with usage examples & cross-references. + (clone, clone_select, merge, merge_select): Minimise overhead + when argchecking is disabled by wrapping complex argument + checking in `if init._ARGCHECK`. + * specs/table_spec.yaml (pack, ripairs, totable): Add missing + specifications. + (clone, clone_select, empty, invert, keys, merge, merge_select) + (monkey_patch, new, size, sort, values): Improve specs to match + argcheck behaviours. + +2014-06-04 Gary V. Vaughan + + refactor: move _ARGCHECK calculation into std.debug_init. + * lib/std/base.lua (_ARGCHECK): Move from here... + * lib/std/debug_init.lua (M._ARGCHECK): ...to here. + Adjust clients. + + maint: remove workaround for legacy LuaRocks bug. + * local.mk (dist_luastddebug_DATA): Move + lib/std/debug_init/init.lua from here... + (dist_luastd_DATA): ...to lib/std/debug_init.lua. + (luastddebugdir, dit_luastddebug_DATA): Remove. + + doc: consolidate LDoc headers for core extension modules. + * lib/std/debug.lua, lib/std/io.lua, lib/std/math.lua, + lib/std/package.lua, lib/std/string.lua, lib/std/table.lua: + Consolidate LDoc headers for consistency. + +2014-06-03 Gary V. Vaughan + + package: check api call argument types, and improve LDocs. + * specs/package_spec.yaml (find, insert, mappath, normalize) + (remove): Specify bad argument behaviours. + * lib/std/package.lua (find, insert, mappath, normalize) + (remove): Use argscheck to diagnose bad arguments. + Add usage examples to LDocs. + + debug: support ":foo" parameter types with argcheck. + * specs/debug_spec.yaml (argcheck): Specify behaviours of using + ":foo" as a parameter type. + * lib/std/base.lua (argcheck): Implement ":foo" parameter checking. + * lib/std/debug.lua (argcheck): Update LDocs. + + refactor: reduce module dependencies of std.debug. + * lib/std/debug.lua: Remove std.io dependency by using core + file:write instead of std.io.writelines. + Remove std.list dependency by using lighter functional.map instead + of list.map. + Remove unused std.object dependency. + (tabify): Instead of a deeply nested in-situ call to list.map + and others, functionally compose an equivalent and use that to + simplify. + + maint: use "int" argcheck type as appropriate. + * lib/std/array.lua, lib/std/functional.lua: Use "int" parameter + type with argcheck as appropriate. + * lib/std/debug.lua (argscheck): Adjust usage example. + * specs/array_spec.yaml, specs/functional_spec.yaml: Adjust. + + math: check api call argument types. + * specs/math_spec.yaml (floor, round): Specify bad argument + behaviours. + * lib/std/math.yaml (floor, round): Use argscheck to diagnose + bad arguments. + + debug: support "int" parameter type with argcheck. + * specs/debug_spec.yaml (argcheck): Specify behaviours of using + "int" as a parameter type. + * lib/std/base.lua (argcheck): Implement "int" parameter checking. + + math: complete specs and improve LDocs. + * lib/std/math.lua (floor, monkey_patch, round): Improve LDocs + with usage examples. + * specs/io_spec.yaml (floor, round): Add missing specifications. + + maint: add an LDoc module header to private std.base module. + * lib/std/base.lua: Add LDoc module header. + + refactor: move split implementation from string to base. + Decouple std.io from std.string (and hence std.table, std.list, + std.functional, std.object, and std.container) by moving + implementation of split into lib/std/base.lua. + * lib/std/string.lua (split): Move from here... + * lib/std/base.lua (base): ...to here, and export. + * lib/std/string.lua: Re-export base.split as string.split. + * lib/std/io.lua: Don't pull all of string.lua and it's + dependencies into memory; use base.split instead of string.split. + + maint: use 2 blank lines between function definitions. + * lib/std/package.lua, lib/std/strict.lua, lib/std/string.lua: + Use 2 blank lines between function definitions. + + debug: complete specs and improve LDocs. + * lib/std/debug.lua (__call, _DEBUG, argcheck, argerror) + (argscheck, say, trace): Improve LDocs with usage examples and + cross-references. + * specs/debug_spec.yaml (_DEBUG): Remove. _DEBUG behaviours are + specified in the api calls that are affected by it. + (say, trace): Add missing specificatons. + + specs: account for different error messages between 5.1 and 5.2. + * specs/io_spec.yaml (process_files): Accept either of the + error message formats for passing a non-existent file to io.input + for Lua 5.1 or Lua 5.2. + +2014-06-02 Gary V. Vaughan + + std: improve LDocs. + * lib/std.lua.in: Improve LDocs with usage examples and + clearer language. + + io: complete specs and improve LDocs. + * lib/std/io.lua (catdir, catfile, die, monkey_patch) + (process_files, readlines, shell, slurp, splitdir, warn) + (writelines): Improve LDocs with usage examples and cross- + references. + * specs/io_spec.yaml (catdir, catfile, die, monkey_patch) + (process_files, readlines, shell, slurp, splitdir, warn) + (writelines): Add missing specificatons. + * specs/spec_helper.yaml (concat_file_content): New function to + support new specifications. + (luaproc): Support subprocess arguments and standard input. + +2014-06-01 Gary V. Vaughan + + object: enhance prototype to recognize file objects. + * specs/object_spec.yaml (prototype): Specify results of passing + file handles and Lua primitives to prototype. + * lib/std/object.lua (prototype): Improve LDocs. + * lib/std/base.lua (prototype): Enhance implementation to meet + new specifications. + * NEWS: Update. + + io: use argcheck for api functions. + * specs/io_spec.yaml (catdir, catfile, die, monkey_patch) + (process_files, readlines, shell, slurp, splitdir, warn) + (writelines): Specify argument type mismatch messages. + * lib/std/io.lua (catdir, catfile, die, monkey_patch) + (process_files, readlines, shell, slurp, splitdir, warn) + (writelines): Call argcheck to validate arguments and meet + specifications. + +2014-05-31 Gary V. Vaughan + + debug: add file object matching to argcheck. + * specs/debug_spec.yaml (argcheck): Specify behaviour when + matching against a file type argument. + * lib/std/debug.lua (argcheck): Add LDocs for file type. + * lib/std/base.lua (argcheck): When the required type is "file" + use io.type () to ensure that an open file object argument was + passed. + + io: don't pull in tree and dependencies with `require "io"`. + * lib/std/io.lua (tree): Remove requirement. + (base): Replace with much lighter module. + (writelines): Call base.leaves instead of tree.ileaves. In + addition to reducing coupling, this also saves another round + of `argscheck`ing, and a callstack frame. + +2014-05-30 Gary V. Vaughan + + array: support ipairs iteration over elements. + * specs/array_spec.yaml (__ipairs): Specify behaviour of ipairs + with arrays. + * lib/std/array.lua (core_metatable.__ipairs) + (alien_metatable.__ipairs): Implement ipairs support for table + and alien.buffer managed table elements. + + maint: demonstrate new and legacy bind api correctly. + Seems like I was confused over which functional.bind api was the + old, and which was the new. Correct that. + * specs/functional_spec.yaml (bind): Swap examples of new bind + api labelled legacy and vice versa. + * NEWS: Update. + +2014-05-29 Gary V. Vaughan + + refactor: merge std.base_array into std.array. + Instead of a one-way degrading from alien.buffer managed elements + to table managed elements with a sub-type, combine both sets of + optimised methods and metamethods back into a single `std.array` + container, which decides with each clone operation how to manage + elements of the named type. + * specs/array_spec.yaml: Specify behaviours for Array object that + dispatches module function calls at runtime, and assigns + optimized methods and metamethods on cloned objects according to + element type. + * lib/std/base_array.lua (pop, push, realloc, set, shift, unshift) + (__index, __newindex, __len, __tostring): Move from here... + * lib/std/array.lua (core_functions.pop, core_functions.push) + (core_functions.realloc, core_functions.set, core_functions.shift) + (core_functions.unshift, core_metatable.__index) + (core_metatable.__newindex, core_metatable.__len) + (core_metatable.__tostring: ...to here. + (pop, push, realloc, set, shift, unshift, __index, __newindex): + Move from here... + (alien_functions.pop, alien_functions.push, alien_functions.set) + (alien_functions.realloc, alien_functions,shift) + (alien_functions.unshift, alien_metatable.__index) + (alien_metatable.__newindex): ...to here. + (core_metatable.__call): Clone a new Array object, setting the + method and metatables with functions optimised for alien.buffer + or Lua table based element management according to availability of + alien, and element type name. + (dispatch): New runtime virtual table dispatch function. + (Array): Dispatch module functions at runtime based on element + type. + * lib/std/base_array.lua: Remove. + * local.mk (dist_luastd_DATA): Remove lib/std/base_array.lua. + * specs/base_array_spec.yaml: Remove. + * specs/specs.mk (specl_SPECS): Remove specs/base_array_spec.yaml. + * NEWS: Update. + + functional: complete specs and improve LDocs. + * lib/std/functional.lua (bind, case, curry, compose, eval) + (collect, map, filter, fold): Improve LDocs with usage examples, + and cross-references. + * specs/functional_spec.yaml (collect, compose, curry, eval) + (filter, fold, id, map, memoize): Add missing specifications. + + debug: argcheck can match functable with "function". + * specs/debug_spec.yaml (argcheck): Specify correct behaviour + when matching a functable against a "function" argument. + * lib/std/base.lua (argcheck): Allow functables when a "function" + type argument is required. + * lib/std/debug.lua (argcheck): Update LDocs. + + functional: bind should not require respecifying fixed args. + * specs/functional_spec.yaml (bind): Move incumbent examples to + a new legacy example. Rewrite original examples with new api. + Add specifications for behaviour when not all arguments are + given in the call to the bound function. + * lib/std/functional.lua (bind): Fill initial argument positions + from original bind arguments, and then propagate final call + arguments into non-fixed parameter positions. + * NEWS: Update. + + functional: use argscheck for api functions. + * specs/functional_spec.yaml (metamethod): Remove. This method + has moved to std.table. + (bind, case, collect, compose, curry, eval, filter, fold, map) + (memoize): Specify behaviour with missing or wrong type + arguments. + * lib/std/functional.yaml (bind, case, collect, compose, curry) + (eval, filter, fold, map, memoize): Add argscheck calls to + validate arguments when _DEBUG or _DEBUG.argcheck are not false. + + refactor: omit spurious parentheses around require result dereferences. + * lib/std/container.lua, lib/std/object.lua, lib/std/set.lua, + lib/std/string.lua, lib/std/tree.lua, specs/container_spec.yaml, + specs/list_spec.yaml, specs/object_spec.yaml, + specs/set_spec.yaml, specs/spec_helper.lua.in: Omit spurious + parentheses around require result dereferences. + + base: use argscheck for api functions. + * specs/base_spec.yaml (deprecate), specs/list_spec.yaml (elems): + Specify bad argument behaviours. + * specs/table_spec.yaml (metamethod): Add missing specifications, + including bad argument behaviours. + * lib/std/base.lua (deprecate, elems, metamethod): Use argscheck + to implement specified behaviours. + + refactor: don't pull in all of debug's dependencies for array. + * lib/std/array.lua, lib/std/base_array.lua: Import argcheck and + argscheck from std.base. + + refactor: move argscheck et.al. into std.base module. + Unfortunately, debug.say is a very high level function that pulls + in a lot of other modules, modules that we'd like to be able to + add debug.argscheck to... since argscheck has almost no pre- + requisites, move it into the base module where it can then be + available everywhere else. + * lib/std/container.lua (prototype): Move from here... + * lib/std/base.lua (prototype): ...to here. + * lib/std/debug.lua (concat, argerror, argcheck, argscheck): + Move from here... + * lib/std/base.lua (concat, argerror, argcheck, argscheck): ...to + here. + Be sure to disable argument checking if _DEBUG or _DEBUG.argcheck + are false. + +2014-05-27 Reuben Thomas + + functional.lua: add missing parameter name to docstring + +2014-05-24 Gary V. Vaughan + + alien: remove surplus std.alien module. + * lib/std/array.lua: Factor away dependencies on std.alien. + (__call): Instead of an _init call built for container.__call, + we now use a custom __call metamethod that returns a base_array + object that is optimized for Lua table buffers when the element + type is not recognized (either because there is no alien module + installed, or it does not support buffers of the given type), or + else builds an alien.buffer optimised object and returns that. + * lib/std/alien.lua, specs/alien_spec.yaml: Remove. + * build-aux/config.ld.in (file): Remove lib/std/alien.lua. + * local.mk (dist_luastd_DATA): Likewise. + (dist_modules_DATA): Remove doc/modules/std.alien.html. + * specs/specs.mk (specl_SPECS): Remove specs/alien_spec.yaml. + + base_array: non-alien capable base class for std.array. + * specs/base_array_spec.yaml: New file. Specify behaviour for + base_array. + * specs/specs.mk (specl_SPECS): Add specs/base_array_spec.yaml. + * lib/std/base_array.lua: New file. Implement base_array class to + satisfy specification. + * local.mk (dist_luastd_DATA): Add lib/std/base_array.lua. + + debug: argcheck "any" does not match `nil`. + * specs/debug_spec.yaml (argcheck): Passing a nil argument does + not satisfy the "any" spec. + * lib/debug.lua (argcheck): Require non-nil argument before + setting passing status for an "any" spec. + (argscheck): Remove "any" shortcut. + +2014-05-21 Gary V. Vaughan + + optparse: fix another global symbol leak. + * lib/std/optparse.lua (on): Declare `key` as a local variable. + + optparse: fix a global symbol leak. + * lib/std/optparse.lua (on): Declare `normal` as a local variable. + * NEWS (Bug fixes): Updated. + + configury: don't require specific git version. + * bootstrap.conf (buildreq): Don't set a git version number, or + else `GIT=true ./bootstrap` doesn't work. + + slingshot: sync with upstream. + * slingshot: Sync with upstream for grep GNUism fix. + +2014-05-20 Gary V. Vaughan + + array: support simultaneous alien and Lua array objects. + * lib/std/array.lua (alien_type): A set of valid types for + arrays to be implemented with alien.array. + (topointer): Default offset to 1. + (setzero): Use memset for alien arrays, direct element buffer + copying for Lua arrays. + (shift, unshift): Use memmove for manipulating alien.arrays, or + else table.insert and table.remove for Lua tables. + (copy): Remove. + (clone): Use memmove for copying elements between same-size + element alien arrays, direct element buffer copying otherwise. + (_init): Simplify accordingly. + + array: a new Object based array for queue and stack-like ops. + * specs/array_spec.yaml: New file. Specify base behaviours for a + new array object. + * specs/specs.mk (specl_SPECS): Add specs/array_spec.yaml. + * lib/std/array.lua: New file. Implement specified behaviours. + * build-aux/config.ld.in (files): Add lib/std/array.lua. + * local.mk (dist_classes_DATA): Add doc/std.array.html. + (dist_luastd_DATA): Add lib/std/array.lua. + * NEWS: Update. + + debug: fix argerror diagnostics on luajit. + Luajit seems to treat `return error` (where error never returns + anyway) as subject to some kind of optimisation that loses a call + stack level by the time `error` looks up the line number for the + targetted level. Removing the `return` results in the same call + stack frame reference in luajit, 5.2 and 5.1. + * lib/std/debug.lua (argerror): Drop the extraneous `return` + statement. + + debug: fix argcheck diagnostics on Lua 5.1. + Lua 5.2 seems to treat `return argerror` (where argerror never + returns anyway) as subject to some kind of optimisation that loses + a call stack level by the time control flow reaches the `error` + call inside. Removing the `return` results in the same call stack + frame reference in 5.2 and 5.1, but requires incrementing the + level for the correct result. + * lib/std/debug.lua (argcheck): Drop the extraneous `return` + statement, and increment `level` before calling `argerror`. + +2014-05-19 Gary V. Vaughan + + alien: implement a subset of alien to simplify wrappers. + * specs/alien_spec.yaml: Specify behaviour for a useful subset + of the alien APIs. + * specs/specs.mk (specl_SPECS): Add specs/alien_spec.yaml. + * local.mk (dist_modules_DATA): Add doc/modules/std.alien.html. + * lib/std/alien.lua: New file, provide pure Lua implementation + of alien.array, alien.memmove and alien.memset. + * build-aux/config.ld.in (files): Add lib/std/alien.lua. + * local.mk (dist_luastd_DATA): Add lib/std/alien.lua. + + debug: make sure to use debug.getinfo (). + * lib/std/debug.lua (trace): Use debug.getinfo instead of bare + getinfo. + * NEWS: Update. + + debug: add argcheck APIs. + * specs/debug_spec.yaml (argcheck, argerror, argscheck): Specify + behaviour of argument checking functions based on luaL_argerror + and luaL_argcheck, for standardised argument error diagnostics. + * lib/std/debug.lua: Tidy up LDocs. + (argcheck, argerror, argscheck): New functions to satisfy the + specifications. + * NEWS: Update. + +2014-05-01 Gary V. Vaughan + + maint: post-release administrivia. + * configure.ac (AC_INIT): Bump version to 41. + * NEWS: Add header line for next release. + * .prev-version: Record previous version. + * ./local.mk (old_NEWS_hash): Auto-update. + + Release version 40 + * NEWS: Record release date. + + refactor: remove unused `std.modules`. + * lib/std/modules.lua: Remove. + * local.mk (dist_luastd_DATA): Adjust. + + refactor: use `t[#t + 1] = v` rather than `table.insert (t, v)`. + Save a function call by factoring away calls to table.insert. + * lib/std/container.lua, lib/std/functional.lua, lib/std/io.lua, + lib/std/list.lua, lib/std/optparse.lua, lib/std/set.lua, + lib/std/strbuf.lua, lib/std/string.lua, lib/std/table.lua, + lib/std/tree.lua: Substitute accordingly. + + refactor: factor out merge_allfields and merge_namedfields. + * lib/std/table (clone, merge): Move shared functionality from + here... + (merge_allfields): ...to here. + (clone_select, merge_select): Move shared funtionality from + here... + (merge_namedfields): ...to here. + + refactor: move leaves and ileaves from base to tree module. + * lib/std/base.lua (ileaves, leaves): Move from here... + * lib/std/tree.lua (ileaves, leaves): ...to here. + * lib/std/base.lua (_leaves): Rename from this... + (leaves): ...to this. + * lib/std/list.lua (flatten): Adjust. + + refactor: move unshared methods out of std.base. + * lib/std/base.lua (merge): Move from here... + * lib/std/table.lua (merge): ...to here. + * lib/std/base.lua (append, compare, concat): Move from here... + * lib/std/list.lua (append, compare, concat): ...to here. + * specs/object_spec.yaml (std.object): Adjust. + + refactor: remove deprecated methods. + * lib/std/base.lua (new, metatable): Remove. + (concat, M): Simplify accordingly. + * lib/std/functional.lua: No need to require std.base. + * lib/std/set.lua (_functions): Move into object declaration, + removing `new`. + * lib/std/strbuf.lua (new): Remove. + * lib/std/tree.lua (new): Remove. + * NEWS: Update. + + tree: allow objects as keys. + * lib/std/tree.lua (Tree.__index): Only fold key list when key + parameter is a raw list, and not when it is a std.object derived + table. + (Tree.__newindex): Only descend the key list creating sub-Trees + when key parameter is a raw list, and not when it is a std.object + derived table. + * NEWS: Update. + + functional: generalize memoize with normalization parameter. + * lib/std/functional.lua (memoize): Don't rely on tostring + having been monkey-patched to std.string.tostring already, but + also don't automatically load all of std.string into memory + unless memoize is called without a normalization function. + * NEWS: Update. + + list: remove deprecated methods. + * lib/std/list.lua: Remove indexKey, indexValue, mapWith, zipWith, + new and slice. + * NEWS: Update. + + specs: std.table.pack is present for any supported Lua release. + * specs/table_spec.yaml (extend_base): List "pack" unconditionally. + + specs: Lua 5.1 does not call tostring on format "%s" arguments. + * specs/string_spec.yaml (..): Explicitly stringify nil argument. + +2014-04-26 Gary V. Vaughan + + std: barrel of monkey(patche)s! + Close #56. + Segregate monkey patching into module functions that have to + be called explicitly. + * lib/std.lua.in: Don't clobber any core metatables, or change + any global symbols on load. + * specs/io_spec.lua (monkey_patch): Specify behaviour of + std.io.monkey_patch function. + * lib/std/io.lua (monkey_patch): Add readlines and writelines + methods to core file objects. + (processFiles): Remove. + * specs/math_spec.lua (monkey_patch): Specify behaviour of + std.math.monkey_patch function. + * lib/std/math.lua (monkey_patch): Overwrite core math.floor. + * specs/string_spec.lua (monkey_patch): Specify behaviour of + std.string.monkey_patch function. + * lib/std/string.lua (monkey_patch): Overwrite core assert and + tostring functions, and add methods and metamethods to core + string objects. + (escapePattern, escapeShell, ordinalSuffix): Remove. + * specs/table_spec.lua (monkey_patch): Specify behaviour of + std.table.monkey_patch function. + * lib/std/table.lua (monkey_patch): Overwrite core table.sort. + * specs/table_spec.lua (barrel, monkey_patch): Specify behaviour + of std.barrel and std.monkey_patch functions. + * lib/std.lua.in (monkey_patch): New function for patching core + symbols and metatables by calling submodule `monkey_patch` + functions. + (barrel): New function for scribbling all over the given + namespace, as well as installing all std monkey_patches. + * specs/debug_spec.yaml, specs/functional_spec.yaml, + specs/io_spec.yaml, specs/math_spec.yaml, specs/package_spec.yaml, + specs/std_spec.yaml, specs/string_spec.yaml, specs/table_spec.yaml, + specs/tree_spec.yaml: Update to reflect removal of default + monkey patching. + + refactor: move `metamethod` from `functional` to `table` module. + * lib/std/functional.lua (metamethod): Move from here... + * lib/std/base.lua (metamethod): ...to here; and... + * lib/std/table.lua (metamethod): ...re-export from here. + Break dependency on `std.functional`. + Adjust all callers. + * NEWS: Update. + + refactor: move clone and clone_rename back into std.table. + * lib/std/base.lua (clone, clone_rename): Move from here... + * lib/std/table.lua (clone, clone_rename): ...to here. + + refactor: break std.container dependency on std.base. + * lib/std/container.lua (instantiate): New function; a faster + equivalent to `merge (clone (proto), t or {})`. + Adjust all callers. + + specs: fix a garbage-in, garbage-out example. + Close #44. + * specs/tree_spec.yaml (tostring): The assumption that tostring + should recurse by itself, or that the Tree constructor should + massage subtables on instantiation were both flawed. Fix the + input to be properly nested tree, and `tostring` will indeed + output a properly nested tree. + +2014-04-25 Gary V. Vaughan + + docs: add missing doc for nometa arg of merge_select. + * lib/std/table.lua (merge_select): Add missing nometa doc. + + table: add merge_select, and support map and nometa args to merge. + Close #56. + * specs/table_spec.yaml (extend_base): Add merge_select. + (merge): Specify behaviour of new `map` and `nometa` args. + (merge_select): Specify behaviour of new `merge_select` api. + (clone, clone_select): Refactor for clarity and orthogonality. + * lib/std/base.lua (merge): Rewrite to support clone-like `map` + and `nometa` parameters, according to improved specifications. + (clone): Rewrite as a call to `merge`. + * lib/std/table.lua (merge_select): New `clone_select` like api + satisfying specs. + (clone_select): Rewrite as a call to `merge_select`. + * NEWS: Update. + +2014-04-23 Gary V. Vaughan + + maint: post-release administrivia. + * configure.ac (AC_INIT): Bump release number to 40. + * NEWS: Add header line for next release. + * .prev-version: Record previous version. + * ./local.mk (old_NEWS_hash): Auto-update. + + Release version 39 + * NEWS: Record release date. + + string: leave global string metatable alone. + Close #30. + * lib/std/string.lua (__append, __concat, __index): Move these + metamethods together at the start of the file, and store them in + the returned module table rather than setting them in the global + string metatable. + Improve header comments, to describe namespace issues. + * lib/std.lua.in: Set string metatable elements from string module + as before. + * specs/string_spec.yaml: Adjust to compensate for changes in + returned string module table. + * NEWS: Update. + + specs: no need to explicitly require spec_helper any more. + Since Specl 11, every spec-file automatically loads the + spec_helper.lua from the same directory, if any. + * specs/container_spec.yaml, specs/debug_spec.yaml, + specs/functional_spec.yaml, specs/io_spec.yaml, + specs/list_spec.yaml, specs/math_spec.yaml, + specs/object_spec.yaml, specs/optparse_spec.yaml, + specs/package_spec.yaml, specs/set_spec.yaml, + specs/strbuf_spec.yaml, specs/string_spec.yaml, + specs/table_spec.yaml, specs/tree_spec.yaml: Remove explicit + `require "spec_helper"`. + + specs: take advantage of improvements to Specl DSL. + * specs/container_spec.yaml, specs/debug_spec.yaml, + specs/functional_spec.yaml, specs/io_spec.yaml, + specs/list_spec.yaml, specs/math_spec.yaml, + specs/object_spec.yaml, specs/optparse_spec.yaml, + specs/package_spec.yaml, specs/set_spec.yaml, + specs/std_spec.yaml, specs/strbuf_spec.yaml, + specs/string_spec.yaml, specs/table_spec.yaml, + specs/tree_spec.yaml: Use `not_to_` instead of `should_not`, + `to_` instead of `should_` and `to_copy` instead of + `should_equal` plus `should_not_be`. + +2014-04-22 Gary V. Vaughan + + maint: update raw urls to new github url scheme. + * README.md: Use new `raw.githubusercontent.com` url scheme + throughout. + + maint: regenerate spec_helper.lua for `make check` if necessary. + * specs/specs.mk (specl-check-local): Add dependency on + specs/spec_helper.lua, so that it is regenerated before the main + `check-local` body is executed if necessary. + + specs: capture stub should return nil to match Specl 12. + * specs/spec_helper.lua.in (capture): Return `nil` for stdout + and stderr to match Specl 12 API. + * specs/table_spec.yaml (clone_rename): Test for `nil`. + + specs: skip deprecation warning expectation when Specl < 12. + * specs/table_spec.yaml (clone_rename): Skip deprecation warning + expectation when the ""-returning stub is being used. + + specs: return empty out and err values from `capture` stub. + * specs/spec_helper.lua.in (table.clone_rename): Return empty + strings for stderr and stdout from fallback stubbed `capture` + implementation. + + specs: elide clone_rename deprecation warning, with Specl 12. + Fix #54. + * specs/spec_helper.lua.in (capture): Stub inprocess.capture if + installed Specl is too old to implement it. + * specs/table_spec.yaml (clone_rename): Wrap clone_rename in + inprocess.capture to elide deprecation warning on stderr. + + refactor: restore alphabetical ordering to table specs. + * specs/table_spec.yaml (clone_rename): Move back into alpha- + betical order. + +2014-04-21 Gary V. Vaughan + + maint: move repository to its own project. + * README.md, configure.ac, rockspec.conf, + * specs/optparse_spec.yaml: Replace rrthomas with lua-stdlib. + + specs: ignore local LUA_INIT and LUA_INIT_5_2 settings. + Fix #53. + * specs/specs.mk (SPECL_ENV): Until the next Specl release is + available, manually reset LUA_INIT and LUA_INIT_5_2. + * bootstrap.conf (buildreq): Add a reminder to clean up after + Specl 12. + +2014-04-21 Reuben Thomas + + Reverse order of arguments to functional.compose; closes #52 + +2014-04-21 Gary V. Vaughan + + tree: return tree root from tree[{}]. + Fix #41. + * specs/tree_spec.yaml (returns tree root for an empty list): New + specification. + * lib/std/tree.lua (__index): Allow empty list as a valid key. + * specs/tree_spec.yaml (std.tree): Remove pending call for + newly passing examples. + * NEWS: Update. + + table: no need to pull all of std.list into memory. + * lib/std/table (elems): Capture base.elems in a local. + (clone_select): Use it. + +2014-04-21 Reuben Thomas + + Modify API of functional.bind, and add spec; closes #48 + + Add a missing require to table.lua + + Add clone_select, closing #50. + + Don’t imply that clone requires its nometa argument to be the literal `true`, thereby blessing our use of string "nometa" argument. + + Remove comment that gives a false impression that the behaviour is there to support backwards-compatibility, whereas in fact it’s necessary for the current API. + +2014-04-21 Gary V. Vaughan + + container: discard unmapped positional parameters. + Fix #35. + * lib/std/container.lua (mapfields): When `map` argument is + given, discard unmapped positional parameters. + (metatable._init): Remove to imply a `nil` value (copy all + fields), because `{}` now discards all positional parameters. + (metatable.__call): Reverse type check of `_init`, so that + mapfields is still called when _init is `nil`. + * specs/object_spec.yaml (std.object): Remove pending call from + newly passing examples. + * NEWS: Update. + + tree: always return nil for non-existent key path. + Fix #39. + * lib/std/functional.lua (function.op["[]"]): Return nil instead + of dereferencing a nil valued table argument. + * specs/tree_spec.yaml (std.tree): Remove pending call prior to + newly passing example. + + functional: handle false valued elements correctly in `map`. + Fix #34. + * lib/std/functional.lua (map): Also insert `false` values into + the results table. + * specs/list_spec.yaml (std.list): Remove pending call from newly + passing example. + +2014-04-16 Gary V. Vaughan + + table: fold `clone_rename` into `clone`. + * specs/table_spec.yaml (std.table): Specify new behaviour with + optional `map` argument. + * lib/std/base.lua (clone): Implement new behaviours. + * lib/std/table.lua (clone_rename): Add deprecation warning, and + move out of the LDoc export table. + (clone): Update doc-comments. + * NEWS: Update. + + base: provide a deprecation mechanism for gentle API upgrades. + * specs/base_spec.yaml: New file. Specify behaviour for a + deprecation wrapper. + * specs/specs.mk (specl_SPECS): Add base_spec.yaml. + * lib/std/base.lua (deprecate): New function. Implement the + specification. + + maint: remove trailing whitespace in NEWS. + * NEWS: Remove trailing whitespace. + + specs: set package.path for in tree specl calls. + Specl 11 works well when called directly in a project root dir, + eg. `specl -freport -v1`, but only if the package path is set + appropriately in each `spec_helper.lua` or similar. + * specs/spec_helper.lua.in: Use `package.normalize` to safely + inject ./lib/?.lua into the head of package.path. + +2014-04-15 Reuben Thomas + + Fix a comment typo + + Fix a comment typo + +2014-04-15 Gary V. Vaughan + + slingshot: sync with upstream. + * slingshot: Sync with latest upstream master. + * bootstrap: Update from slingshot. + * .travis.yml: Regenerate. + + specs: update custom matchers for Specl 11. + * bootstrap.conf (buildreq): Bump specl minimum version to 11. + * specs/spec_helper.lua (matchers.have_size, matchers.have_member): + Matcher object functions require an initial `self` parameter. + (matchers.fail_with): Remove. + + specs: fix a typo; there is no 'a' in should_output. + * specs/optparse_spec.yaml: Fix a typo. + +2014-04-14 Reuben Thomas + + Fix a comment typo + + Fix documentation of table.clone_rename: the parameters got swapped during the earlier re-org + +2014-02-06 Gary V. Vaughan + + package: new path management apis. + * specs/package_spec.yaml: Specify behaviour of new apis. + * lib/std/io.lua (pathsep): Recalculate rather than introduce a + require loop with std.package. + * lib/std/package.lua (find, insert, mappath, normalize, remove): + New functions for maintaining clean pathstrings. + * NEWS: Update. + + functional: new `case` module function. + * specs/functional_spec.yaml: Add specifications for a `case` + function. + * lib/std/functional.lua (case): New function. + * NEWS: Update. + +2014-02-05 Gary V. Vaughan + + optparse: simplify default option setting. + * specs/optparse_spec.yaml: Specify behaviour of new option + defaults parameter. + * lib/std/optparse.lua (parser): Add an optional default options + parameter. + * NEWS: Update. + +2014-02-03 Gary V. Vaughan + + configury: use a sentinel file for multi-target rules. + With thanks to + http://stackoverflow.com/questions/17216048/parallel-gnu-make-and-rules-that-create-several-targets-at-once + * local.mk (ldoc_DEPS): Remove. + $(dist_doc_DATA) $(dist_classes_DATA) $(dist_modules_DATA): + Depend on sentinel file. + ($(srcdir)/doc): Make and populate doc tree atomically for + parallel make. + + configury: be nicer to parallel make. + * local.mk ($(srcdir)/doc): Factor out of... + ($(dist_doc_DATA)): ...here. + (ldoc_DEPS): Add $(srcdir)/doc. + +2014-02-01 Gary V. Vaughan + + optparse: fix a parse error with unhandled opt in combined options. + * specs/optparse_spec.yaml: Add specifications for behaviour + when encountering unhandled options in various circumstances. + * lib/std/optparse.lua (normalise): Meet specifications. + * NEWS: Update. + + specs: remove last vestiges of optparse's specl origins. + * specs/optparse_spec.yaml (std.optparse): Update category, bug + address, and copyright to be stdlib appropriate. + +2014-01-30 Gary V. Vaughan + + maint: post-release administrivia. + * configure.ac (AC_INIT): Bump release number to 39. + * NEWS: Add header line for next release. + * .prev-version: Record previous version. + * ./local.mk (old_NEWS_hash): Auto-update. + + Release version 38 + * NEWS: Record release date. + + specs: commit new have_size matcher. + * specs/spec_helper.lua.in (matchers.have_size): New matcher + used by std.string.split specs. + * configure.ac (AC_CONFIG_FILES): Remove write permission from + generated spec_helper so I don't edit the wrong file next time. + + specs: remove trailing whitespace. + * specs/optparse_spec.yaml: Remove trailing whitespace. + + NEWS: reword a doubled 'to' to satisfy sanity checks. + * NEWS: Reword a doubled 'to'. + + string: split now splits on consecutive whitespace by default. + * lib/std/string.lua (split): Improve LDocs. + Remove @todo. We now have whitespace default split pattern unless + otherwise specified, just like Python. I didn't consider Perl, + because my brain starts to haemorrhage whenever I do that. :-p + * specs/string_spec.yaml (split): Remove spec for string `sep` + argument now that it is optional. + * NEWS: Update. + + string: simplify and speed up `std.string.split`. + * lib/std/string.lua (split): It's a bit more code, but it is + much easier to understand, and it's a lot faster now! + * specs/string_spec.yaml (split): Uncomment pending infinite loop + fix. + * NEWS: Update. + + specs: more comprehensive specs for std.string.split. + * specs/string_spec.yaml (split): Add a lot more specs. + Empty separator causes an infinite loop with the current + implementation. + +2014-01-28 Gary V. Vaughan + + specs: use Specl 10 `a_permutation_of` adaptor for tighter specs. + * specs/tree_spec.yaml: Use `a_permutation_of` instead of + `all_of` to enforce some reordering of only the specified + elements. + + specs: use a custom matcher to specify process status and error. + * specs/spec_helper.lua.in (matchers.fail_with): Custom matcher + to specify process non-zero exit as well as stderr content. + * specs/io_spec.yaml (io.die), specs/optparse_spec.yaml (io.die): + Use it to ensure that in addition to the correct error output, + the exit status is non-zero. + + optparse: integrate with `io.die` and `io.warn`. + * specs/io_spec.yaml: Additional specs for what to do when + `opts.program` and/or `opts.line` are set. + * specs/optparse_spec.yaml (io.die, io.warn): New specs how to + behave when `io.die` or `io.warn` are called after a successful + `parser:parse` call. + * lib/std/io.lua (warn): Implement specified behaviour for new + `opts.line` and `opts.warn` specs. + * lib/std/optparse.lua: Add LDocs for new behaviour. + (parser:parse): Set a metatable on the returned opts table so + than `opts.program` etc. can be found by `io.die` and `io.warn`. + * NEWS: Update. + + io: fix actual and latent bugs in `io.die` and `io.warn`. + * specs/spec_helper.lua.in (luaproc): Factor out of + `tabulate_output`. + (tabulate_output): Adjust. + * specs/io_spec.lua (die, warn): Add new behaviour examples. + * lib/std/io.lua (warn): Adjust to work properly with new specs. + * NEWS: Update. + +2014-01-23 Gary V. Vaughan + + container: inline calls to `empty`. + * lib/std/container (mapfields, metatable._init): Replace + `not empty` with `next`. + (empty): Remove. + + container: drastically simplify and speed up `prototype`. + * lib/std/container.lua (prototype): Inline metaentry, and + simplify. + (metatable.__tostring): Use it. + (metaentry): Remove. + * NEWS: Update. + + container: inline `filter` to only caller. + * lib/std/container.lua (filter): Remove from here. + (metatable.__totable): Inline `filter` here. + + container: cleanup, speedup and bonus bugfix. + * lib/std/container.lua (mapfields): Factored out of + `metatable.__call` and optimised for speed. + (metatable.__call): Adjust accordingly. + (empty): Copied from table.empty to avoind introducing a require + loop. + (modulefunction): Wrap argument in a functable for easily + recognising what not to copy during cloning. + (metatable): Simplify. + * lib/std/object.lua: Import mapfields from Containec, but unwrap + it along with Container.prototype before saving in the Object + metatable for faster access. + Add appropriate LDocs. + * NEWS: Update. + + container: distinguish between the two `functions` tables. + One is the core of Container with metatable as its metatable; the + other is metatable._functions, and is just a list of unpropagated + method functions. We don't want to give the latter a __tostring + metamethod or printing gets very weird. + * lib/std/container.lua (metatable._functions): Make a nometa + clone of the table we return. + * NEWS: Update. + +2014-01-21 Gary V. Vaughan + + configury: update rockspecs in buildreq. + * bootstrap.conf (buildreq): LDoc is now available in the required + version from the official luarocks repo. + + travis: remove unrelease ldoc workarounds. + * .travis.yml: Regenerate, to delete local patches for LDoc. + + doc: vastly improve optparse LDocs. + * lib/std/optparse.lua: Vastly improve optparse LDocs. + * NEWS: Update. + +2014-01-20 Reuben Thomas + + optparse.lua: move "default" options to bottom of help + A more standard position. + +2014-01-19 Gary V. Vaughan + + maint: post-release administrivia. + * configure: Bump revision to 38. + * NEWS: Add header line for next release. + * .prev-version: Record previous version. + * ./local.mk (old_NEWS_hash): Auto-update. + + Release version 37 + * NEWS: Record release date. + + doc: keep config.ld out of doc/ for LuaRocks docdir install. + * doc/config.ld.in: Move from here... + * build-aux/config.ld.in: ...to here. + * configure.a (AC_CONFIG_FILES): Adjust. + * local.mk ($(dist_lua_DATA)): Adjust. + * NEWS: Update. + + refactor: rearrange methods and functions for backwards compatibility. + * lib/std/list.lua: Make use of `_function` table to reinstate + backwards compatible module functions. + + maint: retract release 36. + Release 36 intentionally, but unnecessarily, broke backwards + compatibility in list, set, strbuf and tree. Reinstate + compatibility with v35 and earlier where possible. + * lib/std/list.lua (list.filter, list.foldl, list.foldr) + (list.indexKey, list.indexValue, list.map, list.mapWith) + (list.new, list.project, list.shape, list.zipWith): Accept + parameters in the original v35 order. + * lib/std/set.lua, lib/std/strbuf.lua, lib/std/tree.lua (new): + Reinstate for undocumented backwards compatibility. + * News: Update. + + object: share metatables more aggressively. + * lib/std/container.lua (clone): Don't create a new metatable + unnecessarily just because the base object has a `_function` + element -- which is not copied in any case. + + string: use new syntax for List instantiation. + * lib/std/string.lua: List is an Object now. + (split): Use `List` instead of `list`. + (require_version): Use new syntax for List instantiation. + + Revert "maint: post-release administrivia." + This reverts commit 846fd4e578cf8a57c8268979c9fbd2495b0e7b5b. + +2014-01-17 Gary V. Vaughan + + maint: post-release administrivia. + * configure.ac (AC_INIT): Bump version to 38. + * NEWS: Add header line for next release. + * .prev-version: Record previous version. + * ./local.mk (old_NEWS_hash): Auto-update. + + Release version 37 + * NEWS: Record release date. + + specs: make sure hell.spawn uses the same Lua as Specl examples. + * slingshot: Sync with upstream, for build-aux/specs.mk fix. + * configure.ac (AC_CONFIG_FILES): Add specs/spec_helper.lua. + * specs/spec_helper.lua: Move from here... + * specs/spec_helper.lua.in: ...to here. + * .gitignore: Add spec_helper.lua. + * specs/specs.mk (SPECL_ENV): Add $builddir/specs to LUA_PATH + for generated spec_helper.lua. + * specs/spec_helper.lua.in (LUA): Don't dig through `_G.arg`, + wait for configure substitution. + + specs: remove duplicate examples. + * specs/math_spec.yaml: forgot to remove the old namespace + corruption tests from this file when adding new check in 3507e84. + + specs: compatibility with Specl < 11. + * specs/specs.mk (specl_SPECS): Move std_spec.yaml to the end of + the list, where symbol leaks don't affect subsequent examples. + + specs: compensate for table.pack differences between Lua 5.1/5.2. + * specs/table_spec (before): Make sure `extend_base` and + `enhance_base` reflect whether core table library has a `pack` + entry. + + std: lazy load submodules on demand. + * specs/std_spec.yaml: New specifications for lazy loading. + * specs/specs.mk (specl_SPECS): Add specs/std_spec.yaml. + * lib/std.lua.in (std.__index): Implement it. + * NEWS: Update. + + doc: add the package name and version to html doc page titles. + * doc/config.ld: Move from here... + * doc/config.ld.in: ...to here. Add a title setting incorporating + @PACKAGE@ and @VERSION@. + * configure.ac (AC_CONFIG_FILES): Add doc/config.ld. + * .gitignore: Add doc/config.ld. + Suggested by Dirk Laurie + + maint: plug symbol leaks with working specifications. + * specs/spec_helper.lua (show_apis): New function. Compare table + entries in a sub-process, to support tracking namespace changes + when requiring modules by various means. + * specs/functional_spec.yaml: New file. Use it to ensure + "std.functional" doesn't leak symbols. + * specs/debug_spec.yaml, specs/functional_spec.yaml, + specs/io_spec.yaml, specs/list_spec.yaml, specs/math_spec.yaml, + specs/object_spec.yaml, specs/optparse_spec.yaml, + specs/package_spec.yaml, specs/set_spec.yaml, + specs/spec_helper.lua, specs/specs.mk, specs/strbuf_spec.yaml, + specs/string_spec.yaml, specs/table_spec.yaml, + specs/tree_spec.yaml: Rewrite specs that check symbol leaks + using show_apis(). + * lib/std/base.lua (new): Don't forget the forward declaration. + * lib/std/set.lua (proper_subset): Likewise. + * NEWS: Update. + Reported by Dirk Laurie + + rockspecs: update detailed description text. + * rockspec.conf (description.detailed): Remove mention of regexps + and getopt. Add mention of civilised option parsing. + + maint: cosmetic fix to imported module list. + * lib/std/modules.lua: Replace `getopt` with `optparse`. + + maint: workaround a luarocks bug handling debug_init.lua. + LuaRocks misinstalls build.modules entries ending in `init.lua`, + so rearrange the source tree and Automake rules so that + `require "std.debug_init"` still works as before even though + the file ends up in the wrong directory after installation. + * lib/std/debug_init.lua: Move from here... + * lib/std/debug_init/init.lua: ...to here. + * slingshot: upgrade for `.../init.lua` handling in mkrockspecs. + * local.mk (dist_luastd_DATA): Remove lib/std/debug_init.lua. + (dist_luastddebug_DATA): New installation dir. Add + lib/std/debug_init/init.lua. + + optparse: replace getopt with an easier to use option parser. + * specs/optparse_spec.yaml: Specify behaviour for a civilised + option parsing API. + * lib/std/optparse.lua: New module. + * lib/std/getopt.lua: Remove. + * doc/config.ld (file), local.mk (dist_luastd_DATA): Adjust. + * specs/getopt_spec.yaml: Remove. + * local.mk (dist_modules_DATA, dist_classes_DATA): Adjust. + * specs/specs.mk (specl_SPECS): Likewise. + * build-aux/sanity-cfg.mk: New file. Don't fail sanity checks on + account of 'OptionParser' in optparse.lua error message. + * NEWS: Update. + +2014-01-17 Reuben Thomas + + string.lua: fix a missing close paren in a docstring + +2014-01-16 Gary V. Vaughan + + slingshot: resync for `mkrockspecs --repository` support. + * slingshot: Sync with upstream. + * local.mk (mkrockspecs_args): Add `--repository lua-stdlib`. + * .travis.yml: Regenerate. + +2014-01-15 Gary V. Vaughan + + slingshot: sync with upstream, for bootstrap git detection fix. + * slingshot: Sync with upstream. + * bootstrap: Sync with slingshot. + + maint: post-release administrivia. + * NEWS: Add header line for next release. + * configure.ac: Bump version number to 37. + * .prev-version: Record previous version. + * ./local.mk (old_NEWS_hash): Auto-update. + + Release version 36 + * NEWS: Record release date. + +2014-01-13 Gary V. Vaughan + + configury: use explicit lists instead of $(wildcard ...). + * local.mk (dist_classes_DATA, dist_modules_DATA): Only GNU make + supports $(wildcard ...) expressions, and Automake complains + every time it sees one. List files explicitly. + ($(dist_doc_DATA)): Add $(dist_classes_DATA) and + $(dist_modules_DATA) to LHS of this rule so that make knows it + has to run LDoc to create those files. + + slingshot: resync for subdirectory bootstrap fix. + * slingshot: Sync with upstream. + + slingshot: sync with upstream, for rockspecs in $buildreq. + * slingshot: Sync with upstream. + * bootstrap: Sync with slingshot. + * configure.ac (SPECL_MIN): Remove. + * bootstrap.conf (buildreq): Add ldoc and specl rockspecs. + * .travis.yml: Regenerate, but manually splice in the LDoc-1.4.0 + luarocks install while waiting for a luarocks release. + +2014-01-05 Gary V. Vaughan + + slingshot: sync with upstream. + Fix the annoying contest.sed file dropping bug. + * slingshot: Sync with upstream. + * bootstrap: Sync with slingshot. + + doc: distribute ldoc classes and modules documentation. + * local.mk (filesdir, dist_files_DATA): Rename from these... + (classesdir, dist_classes_DATA): ...to these. + (dist_classes_DATA, dist_modules_DATA): Use correct paths in + gmake wildcard expression. + +2014-01-04 Gary V. Vaughan + + maint: update copyright notices to include 2014. + * .x-update-copyright: New file. Exclude files not owned by this + project from update-copyright rules. + * boootstrap.conf, configure.ac, local.mk: Bump copyright year. + + travis: horrid workaround for crashy LDoc master vs apt-get luarocks. + * slingshot: Sync for Travis fixes. + * .travis.yml: Temporary code to fetch a custom stable version of + LDoc that doesn't crash on stdlib doc-comments. + + maint: fix copyright attribution in COPYING. + * COPYING: Correct year and authors in copyright statement. + + slingshot: sync with upstream and simplify accordingly. + * slingshot: Sync with upstream. + * bootstrap: Update from slingshot. + * configure.ac (AM_INIT_AUTOMAKE): Remove 'foreign'. Slingshot + now handles missing README automatically. + * bootstrap.slingshot: Remove. No longer required. + * bootstrap.conf: Remove bootstrap.slingshot source boilerplate. + (stdlib_force_changelog): Remove. Automated by slingshot now. + * INSTALL: Remove. Autotools copies in the canonical version + automatically. + * COPYING: New file, with MIT License, to avoid Autotools + copying over a standard GPLv3 COPYING boilerplate. + * .gitignore: Update. + + slingshot: sync with upstream, and update copyright years. + * slingshot: Update, particularly for update-copyright support. + * bootstrap, bootstrap.slingshot: Update from slingshot. + +2014-01-01 Gary V. Vaughan + + slingshot: sync with upstream. + * slingshot: Sync with latest upstream, particularly for Travis fixes. + * .travis.yml: Regenerate. Plus temporary patch to run gvvaughan/next + branch of LDoc during integration until stevedonovan/master is fixed. + +2013-12-11 Gary V. Vaughan + + tree: derive from std.container. + * specs/tree_spec.yaml: Replace object methods with module + functions throughout. + Add specs for inheritted functionality. + * lib/std/tree.lua: Rewrite as a derivative of std.container. + Change object methods to Container style module functions. + * NEWS: Update. + + doc: update object constructor _function documentation. + * lib/std/container.lua: Update module doc-comment to reflect + simplification of passing module functions in _functions table. + (std.container): Update _function description. + * lib/std/object.lua (std.object): Likewise. + * lib/std/set.lua (std.set): Likewise. + + specs: improve expectation description. + * specs/set_spec.yaml: Write "shows the type name" instead of + "contains the type". + +2013-12-10 Gary V. Vaughan + + refactor: automatically fill method functions form _functions table. + * lib/std/container.lua (metatable.__call): Copy entries from + newly passed _functions table to object. + * lib/std/set.lua (Set): Delete manual copy of method functions. + + set: derive from std.container, keys no longer clash with method names. + * specs/set_spec.yaml: Replace object methods with prototype + functions through out. + Check that set elements with the same name as a prototype + function behave properly. + * lib/std/set.lua: Rewrite as a derivative of std.container. + Change object methods to Container style prototype functions. + * NEWS: Update. + + list: remove trailing whitespace. + * lib/std/list.lua: Remove trailing whitespace. + + container: new module for objects that use [] to access contents. + * specs/container_spec.yaml: Specify behaviour of Container + objects. + * specs/specs.mk (specl_SPECS): Add specs/container_spec.yaml. + * specs/object_spec.yaml: Remove duplicate tests. + * doc/config.ld (file), local.mk (dist_luastd_DATA): Add + lib/std/container.lua. + * lib/std/container.lua: New file. Implement Container objects. + They have no methods, so that __index can be used for accessing + contents instead. + * lib/std/object.lua: Vastly improved doc-comments. + Simplify drastically in light of Container implementation. + * NEWS: Update. + +2013-12-09 Gary V. Vaughan + + Revert "maint: move lua extension sources to $top_srcdir/ext." + This reverts commit 4d51ca63a1eedd3759201db8b3df850a658856e6. + + I have no idea why I thought moving Lua libraries from the lib + tree to the Lua C extensions ext tree seemed like a good idea. + Undo the damage. + * ext/: Rename back to lib/. + * local.mk (std_path, dist_lua_DATA, dist_luastd_DATA) + (mkrockspecs_args, EXTRA_DIST, dist_files_DATA) + (dist_modules_DATA): Adjust. + * doc/config.ld (file): Adjust. + * .gitignore: Update. + +2013-12-09 Gary V. Vaughan + + table: add string support to totable function. + * ext/std/table.lua (totable): When passed a string, return a + table of each character in the string. + * NEWS: Update. + + specs: simplify the tree merge specs. + * specs/tree_spec.yaml (tree merge): comparing non-leaf nodes + is not a fair test, better to set k3 to a different string + that can be tested before and after the merge without worrying + about whether a table node is coerced to a tree node. + +2013-11-28 Gary V. Vaughan + + specs: don't assume in order traversal by tree.nodes. + * specs/tree_spec.yaml (node): Use `should_contain.all_of` + in place of `should_equal` to avoid enforcing a particular + traversal order. + + tree: fix broken tests, and improve doc-comments (Fixes #40) + * specs/tree_spec.yaml (inodes, nodes): Be sure to clone iterator + returned paths before storing. Some corrections to the expected + results. + Remove pending "see issue #40" instances. + (merge): Fix tests. Remove pending "see issue #40" instances. + * ext/std/tree.lua (nodes): Much improved doc-comment. + (merge): Assert the arguments are actual tree nodes, because + raw tables do not work! + + doc: update doc-comments for latest LDoc master. + * doc/config.ld (metamethod): Delete. Metamethods are now + separated out automatically. + * ext/std/list.lua: Describe use of object methods as module + functions. + Update doc-comments. + * ext/std/object.lua, ext/std/set.lua, ext/std/strbuf.lua, + ext/std/strict.lua, ext/std/tree.lua: Update doc-comments. + +2013-11-27 Gary V. Vaughan + + tree: add specl specs, and fix simple API inconsistencies. + * specs/tree_spec.yaml: New file. Examples of how tree APIs + should behave. + * specs/specs.mk (specl_SPECS): Add specs/tree_spec.yaml. + * ext/std/base.lua (ileaves, leaves): Report non-table arguments. + * ext/std/tree.lua (clone, inodes, nodes, merge): Likewise. + + string: report assert failures from assert caller level. + * ext/std/string.lua (assert): Call error with level 2, to report + errors from the assert caller not assert itself. + + tree: fix argument order for list.foldl call. + The argument order for list.foldl changed in 9fe27bb, but one + call site wasn't updated to match. + * ext/std/tree.lua (metatable.__index): Use new argument order + for list.foldl call. + +2013-11-26 Reuben Thomas + + Remove some spurious comment markers in docstrings. + These were introduced by commit 31b314c835 converting doc-comments to + LDoc 1.4. + +2013-11-26 Gary V. Vaughan + + travis: replace recently missing links for libyaml + Rerunning previously passing integration test shows that the + multilibbed libyaml libraries are no longer available from + /usr/lib alongside other libs - maybe an OS upgrade, or an + upstream packaging error? + * .travis.yml (install): Remove libyaml-dev reinstall. + Run a shell find command to link the multilib libyaml libs + back into /usr/lib. If this command starts failing in future + then it should mean libyaml has moved back to the proper place + and we're failing to relink it manually, so this workaround + can be removed. + + maint: install libyaml-dev for Travis. + Seems recent tests have been failing because libyaml-dev is no + longer installed by default. + * .travis.yml (install): Add libyaml-dev which pulls in libyaml. + + doc: distinguish object and class methods in object doc-comments. + * ext/std/object.lua (clone): Rename first argument to self, and + remove associated @param as a hint to LDoc. + (stringify, totable): Likewise. + (metatable): Document important fields. + (metatable.__call): Add documentation. + + doc: use a more topographical ordering for document sidebar. + Now that the latest LDoc doesn't enforce strict alphabetical + ordering, reorder the documentation into a more topographical + order for easier reading. + * doc/config.ld (file): Reorder for clarity. + + doc: separate camelCaseCompat methods to avoid LDoc warnings. + Using the @export feature of LDoc provides the flexibility to + change the exported functions from a single table, but we also + have to be careful not to sweep up undocumented access points + in the same table, otherwise LDoc correctly warns us that those + functions have no doc-comments. + * ext/std/getopt.lua (getOpt, processArgs, usageInfo): Move + to a separate table, and merge back into the module table before + return. + * ext/std/io.lua (processFiles): Likewise. + * ext/std/list.lua (indexKey, indexValue, mapWith, zipWith): + Likewise. + * ext/std/string.lua (escapePattern, escapeShell, ordinalSuffix): + Likewise. + + doc: markup std.string.escape_string correctly for LDoc. + * ext/std/string.lua (escape_string): Add missing leading hyphen. + + doc: LDoc master now handles local references differently. + * .travis.yml (install): Switch to github master rockspec. + * ext/std/list.lua (enpair, depair, __concat, __add): Adjust + @see argument. + * ext/std/object.lua (__totable, __tostring): Likewise. + * ext/std/set.lua (__add, __sub, __mul, __div, __le, __lt): + Likewise. + * ext/std/strbuf.lua (__concat, __tostring): Likewise. + +2013-11-18 Gary V. Vaughan + + doc: point to updated online LDoc documentation. + * README.md (Documentation): Refer to latest LDoc docs. + + maint: remove last references to luadoc. + * local.mk (dist_doc_DATA): Remove luadoc.css. Add ldoc.css. + + travis: use unofficial ldoc 1.4.0 release candidate. + We're using features from the release candidate already, so + temporarily point the travis installer to that rockspec while + waiting for an official release. + * .travis.yml: Adjust ldoc rockspec location. + + list: fix broken specs. + * ext/std/list.lua (depair): Needs to be the inverse operation of + enpair, which requires returning a bare table, and not a List + object. + + .gitignore: ignore ldoc output, not luadoc output. + * .gitignore: Remove /doc/luadoc.css. Add /doc/ldoc.css. + + getopt: revert an accidental comment deletion. + * ext/std/getopt.lua (makeOptions): Revert accidental deletion. + + doc: convert doc-comments to LDoc 1.4. + * doc/config.ld: New file. Configuration for LDoc. + * local.mk (EXTRA_DIST): Add it. + (dist_doc_DATA): Remove Luadoc files. Add LDoc files. + ($(dist_doc_DATA)): Invoke LDoc. + * configure.ac (SS_CONFIG_TRAVIS): Request ldoc instead of luadoc. + * .travis.yml: Regenerate. + * .gitignore: Adjust. + * ext/std.lua.in, ext/std/base.lua, ext/std/debug.lua, + ext/std/functional.lua, ext/std/getopt.lua, ext/std/io.lua, + ext/std/list.lua, ext/std/math.lua, ext/std/modules.lua, + ext/std/object.lua, ext/std/package.lua, ext/std/set.lua, + ext/std/strbuf.lua, ext/std/strict.lua, ext/std/string.lua, + ext/std/table.lua, ext/std/tree.lua: Convert doc-comment syntax to + richer LDoc 1.4 format. + +2013-11-15 Gary V. Vaughan + + maint: move lua extension sources to $top_srcdir/ext. + For consistency with other Lua projects I maintain, rename the + lib directory to ext. + * lib/: Rename to ext/. + * local.mk (std_path, dist_lua_DATA, dist_luastd_DATA) + (mkrockspecs_args, EXTRA_DIST, dist_doc_DATA, dist_files_DATA) + (dist_modules_DATA): Adjust. + * .gitignore: Update. + + bootstrap: drop 'lua-' prefix from project name. + * configure.ac (AC_INIT): Set name to 'stdlib'. + * .travis.yml: Regenerate. + +2013-09-15 Gary V. Vaughan + + slingshot: sync with upstream. + * slingshot: Update to latest revision, particularly to get the + fix for release announcement content. + * bootstrap: Upgrade from latest slingshot. + * .travis.yml: Regenerate. + +2013-06-27 Gary V. Vaughan + + list: std.list returns the List prototype object. + Removing the separation between List prototype and the module + table that used to wrap it, for consistency with the other + Object specialisations. We break backwards compatibilitf here + though because the argument ordering used to be different for + several functions depending on whether they were called through + the module table, or as List methods. The List method ordering + (i.e. list argument first) wins out here, because that is a + prerequisite of being able to call object methods with the Lua + : syntax. + * specs/list_spec.yaml: Remove examples of constructing a List + from the module table, and with the `new` command. + * lib/std/list.lua: Much simplified by removal of the module + table. + (filter, foldl, foldr, index_key, index_value, map, map_with) + (project, shape, zip_with): The list operand is always first + now, whether called with a `.' or a `:'. + Adjust all callers. + (new): Remove. Adjust all callers. + * NEWS: Update. + + strbuf: std.strbuf returns the strbuf prototype object. + Fix some latent bugs caused by the artificial separation between + the metatable of strbuf and strbuf.StrBuf, by removing the + distinction between the two. `require "std.strbuf"` returns the + actual StrBuf object, ready to go! + * specs/strbuf_spec.yaml: Remove examples of constructing a StrBuf + from the module table, and with the `new` command. + * lib/std/strbuf.lua: No need to wrap the StrBuf prototype in an + additional layer of abstraction to return a module table containing + the StrBuf prototype. Just return StrBuf directly. + (strbuf.new): Remove. + Adjust all callers. + + set: std.set returns the Set prototype object. + Fix some latent bugs caused by the artificial separation between + the metatable of set and set.Set, by removing the distinction + between the two. `require "std.set" returns the actual Set object, + ready to go! + * specs/set_spec.yaml: remove examples of constructing a set from + the module table, and with the `new` command. + * lib/std/set.lua: No need to wrap the Set prototype in an + additional layer of abstraction to return a module table containing + the Set prototype. Just return Set directly. + (set.new): Remove. + Adjust all callers. + + object: std.object returns the root object. + Fix some latent bugs caused by the artificial separation between + the metatable of object and object.Object, by removing the + distinction between the two. `require "std.object"` returns the + actual root object, ready to go! + * specs/object_spec.yaml: Remove examples of constructing an + object from the object module table and the `new` method. + * lib/std/object.lua: No need to wrap the root object in an + additional layer of abstraction to return a module table containing + the root object. Just return the root object directly. + (object.new): Remove. + Adjust all callers. + * NEWS: Update. + + object: don't assume object metatable __index is always a table. + * lib/std/object.lua (clone): Only merge new object metatable + methods when both metatables have a table type __index entry. + (metatable:__call): Let Lua do the lookup for an object clone + function, incase __index is a function. + + object: be consistent with various object constructors. + * specs/object_spec.yaml: Specify behaviour for Object:clone {} + and object.new () constructors, with single table initialiser and + parameter list respectively, plus the Object {} and object () + syntactic sugars for each. + * specs/list_spec.yaml, specs/set_spec.yaml, + specs/strbuf_spec.yaml: Likewise for other objects to make sure + all are consistent. + * lib/std/list.lua, lib/std/set.lua, lib/std/strbuf.lua: Implement + the specified behaviours. + + object: fix __lt and __le support with shared metatables. + * specs/object_spec.yaml: Specify desired behaviour with new + examples for metatable sharing. + * lib/std/object.lua (metaentry): New helper function. + (object_type, clone, stringify, totable): Factor out of root + object initialiser. + (clone): Maintain separation between "_" prefixed fields, kept + in the new object metatable, and everything else in the object. + If the newly instantiated object requires a new metatable to + support the additional "_" prefixed keys, create it, otherwise + share the prototype's metatable. + Additionally, when creating a new metatable, be sure to merge + in __index methods from the prototype metatable. + (Object): Much simplified! + (metatable): Set type, methods and metamethods for root object. + * lib/std/list.lua (methods, metalist, new): Refactored from + these... + (List, new): ...to these, also simplifed accordingly. + * lib/std/set.lua (M, metaset, new): Refactored from these... + (M, new): ...to this, much simplified. + * specs/list_spec.yaml, specs/set_spec.yaml: Add more examples + to specify metatable propagation behaviour, and refactor for + clarity and consistency. + * NEWS: Update. + + set: share metatable so that __le and __lt work. + * specs/set_spec.yaml: Many more examples, in particular to check + behaviour of __le and __lt comprehensively. + * lib/std/set.lua (metaset): New metatable shared by all sets. + (new): Adjust. + * NEWS: Update. + + travis: regenerate .travis.yml. + * .travis.yml: specl-8 is out, so don't try to work around it + any more! + + specs: update spec_helper for Grand Renaming II™. + * specs/spec_helper.lua: Fix table_ext reference for name change + to straight table. + +2013-05-29 Gary V. Vaughan + + object: __tostring should call __totable metamethod, not method. + * lib/std/object.lua (new.__tostring): Because we have to set a + separate metatable for objects that have __lt or __le metamethods, + self:__totable () is not always the same as getmetatable (self). + __totable (self). Use the longhand so that __tostring calls the + right function in both cases. + +2013-05-22 Gary V. Vaughan + + travis: use specl from git until specl 8 is released. + * .travis.yml: Fetch the rockspec from github. Install it. + + list: derive from `std.object`. + * specs/list_spec.yaml: Describe expected behaviour of list as an + Object. + * specs/specs.mk (specl_SPECS): Add specs/list_spec.yaml. + * lib/std/list.lua (metalist): __lt and __le metamethods only + work if both objects share the same metatable... this one for + list objects. + (new): Return a new Object, with metalist as the metatable, + and _clone doctored to propagate the metalist metatable. + (M): Return a callable table that forwards module calls to the + `new` function. + * NEWS: Update. + + object: be sure to run tostring on array elements before table.concat. + * lib/std/object.lua (new.__tostring): table.concat requires that + all elements already be strings, so call tostring on the array + part of the object table before passing to table.concat. + + object: respond to tostring() with stringified table of non-hidden fields. + Rather than add individual __tostring metamethods to every object + type, implement a general stringification algorithm in the base + object, to return the type followed by the array part of the object, + and then the dictionary part of the object. + * specs/object_spec.yaml (__tostring): Describe desired behaviour. + * specs/set_spec.yaml (__tostring): Likewise. + * lib/std/object.lua: Don't overwrite core table functions with + std.base. Adjust all callers. + (new.__tostring): return stringification of non-hidden fields. + * lib/std/set.lua (new.__tostring): Remove. Inheritted base object + metamethod is equivalent. + + configury: specl 8 required for stdlib compatible objects. + * configure.ac (SPECL_MIN): Bump to Specl 8. Earlier versions use + an incompatible object system, which crash when fed recently + enhanced stdlib objects. + + string: prettyprint valid symbol keys without spurious quoting. + * specs/string_spec.yaml (prettytostring): Remove quoting from + examples with valid symbols as keys. Add an example with some + invalid symbol names as table keys. + * lib/std/string.lua (prettytostring): Skip the key quoting + code unless a key is not a string, or contains any non-word + character. + + string: display prettyprinted table keys in table.sort order. + When comparing actual and expected output in unit tests, or with + Specl matchers, it's quite difficult to write robust examples when + the output of prettyprint is randomised for tables. This commit + ensures that table keys are always output in the same order. + * specs/string_spec.yaml (prettytostring): Examples of desired + behaviour for string.prettytostring. + * lib/std/string.lua (render): Use a sorted list of keys to + determine the order in which they are output. + * NEWS: Update. + + object: __totable and __index support. + Provide object.type, analagously to io.type. + * specs/object_spec.yaml (__totable): Specify desired + behaviour. + * specs/set_spec.yaml (__totable): Likewise. + * specs/strbuf_spec.yaml (__totable): Likewise. + * lib/std/object.lua (typeof): Rename from this... + (M.type): ...to this, to support e.g. object:type (). + (new.__totable): Return a table of non `_` prefixed object + elements. + (new.__index): Defer to methods in M, when the object itself has + none. + * lib/std/getopt.lua (argtype): Set of valid argtype keys. + (getopt): Comparing an Option's type field to nil doesn't work, + because of the Object:type () method it shadows. Instead check + for a value not in the argtype set. + (usageinfo): Reorder type tests to avoid the same Object:type() + problem. + * lib/std/set.lua: Use object.type instead of object.typeof. + * specs/object_spec.yaml, specs/set_spec.yaml, + specs/strbuf_spec.yaml: Adjust. + * NEWS: Update. + + maint: The Grand Renaming II™ - use `require "std.string"` etc. + Now that individual modules are loaded from the std subdirectory, + remove the spurious "_ext" suffix so that `require "std.io_ext"` + is not needed any more. + * lib/std/debug_ext.lua, specs/debug_ext_spec.yaml, + lib/std/io_ext.lua, specs/io_ext_spec.yaml, + lib/std/math_ext.lua, specs/math_ext_spec.yaml, + lib/std/package_ext.lua, specs/package_ext_spec.yaml, + lib/std/string_ext.lua, specs/string_ext_spec.yaml, + lib/std/table_ext.lua, specs/table_ext_spec.yaml: Rename from + these... + * lib/std/debug.lua, specs/debug_spec.yaml, lib/std/io.lua, + specs/io_spec.yaml, lib/std/math.lua, specs/math_spec.yaml, + lib/std/package.lua, specs/package_spec.yaml, lib/std/string.lua, + specs/string_spec.yaml, lib/std/table.lua, specs/table_spec.yaml: + ...to these. Adjust all require calls. + * specs/io_spec.yaml, specs/string_spec.yaml, specs/table_spec.yaml + (extensions): Add some missing apis. + * lib/std.lua.in, lib/std/modules.lua: Adjust and simplify. + * specs/specs.mk (specl_SPECS): Adjust. + * local.mk (dist_luastd_DATA): Adjust. + * NEWS: Update. + + object: don't trigger table metamethods for type lookup. + * lib/std/object.lua (typeof): When looking up the type of an + object table with no _type field, don't use __index metamethod. + +2013-05-19 Gary V. Vaughan + + configury: support non-autotool LuaRocks installation. + * slingshot: Sync with upstream. + * local.mk (mkrockspecs_args): Add --module-dir so that latest + mkrockspecs will use the LuaRocks builtin build.type. + * std/std.lua.in, std/base.lua, std/debug_ext.lua, + std/debug_init.lua, std/functional.lua, std/getopt.lua, + std/io_ext.lua, std/list.lua, std/math_ext.lua, std/modules.lua, + std/oject.lua, std/package_ext.lua, std/set.lua, + std/strbuf.lua, std/strict.lua, std/string_ext.lua, + std/table_ext.lua, std/tree.lua: Move from here... + * lib/std.lua.in, lib/std/base.lua, lib/std/debug_ext.lua, + lib/std/debug_init.lua, lib/std/functional.lua, lib/std/getopt.lua, + lib/std/io_ext.lua, lib/std/list.lua, lib/std/math_ext.lua, + lib/std/modules.lua, lib/std/oject.lua, lib/std/package_ext.lua, + lib/std/set.lua, lib/std/strbuf.lua, lib/std/strict.lua, + lib/std/string_ext.lua, lib/std/table_ext.lua, lib/std/tree.lua: + ...to here. + * local.mk (std_path): Adjust. + * std/.gitignore: Remove. Consolidate into... + * .gitignore: ...here. + * std/std.mk: Remove. Consolidate into... + * local.mk: ...hore. + * NEWS: Update. + +2013-05-16 Gary V. Vaughan + + NEWS: fix poor grammar in set to object entry. + * NEWS: Fix poor grammar in set to object entry. + + object: rename object.Object to object.new for consistency. + * std/object.lua (Object): Rename from this... + (new): ...to this. Adjust all callers. + * specs/object_spec.yaml: Adjust. + * NEWS: Update. + +2013-05-15 Gary V. Vaughan + + set: derive from `std.object.Object`. + * specs/set_spec.yaml: Describe expected behaviour of set as an + Object. + * specs/spec_helper.lua (have_member): New custom Specl matcher. + * specs/specs.mk (specl_SPECS): Add specs/set_spec.yaml. + (EXTRA_DIST): Add specs/spec_helper.lua. + * std/set.lua (delete, insert): Return the modified set. + (difference, intersection, propersubset, subset) + (symmetric_difference, union): Coerce a table in argument 2 to a + set. + (new): Return a new Object, with metamethods rolled in. + (M): Return a callable table that forwards module calls to the + `new` function. + * NEWS: Update. + + strbuf: derive from `std.object.Object`. + * specs/strbuf_spec.yaml: Describe expected behaviour of strbuf as + an Object. + * specs/specs.mk (specl_SPECS): Add specs/strbuf_spec.yaml. + * std/strbuf.lua (new): Return a new Object, with metamethods and + object methods rolled in. + (M): Return a callable table that forwards module calls to the + `new` function. + * NEWS: Update. + +2013-05-14 Gary V. Vaughan + + object: bake in inherited object type names. + * specs/object_spec.yaml: New file. Examples of how typed objects + should behave. + * specs/specs.mk (specl_SPECS): Add specs/object_spec.yaml. + * std/table_ext.lua (clone, clone_rename, merge): Move from here... + * std/base.lua (clone, clone_rename, merge): ...to here. + * std/table_ext.lua (M.clone, M.clone_rename, M.merge): Re-export + implementations from `std.base`. + * std/object.lua (Object._type): Name the `type` of this object. + (typeof): Insepet the type of an object, falling back on system + type for non-objectes. + Return the public interface table, with a `__call`able metatable + that delegates to Object for instantiation. + + configury: bump minimum specl version to release 7. + Specl 6 has a bug in `should_contain` matcher that makes the + package_ext checks fail spuriously. + * configure.ac (SPECL_MIN): Bump to 7. + +2013-05-12 Gary V. Vaughan + + string_ext: make escape_pattern compatible with Lua 5.2. + Lua 5.2 complains of an illegal character if a non-pattern meta + character is preceded by a '%'. + * std/string_ext.lua (escape_pattern): Escape only meta-chars. + * specs/string_ext_spec.yaml (escape_pattern): Adjust example + code to match. + * NEWS: Update. + + refactor: move base.lua methods to more appropriate modules. + Move things around to remove more trampling of _G, and break the + remaining dependency loops, so that modules can all be required + independently. + * std/list.lua (M.append, M.compare, M.concat, M.elems, M.ileaves) + (M.leaves, M.new): Move from here... + * std/base.lua (M.append, M.compare, M.concat, M.elems, M.ileaves) + (M.leaves, M.new): ..to here, for breaking remaining dependency loops. + * std/list.lua (M.append, M.compare, M.concat, M.elems, M.new): + Import from base, and reexport. + * std/base.lua (_G.inodes, _G.nodes): Move from here... + * std/tree.lua (M.inodes, M.nodes): ...to here. + (M.leaves, M.ileaves): Import from base, and reexport. + * std/base.lua (_G.pack, _G.ripairs, _G.totable): Move from here... + * std/table_ext.lua (M.pack, M.ripairs, M.totable): ...to here. + * specs/table_ext_spec.yaml (pack, ripairs, totable): Add pending + example descriptions. + * std/base.lua (_G.metamethod, _G.id, _G.bind, _G.curry, _G.compose) + (_G.memoize, _G.eval, _G.collect, _G.map, _G.filter, _G.fold, _G.op): + Move from here... + * std/functional.lua (M.metamethod, M.id, M.bind, M.curry, M.compose) + (M.memoize, M.eval, M.collect, M.map, M.filter, M.fold, M.op): + New new module. ...to here. + * std/modules.lua: Add std.functional. + * std/std.mk (nobase_dist_lua_DATA): Add std/functional.lua. + * std/base.lua (_G.die, _G.warn): Move from here... + * std/io_ext.lua (M.die, M.warn): ...to here. + * std/std.lua.in: Reexport all recently modularized APIs into _G. + * std/getopt.lua: Explicitly import used modules. + * std/set.lua, std/strbuf.lua: Rearrange definitions to match other + modules. + * std/string_ext.lua: Require std.functional for metamethod(). + (_tostring): Be explicit when saving a handle for _G.tostring. + * NEWS: Update. + + debug_ext: use correct require dependencies. + * std/debug_ext.lua (say): Be explicit about the use of enhanced + string.tostring. + Be sure to require "list" before using its methods. + +2013-05-11 Gary V. Vaughan + + configury: bump release number to 36. + * configure.ac (AC_INIT): Bump release number to 36. + + maint: no need to gitignore files we no longer generate. + * .gitignore: Remove luarocks-config.lua and release-notes-* from + ignore list. + +2013-05-06 Gary V. Vaughan + + slingshot: Update. + * slingshot: Update. + * configure.ac (AC_INIT): Package name has to match gnulib repo- + name for generated rockspecs to find the tag zipballs. + * .gitignore: Move /stdlib-*.tar.gz to /lua-stdlib-*.tar.gz. + * .travis.yml: Regenerate. + + travis: make the lua5.1 hacked luadoc executable. + * slingshot: Upgrade. + * .travis.yml: Regenerate. + + travis: don't install luadocs twice. + * slingshot: Update. + * .travis.yml: Regenerate. + + travis: update slingshot, for better luadoc handling. + * slingshot: Update. + * configure.ac: Adjust. + * .travis.yml: Regenerate. + +2013-05-05 Gary V. Vaughan + + Revert "travis: force luadoc to run only using Lua 5.1." + This reverts commit d54819f8b58988b10f8a298746abc28ce30b41c8. + + travis: force luadoc to run only using Lua 5.1. + * slingshot: Update. + * configure.ac (EXTRA_ROCKS): Ugly hack to inject the interpreter + substitution without requiring a locally modified travis.yml.in. + * .travis.yml: Regenerate. + + slingshot: update. + * slingshot: Update. + * bootstrap.slingshot: Manually update. + * bootstrap.conf (slingshot_files): Add m4/slingshot.m4. + * configure.ac: Adjust. + * .travis.yml: Regenerate. + + travis: install luadoc before running luarocks make. + * configure.ac (EXTRA_ROCKS): Add luadoc. + * .travis.yml: Regenerate. + + slingshot: update. + + travis: regenerate .travis.yml. + * .travis.yml: Regenerate. + + travis: specify EXTRA_ROCKS correctly. + * configure.ac (EXTRA_ROCKS): Add lyaml rock. + * .travis.yml: Regenerate. + + slingshot: use the public slingshot url. + * slingshot: Update. + * bootstrap.slingshot: Sync from upstream. + + maint: post-release administrivia. + * NEWS: Add header line for next release. + * .prev-version: Record previous version. + * ./local.mk (old_NEWS_hash): Auto-update. + + Release version 35 + * NEWS: Record release date. + + slingshot: sync with upstream. + * .gitmodules: Remove. Slingshot regenerates on demand. + * .gitignore: Ignore .gitmodules. + * slingshot: Update. + * bootstrap.slingshot: Copied from slingshot, for clean checkout + bootstrapping before slingshot subproject is populated. + * bootstrap.conf: Adjust. + + maint: collect previous release notes into NEWS. + * NEWS: new file, required for the Slingshot release process. + * local.mk (old_NEWS_hash): Update. + + formatting: fix whitespace errors flagged by slingshot syntax-check. + * INSTALL, specs/debug_ext_spec.yaml: Remove trailing blank lines. + * specs/io_ext_spec.yaml, specs/match_ext_spec.yaml, + specs/table_ext_spec.yaml: Remove trailing spaces. + * std/std.mk: Remove SPACE-TAB sequence. + + configury: adopt slingshot release mechanism. + * GNUmakefile, Makefile.am, build-aux/mkrockspecs, + m4/ax_compare_version.m4, m4/ax_lua.m4, m4/ax_with_prog.m4: + Remove. These files are handled by slingshot now. + * bootstrap: New file. Copied from slingshot subproject. + * bootstrap.conf (stdlib_copy_slingshot): Install missing slingshot + files. + * configure.ac: Adjust to drive slingshot processes. + * local.mk: New file. Project local make rules. + * rockspec.conf: New file. Slingshot rockspec template. + * specs/specs.mk: Simplified. + * .gitignore: Update. + * .travis.yml: Regenerate. + + slingshot: add slingshot as a git submodule. + * .gitmodules: New file. + * slingshot: New submodule. + +2013-04-30 Gary V. Vaughan + + configury: there is no distinct top_srcdir with non-recursive make. + * std/std.mk (dist_doc_DATA, dist_files_DATA, dist_modules_DATA): + Use plain $(srcdir). + + std: don't overwrite lua library tables. + Overwriting a global library table causes hard-to-debug problems + when the C part of Lua is holding references to the original + (overwritten) table. + * std/std.lua.in: When injecting new entry points into a core + library, do it an entry at a time to avoid breaking references to + the original address in the core. This fixes a problem with the + `package.path` and `package.cpath` being lost when set from + LUA_INIT, among many other potential issues. + +2013-04-13 Reuben Thomas + + Remove non-core modules, which go into separate lua-rrtlib for ad-hoc modules + +2013-04-11 Gary V. Vaughan + + specs: upgrade to Specl 5. + * specs/string_ext_spec.yaml, specs/table_ext_spec.yaml: Update + all 'should_error' matchers to saner Specl 5 ordering. + * specs/specs.mk (SPECL_MIN): Bump to 5. + + getopt: also requires io_ext module to be loaded. + * std/getopt.lua: Load io_ext into a local table. + +2013-04-10 Gary V. Vaughan + + configury: install correctly with configure or luarocks. + For the classic './configure; make; make install' to work we need + to install to '$luadir' as discovered by ax_lua.m4. But we also + need to install directly to luarocks '$(LUADIR)' otherwise the + stdlib source files are not copied onto the LUA_PATH properly by + 'luarocks make stdlib-*-1.rockspec'. + * mkrockspecs.lua: Move from here... + * build-aux/mkrockspecs: ...to here. + * GNUmakefile (rockspecs): Adjust accordingly. + * build-aux/mkrockspecs: Synchronise with the version that loads + a template, interpolates the variables and writes out the result. + * rockspecs.lua (build.install_command): Pass luadir to ensure + installation to the correct directory for luarocks. + * stdlib.rockspec.in: Remove. + * bootstrap: New script to call autoreconf for compatibility with + new build-aux/mkrockspecs. + * GNUmakefile (Makefile.in): Use it. + +2013-04-08 Gary V. Vaughan + + docs: point to github pages documentation. + * README.md (Documentation): Point to github pages documentation. + + docs: fix stupid typo. + * README.md (Installation): That would be luarOCKS not luarCOKS :-$ + + maint: correct git master installation instructions. + * README.md (Installation): Provide working instructions for + installing git master with Luarocks. + Liberal use of additional useful links. + +2013-04-07 Reuben Thomas + + README.md: simplify installation instructions, and other minor tweaks + +2013-04-07 Gary V. Vaughan + + maint: remove spurious LUA override in Travis CI build. + * .travis.yml (script): No need to force Lua 5.1 when running + initial make now that we fixed up luadocs. + + docs: we need to autoreconf a master branch github checkout. + * README.md (Installation): Show missing autoreconf invocation. + +2013-04-07 Gary V. Vaughan + + Revert "maint: support rerunning check-local in multiple lua environments." + This reverts commit e052c045cfc5b837e2dcb36db2e1cd34791a3c69. + + Conflicts: + Makefile.am + +2013-04-07 Gary V. Vaughan + + docs: rename README to README.md for github display goodness. + * README: Move from here... + * README.md: ...to here. + * Makefile.am (EXTRA_DIST): Add README.md. + + maint: tweak luadoc wrapper to work with Travis CI. + * .travis.yml (script): luadoc is a shell wrapper that calls the + wrong lua, so make a copy, patch it to call a compatible lua binary, + and run that instead. + + maint: run luadoc in a Lua 5.1 environment for Travis CI. + * .travis.yml (script): luadoc only works with Lua 5.1, so set the + execution environment up carefully for it to run correctly. + + maint: make luadoc available to Travis CI. + * .travis.yml (install): Install a system luarocks, and a luadoc + binary that uses it. + + maint: don't forget to make std/std.lua for Travis CI. + * .travis.yml (script): Run a full make to ensure std.lua is + generated in time for running mkrockspecs.lua! + + configury: don't hardcode specl, use $(SPECL) everywhere. + * specs/specs.mk (specs-check-local): Use $(SPECL), because bare + specl may not be on PATH, or otherwise correctly enabled. + * .travis.yml (script): Always call make with V=1 for better + logging. + + maint: install help2man for Travis CI specl build. + * .travis.yml (script): specl-v4 build currently fails when trying + to regenerate specl.1.in, so install help2man until that bug is + fixed. + + maint: set LUA_PATH for specl build with Travis CI. + * .travis.yml (script): Make sure local specl install can find + ansicolors and lyaml in local luarocks tree. + + maint: remove unnecessary manual ansicolors installation for Travis CI. + * .travis.yml: The previous error was caused by a bug in specl + release 3, not a lack of ansicolors. Remove the bogus ansicolors + installation now that specl-v4 is released. + + maint: make sure ansicolors is installed for Travis CI. + * .travis.yml (script): Install ansicolors to local luarocks tree. + + maint: make sure specl is installed for Travis CI. + * .travis.yml (script): Install specl, and use it for make check. + + configury: respect `make V=0` in rockspecs rule. + * GNUmakefile (rockspecs): Be quieter by default. + + std: strip leading "std." from global namespaces. + * std/std.lua.in: Before injecting symbols into the global + namespace, strip off the leading "std." added by std.modules.lua. + + configury: use uninstalled stdlib again. Again. (issue #24) + * specs/specs.mk (std_path): Move from here... + * Makefile.am (std_path): ...to here. + (LUA_ENV): Make sure to set this so that mkrockspecs rule uses + uninstalled stdlib again. + * specs/specs.mk (SPECL_ENV): Simplify accordingly. + + configury: fix a typo in luarocks-config.lua generation. + * GNUmakefile (luarocks-config.lua): Only one e in 'echo'. + + maint: integration with Travis CI system. + * .travis.yml: configure, build and test with each of Lua 5.1, + Lua 5.2 and luajit. + * GNUmakefile (luarocks-config.lua): Set interpreter environment + variables for luarocks invocations. + * mkrockspecs.lua: Invoke luarocks via user LUAROCKS setting. + * stdlib.rockspec.in (build.build_command): Pass LUA as a + configure precious variable so that recent ax-lua.m4 uses it as + passed instead of running the hardcoded search for a lua binary. + * README: Add travis build status badge markup. Plus some + indentation corrections for correct markdown code rendering. + +2013-04-01 Gary V. Vaughan + + docs: improved installation instructions. + * README: improve installation instructions. + + maint: The Grand Renaming™ - use `require "std.list"` etc. + * Makefile.am (SPEC_ENV, SPECL, SPECL_MIN, SPECS, MULTICHECK) + (check-local, src_spec): Split out from here... + * specs/specs.mk (SPECL_ENV, SPECL, SPECL_MIN, specl_SPECS) + (MULTICHECK, specs-check-local, std_path): New file. ...to here. + * Makefile.am (SOURCES, dist_data_DATA, dist_doc_DATA) + (dist_files_DATA, dist_modules_DATA): Split out from here... + * std/std.mk (nobase_dist_lua_DATA, dist_lua_DATA, std/std.lua) + (dist_doc_DATA, dist_files_DATA, dist_modules_DATA): New file. + ...to here. + * specs/debug_ext_spec.yaml, specs/getopt_spec.yaml, + specs/io_ext_spec.yaml, specs/math_ext_spec.yaml, + specs/package_ext_spec.yaml, specs/string_ext_spec.yaml, + specs/table_ext_spec.yaml: Adjust require calls. + * src/base.lua, src/bin.lua, src/debug_ext.lua, src/debug_init.lua, + src/fstable.lua, src/getopt.lua, src/io_ext.lua, src/lcs.lua, + src/list.lua, src/math_ext.lua, src/mbox.lua, src/modules.lua, + src/object.lua, src/package_ext.lua, src/parser.lua, src/set.lua, + src/std.lua.in, src/strbuf.lua, src/strict.lua, src/string_ext.lua, + src/table_ext.lua, src/tree.lua, src/xml.lua: Move from here... + * std/base.lua, std/bin.lua, std/debug_ext.lua, std/debug_init.lua, + std/fstable.lua, std/getopt.lua, std/io_ext.lua, std/lcs.lua, + std/list.lua, std/math_ext.lua, std/mbox.lua, std/modules.lua, + std/object.lua, std/package_ext.lua, std/parser.lua, std/set.lua, + std/std.lua.in, std/strbuf.lua, std/strict.lua, std/string_ext.lua, + std/table_ext.lua, std/tree.lua, std/xml.lua: ...to here. Adjust + require calls. + * std/modules.lua: Add "std." prefix. + * std/std.lua.in: Strip "std." prefix before injecting required + symbols into global namespace. + +2013-03-29 Gary V. Vaughan + + maint: support rerunning check-local in multiple lua environments. + * .luamultienv: Example multienv runner. + * Makefile.am (SPECL): Allow overriding. + (MULTICHECK): Location of multicheck script. + (check-local): Run multicheck without looping, if present. + + getopt: ensure we find _G.arg from Specl nested setfenv environments. (issue #27) + Although only necessary for Lua 5.1, this fix is harmless for Lua + 5.2, and we support both! + * src/getopt.lua (processArgs): Use only fully qualified _G.arg + references, for Lua 5.1 Specl compatibility. + Fixes issue #27. + +2013-03-29 Reuben Thomas + + getopt: instead of parsing undefined options, stop at first non-option + This is a configurable behaviour in C getopt, and does what I actually + wanted in the first place. If the other behaviour has a use case, it + can be reinstated later. + +2013-03-26 Gary V. Vaughan + + string_ext: propagate string metamethods correctly. (issue #26) + * specs/string_ext_spec.yaml (describe caps, describe chomp) + (describe escape_pattern, describe escape_shell, describe finds) + (describe format, describe ltrim, describe pad, describe rtrim) + (describe split, describe tfind, describe trim, describe wrap): + Add new specifications for metamethods. + * src/string_ext.lua (M): Declare at the beginning, and then + copy function references in at the end. + (__index metamethod): Delegate to M for unknown metamethods, now + that it carries all string functions too. + Fixes issue #26. + + refactor: eliminate forward declarations by reordering string_ext.lua. + * src/string_ext.lua (split): Move above require_version, the + earliest caller. Declare as a local. Rewove forward declaration. + (finds): Move above split, the earliest caller. + (tfind): Move above finds, the earliest caller. + (format): Move above assert, the earliest caller. Declare as + local. Remove forward declaration. + + refactor: don't over simplify specs/string_ext_spec.yaml. + * specs/string_ext_spec.yaml (context by name): Add back + accidentally deleted line. + + refactor: simplify specs/string_ext_spec.yaml. + * specs/string_ext_spec.yaml (context by name): Unroll a function. + (it adds extension apis to the global table): Remove debug code. + + std: propagate methods into global environment correctly. (issue #25) + * specs/string_ext_spec.yaml (keywords): List of methods that + should propagate into the global environment. + (it propagates keywords to the global environment): New + specification. + * src/std.lua.in: Propagate global methods correctly. + Fixes issue #25. + + configury: mkrockspecs uses uninstalled stdlib again. (issue #24) + * GNUmakefile (ROCKSPEC_ENV): Default to same value as LUA_ENV. + (MKROCKSPECS): Now uses lua-stdlib from the build tree, not the + previously installed version. + +2013-03-25 Gary V. Vaughan + + debug_ext: don't perturb the global environment by default. + For backwards compatibility, `require "std"` will still write + symbols into the global environment, but when loaded directly be + much more hygienic and return everything in a table, like most + other modules: + * src/debug_ext.lua (M): Return a table of previous debug_ext + globals. + * src/debug_init.lua: Wrap _DEBUG in a returned table, but + initialize from _G._DEBUG when first loaded. + * specs/debug_ext_spec.yaml: New specs. Specify behaviour of + loading just debug_ext, or indirectly via `require "std"`. + Add pending examples for remaining debug_ext APIs. + * Makefile.am (SPECS): Add specs/debug_ext_spec.yaml. + + math_ext: don't perturb the global environment by default. + For backwards compatibility, `require "std"` will still write + symbols into the global environment, but when loaded directly be + much more hygienic and return everything in a table, like most + other modules: + * src/math_ext.lua (M): Return a table of previous math_ext + globals. + * specs/math_ext_spec.yaml: New specs. Specify behaviour of + loading just math_ext, or indirectly via `require "std"`. + Add pending examples for remaining math_ext APIs. + + io_ext: don't perturb the global environment by default. + For backwards compatibility, `require "std"` will still write + symbols into the global environment, but when loaded directly be + much more hygienic and return everything in a table, like most + other modules: + * src/io_ext.lua (M): Return a table of previous io_ext globals. + (file_metatable): Move from here... + * src/std.lua.in: ...to here. + * specs/io_ext_spec.yaml: New specs. Specify behaviour of loading + just io_ext, or indirectly via `require "std"`. + Add pending examples for remaining io_ext APIs. + + refactor: move leaves and ileaves from base to list. + Break another dependency loop. + * src/base.lua (_leaves, _G.ileaves, _G.leaves): Move from here... + * src/list.lua (_leaves, ileaves, leaves): ...to here. + (M): Adjust. + * src/io_ext.lua: For ileaves, `require "list"` instead of "base". + (writelines): Adjust. + * src/tree.lua (M): Re-export list.ileaves and list.leaves as + tree.ileaves and tree.leaves. + * src/std.lua (_G): Re-export list.ileaves and list.leaves to the + global environment. + + string_ext: don't perturb the global environment by default. + For backwards compatibility, `require "std"` will still write + symbols into the global namespace, but when loaded directly be + much more hygienic and return everything in a table, like most + other modules: + * src/base.lua: Don't depend on list or string_ext modules to + break a depndency loop. + (_G.require_version, _G.render, _G.tostring, _G._tostring) + (_G.prettytostring, _G.pickle, _G.assert): Move these "stringy" + functions from here... + * src/string_ext.lua: ...to here, breaking another dependency + loop. + (M): Define module symbols in this table and return it. + * specs/string_ext_spec.yaml: Add pending examples for the + relocated APIs. + * src/std.lua.in: Inject string_ext module symbols into global + string namespace, and newly relocated base functions directly + into _G. + * src/debug_ext.lua, src/getopt.lua, src/xml.lua: Use required + string_ext return value from a local table. + * specs/string_ext_spec.yaml: Specify new behaviour, being + careful about `require "std"` side-effects. + + table_ext: don't perturb the global environment by default. + For backwards compatibility, `require "std"` will still write + symbols into the global namespace, but when loaded directly be + much more hygienic and return everything in a table, like most + other modules: + * src/table_ext.lua (unextended): Remove. + (M): Include core lua table.sort function as M._sort, and + return M. + * src/std.lua.in: Inject table_ext module symbols into global + namespace. + * specs/table_ext_spec.yaml: Specify new behaviour, being + careful about `require "std"` side-effects in global namespace.. + * src/base.lua, src/fstable.lua, src/getopt.lua, src/object.lua, + src/string_ext.lua: Save "table_ext" import into a local table and + call APIs directly from there. + + package_ext: don't perturb the global environment by default. + For backwards compatibility, `require "std"` will still write + symbols into the global namespace, but when loaded directly be + much more hygienic and return everything in a table, like most + other modules: + * src/package_ext.lua (M): Define module symbols in this table + and return it. + * src/std.lua.in: Inject package_ext module symbols into global + namespace. + * specs/package_ext_spec.yaml: Specify new behaviour, being + careful about `require "std"` side-effects. + + configury: bump release number to 35. + * configure.ac (AC_INIT): Bump release number to 35. + +2013-03-24 Gary V. Vaughan + + configury: warn if `make check` needs a newer Specl. + * Makefile.am (SPECL_MIN): Oldest release capable of running our + spec files. + (check-local): If actual Specl version is older than SPECL_MIN + show a diagnostic rather than try to run the specs. + + maint: move GNU make only rules to GNUmakefile. + * Makefile.am (SPECL_OPTS, specl_verbose_, specl_verbose_0) + (specl_verbose_1): Move from here... + * GNUmakefile: ...to here. + (specl_verbose_1): Now that Specl-3 passes --verbose on to the + selected formatter to handle, explicitly request the long-form + report formatter for `make check V=1`. + + maint: revert premature merge of pull request #23. + Reverse apply 5508adb. + +2013-03-23 Reuben Thomas + + README: make formatting consistent + + GNUmakefile: change to using a separate checkout for release branch + +2013-03-15 Reuben Thomas + + Merge pull request #23 from rrthomas/gary/ext-hygiene + package_ext: don't perturb the global environment by default. + +2013-03-15 Gary V. Vaughan + + package_ext: don't perturb the global environment by default. + For backwards compatibility, `require "std"` will still write + symbols into the global namespace, but when loaded directly be + much more hygienic and return everything in a table, like most + other modules: + * src/package_ext.lua (M): Define module symbols in this table + and return it. + * src/std.lua.in: Inject package_ext module symbols into global + namespace. + * specs/package_ext_spec.yaml: Specify new behaviour, being + careful about `require "std"` side-effects. + + string_ext: fix a bad assumption in a spec example. + Running string.format in the expectation `should` uses the core + Lua string.format which doesn't prettify tables, but we're + comparing it to the string_ext `..` operator which uses the std + enhanced string.format, and that *does* prettify tables. + * specs/string_ext_spec.yaml (stringifies non-string arguments): + Manually expand the the stringified table in should to match the + std prettified table stringification. + + list: fix standalone loading of string_ext module. + This fix reenables simple 'require "string_ext"' without an additional + 'require "std"'. + * src/string_ext.lua: Load the list module, which is used in split(). + Store APIs from `require "strbuf"`. + +2013-03-14 Gary V. Vaughan + + list: fix standalone loading of list module. + This fix reenables simple 'require "getopt"' without an additional + 'require "std"'. + * src/list.lua (new): Add a forward declaration. + (sub, concat, rep, reverse, transpose, enpair, flatten, shape) + (indexKey, indexValue): Use new, rather than list.new. + * src/getopt.lua (processArgs): Call local 'getOpt' function. Global + getopt.getOpt may never exist! + +2013-03-09 Reuben Thomas + + README: mention all extra dependencies of fstable + + getopt: remove the need for ugly getopt.Option constructor + +2013-03-09 Gary V. Vaughan + + maint: support 'make check V=1' and 'SPECL_OPTS=-v make check'. + * Makefile.am (SPECL_OPTS): Unless set in the environment already, + pass -v to specl according to whether or not V=1 was given to make. + (check-local): Use it. + +2013-03-09 Reuben Thomas + + getopt: allow parsing of undefined options; useful for programs which wrap other programs + +2013-03-07 Gary V. Vaughan + + specs: update to cleaner Specl-2 YAML spec format. + * specs/getopt_spec.lua, specs/package_ext_spec.lua, + specs/string_ext_spec.lua, specs/table_ext_spec.lua: Rename from + these... + * specs/getopt_spec.yaml, specs/package_ext_spec.yaml, + specs/string_ext_spec.yaml, specs/table_ext_spec.yaml: ...to + these. Reformat as YAML. + * Makeflie.am (SPECS): Remove old _spec.lua filenames, and add + new _spec.yaml filenames. + +2013-03-06 Gary V. Vaughan + + specs: allow for package.config differences in Lua 5.1. + * specs/package_ext_spec.lua (it splits package.config up): Allow + for optional trailing \n present in 5.2 but not 5.1. + +2013-02-27 Gary V. Vaughan + + configury: bump release number to 34. + * configure.ac (AC_INIT): Bump release number to 34. + + specs: rely on installed specl rather than shipping our own. + * specs/specl, specs/lib/specl.lua: Remove. + * Makefile.am (EXTRA_DIST): Adjust accordingly. + (lib_spec): Remove. + (SPEC_ENV): Adjust accordingly. + (SPECS): Add $(srcdir) prefix for distcheck. + (check-local): Run listed SPECS using installed specl. + + maint: don't checkout master again before running woger. + * GNUmakefile (check-in-release): Move current_branch save and + restore from here... + (release): ...to here. + + maint: mkrockspecs simplifications. + * luarocks-config.lua.in: Remove. + * configure.ac (AC_CONFIG_FILES): Remove luarocks-config.lua. + * GNUmakefile (luarocks-config.lua): Make on demand. + * Makefile.am (rockspecs): Move from here... + * GNUmakefile (rockspecs): ...to here. + (WOGER_ENV, WOGER_OUT): Factored out of release rule. + (release): Simplify accordingly. + + maint: don't forget to distribute GNUmakefile. + * Makeflie.am (EXTRA_DIST): Add GNUmakefile. + +2013-02-25 Gary V. Vaughan + + io_ext: remove spurious access to global 'prog' variable. + * src/io_ext.lua (processFiles): The supplied function receives + a copy of each file name, so no need to make assumptions about + global variables in here. + +2013-02-23 Gary V. Vaughan + + specs: add a skeleton specl spec for getopt module. + * src/getopt.lua (test): Remove. + * specs/getopt_spec.lua: Reimplement as specs. + * Makefile.am (SPECS): Add spects/getopt_spec.lua. + * specs/getopt_spec.lua): More specs for default option overrides. + * src/getopt.lua (processArgs): Fix a small scoping bug uncovered + by the new specs. + + getopt: don't display the full help for option errors. + * src/getopt.lua (processArgs): Show the error and a short help + message for option errors, and save the full blown help screens + for '--help'. + (usage): Simplify concatenations slightly. + + getopt: support GNU style --help and --version output. + * src/getopt.lua (usage): Take arguments from the prog parameter + instead of inspecting global variables. + If present, wrap paragraphs from prog.description and display + between purpose and option table. + (version): New function. Display version info according to prog. + version and prog.copyright. + (processArgs): Use it. + * template.lua: Update. + * specs/specl: Update. + + getopt: insert a blank line between usage and purpose. + + getopt: move options and prog out of the global namespace. + * src/getopt.lua (usage): Require prog as a parameter with options + inside it. + (processArgs): Likewise. + * template.lua: Update. + + getopt: only add default help and version when user supplied none. + * src/getopt.lua (makeOptions): Make two passes through the option + list. The first to collect all option declarations, and the second + to make an index. + (appendOpt): Factor out, and add a nodupes parameter to prevent + addition option specs being added when any of the option letters + they use have been claimed already. + Use it to only add default help and version options when the user + didn't specify their own already. + + getopt: format long and short options properly. + * src/getopt.lua (fmtOpt): Display two leading dashes for long + options. + (usageInfo): Indent past the short option column where only a + long option is being displayed. + + getopt: move Option out of the global namespace. + * src/getopt.lua (_G.Option): Rename from this... + (Option): ...to this local declaration. + (M): Export Option as a new public interface. + Remove TODO. + * template.lua, specs/specl: Update. + +2013-02-22 Reuben Thomas + + Fix some spec failures in string.wrap, and one error in a spec. + + string_ext.lua: fix a reference to string.sub. + +2013-02-22 Gary V. Vaughan + + string_ext: prefer snake_case to camelCase APIs. + * src/string_ext.lua (escapePattern, escapeShell, ordinalSuffix): + Rename from these... + (escape_pattern, escape_shell, ordinal_suffix): ...to these. + (M): Continue to support camelCase calls for backwards compatibility. + * specs/string_ext_spec.lua: Update. + Add specs to ensure camelCase APIs continue to work. + + maint: update string_ext to use Lua 5.2 style modules. + * src/string_ext.lua: Save unextended string table, returning + that after injecting stdlib extensions. + * specs/string_ext_spec.lua (context when requiring the module): A + few new specifications for requiring string_ext. + + specs: add missing specs for string.finds and string.tfind. + * specs/string_ext_spec.lua (describe string.finds (), describe + string.tfind ()): New specs for these APIs. + + specl: ensure that failing to meet specs kills make distcheck. + * specs/lib/specl.lua (run): Return true unless there are + expectation failures. + * specs/specl: Exit with non-zero status for with expectation + failures. + + string_ext: bail out early with fatal type mismatch errors. + * src/string_ext.lua (wrap): Rather than letting the guts of wrap + choke unpredictably later on, assert the required types at the + outset. + (tfind): Likewise. These currently bubble up to finds and split + as well. + + specl: update ordinalSuffix () error specs to match the new error messages. + * specs/string_ext_spec.lua (describe string.ordinalSuffix): Make the + expectations match reality. + +2013-02-22 Reuben Thomas + + Fix ordinalSuffix for negative arguments (issue #20). + + string_ext.lua: use Lua terminology "pattern" rather than "regex" + +2013-02-22 Gary V. Vaughan + + specs: add specl specification for string_ext module. + * specs/string_ext_spec.lua: New file. Specl specs for + string_ext. + * Makefile.am (SPECS): Add specs/string_ext_spec.lua. + + specl: sync from upstream. + * specs/lib/specl.lua: Upgrade to the latest upstream, where the + contain and match matchers are not broken! + + string_ext: don't use math.mod, which doesn't exist in Lua 5.2. + * string_ext.lua (ordinalSuffix): Use '%' operator instead of + math.mod, which is compatible with Lua 5.1 and 5.2. + + maint: move maintainer rules into GNUmakefile. + * Makefile.am: Move maintainer rules from here... + * GNUmakefile.am: New file. ...to here. + Automatically autoreconf the directory if configure is missing. + + configury: make sure WOGER has a default value. + * Makefile.am (WOGER): Set it to 'woger' by default. + + configury: bump release number to 33. + * configure.ac (AC_INIT): Bump release number to 33. + +2013-02-21 Gary V. Vaughan + + configury: make the release rules more robust. + * Makefile.am (GIT, GIT_REMOTE, LN_S): Set as macros that can be + overridden during testing. + Adjust all callers. + (tag-release, check-in-release): Move the push commands that + publish changes upstream from here... + (release): ...to here. + (check-in-release): Rather than rely on having a parallel checkout + in a particular sibling directory, just switch branches from here + temorarily. + (unpack-distcheck-release): Once in the relase branch, overwrite + the previous release with the tarball from distcheck. + + configury: bump release number to 32. + * configure.ac (AC_INIT): Bump release number to 32. + + specs: add specl specification for package_ext module. + * specs/package_ext_spec.lua: New file. Specl specs for + package_ext. + * Makefile.am (SPECS): Add specs/package_ext_spec.lua. + + maint: update package_ext to use Lua 5.2 style modules. + * src/package_ext.lua: Save unextended package table, returning + that after injecting stdlib extensions. + + specs: specify return of the unextended module from table_ext. + * specs/table_ext_spec.lua (context when requiring the module): A + few new specifications for requiring table_ext. + + maint: update table_ext.lua to use Lua 5.2 style modules. + * src/table_ext.lua: Save unextended package table, returning that + after injecting stdlib extensions. + Declare everything locally. + (M): Public interface. + + specl: new specification testing framework. + * specs/lib/specl.lua: New file for specification testing. + * specs/specl: New file. Command line wrapper for specl.lua. + * specs/table_ext_spec.lua: New file. Specl tests for + src/table_ext.lua. + * Makefile.am (src_spec, lib_spec): Lua search path strings for + directories in the source tree. + (LUA_ENV): Adjust. + (SPEC_ENV): New macro. Set Lua environment for calling specl. + (SPECS): New macro. A list of specs/*_spec.lua files. + (EXTRA_DIST): Add new specification testing files. + (check-local): Hook the specl tests into the Automake test + framework. + + bugfix: make sure getopt.opt is updated in the module table. + Fix a bug preventing visibility of getopt.opt after port to Lua + 5.2 style modues. + * src/getopt.lua (M): Add opt table. Move declaration above... + (processArgs): ...here, and update M.opt instead of undeclared + opt variable. + Merge other public entry points into M table before returning. + + configury: use static list of SOURCES instead of $(filter)ing. + * Makefile.am (SOURCES): Remove the GNU Make extensions used to + dynamically build the list, and just list each file statically. + +2013-02-20 Reuben Thomas + + Makefile.am: bump version to 31. + + list.lua: rename slice to sub, for compatibility with strings. + + list.lua: add list methods. + + Makefile.am: re-add explicit setting for release notes file, which we can once more prepare ahead of time + + Makefile.am: no longer need to re-bootstrap after checking in release files. + +2013-02-18 Reuben Thomas + + m4/ax_lua.m4: get latest version; no code changes, but some pleasing spelling corrections. + + list.lua: allow list.new to take no arguments to create an empty list. + The previous commit relied on this behaviour; oops. + + list.lua: make all methods that return lists make them with list.new, not {}. + Also rename result variables consistently to r, not, in some cases, m. + +2013-02-17 Reuben Thomas + + Makefile.am: checking out release branch in same directory doesn't work; use another directory. + + Makefile.am: since git clean deletes release notes file, don't supply it to woger (which will prompt for release notes). + + Makefile.am: need to git clean directories too. + + Makefile.am: run git clean to ensure we can check out the release branch. + + Makefile.am: only supply std.lua once (fixes make distcheck in some setups) + +2013-02-15 Reuben Thomas + + Makefile.am: copy a couple of fixes from luaposix. + + rockspecs.lua: add luarocks include path to configure command. + + Makefile.am: remove reference to defunct stdlib.rockspec.in + + rockspecs.lua: remove lrexlib-specific comment + +2013-02-12 Gary V. Vaughan + + maint: bump version to 30. + * configure.ac (AC_INIT): Bump version to 30. + +2013-02-12 Gary V. Vaughan + + Merge pull request #14 from gvvaughan/pull-request/move-to-lua52-module-style + Pull request/move to lua52 module style + +2013-02-12 Gary V. Vaughan + + configury: make sure we build std.lua when necessary. + Using wildcard for SOURCES before std.lua has been built now that + configure no longer makes it for us, means standard make or + luarocks install after building from a fresh checkout results in + std.lua not being built or installed. + * Makefile.am (SOURCES): Add src/std.lua. + +2013-02-12 Gary V. Vaughan + + Merge pull request #12 from gvvaughan/pull-request/fix-luadocs-hard-dependency + configury: fix hard dependency on luadocs. + + Merge pull request #11 from gvvaughan/pull-request/use-uninstalled-stdlib + configury: load local std modules as expected by mkrockspecs.lua. + +2013-02-11 Reuben Thomas + + parser.lua: update to current stdlib and Lua 5.2, fixing a couple of small bugs + Remove a debugging line accidentally left in before. + + base.lua: fix die; previously produced rubbish! + +2013-02-11 Gary V. Vaughan + + maint: update parser to use lua 5.2 style modules. + * src/parser.lua: Simply return the Parser object. + + maint: update object to use lua 5.2 style modules. + * src/object.lua: Simply return the callable Object table. + Adjust all callers. + + maint: update setbuf to use lua 5.2 style modules. + * src/strbuf.lua: Declare everything locally, and return a table + of interfaces, per lua 5.2 module style. + Adjust all callers. + + maint: update mbox to use lua 5.2 style modules. + * src/mbox.lua: Declare parse locally, and return a table with a + reference, per lua 5.2 module style. + + maint: update lcs to use lua 5.2 style modules. + * src/lcs.lua: Declare longestCommonSubseq locally, and return a + table containing a reference to it, per lua 5.2 module style. + + maint: update getopt to use lua 5.2 style modules. + * src/getopt.lua: Declare everything locally, and return a table + of interfaces, per lua 5.2 module style. + + maint: update fstable to use lua 5.2 style modules. + * src/fstable.lua: Declare everything locally, and return a table + of interfaces, per lua 5.2 module style. + + maint: update bin to use lua 5.2 style modules. + * src/bin.lua: Declare everything locally, and return a table of + interfaces, per lua 5.2 module style. + + maint: update trees to lua 5.2 style modules. + * src/tree.lua: Declare everything locally, and return a table of + interfaces, per lua 5.2 module style. + + maint: update lists to use lua 5.2 style modules. + * src/list.lua: Declare everything locally, and return a table of + interfaces, per lua 5.2 module style. + Adjust all callers. + + maint: update sets to use lua 5.2 style modules. + This is backwards compatible with lua 5.1, and allows use of sets + with the strict module loaded, which raised a bogus error before. + * src/std.lua.in (version): Return this in the module namespace. + Require the bundle of "std" modules into the global namespace. + * src/set.lua: Declare everything locally, and return a table + of interfaces, per lua 5.2 module style. + + configury: fix hard dependency on luadocs. + * m4/ax_with_prog.m4: New file. + * configure.ac (AX_WITH_PROG): Use it to find a luadocs binary. + * Makefile.am ($(dist_doc_DATA)): Use the substituted binary. + * rockspecs.lua: Remove insertion of luadocs dependency. + + * configury: load local std modules as expected by mkrockspecs.lua. + Rather than having to manually update the installed lua-stdlib + behind luarocks' back, let the mkrockspecs.lua script find the + modules it was written for in the source tree. + * Makefile.am (LUA_PATH): Default to compiled-in search path. + (LUA_ENV): Load modules from the source tree instead of potentially + outdated versions from the lua installation. + (rockspecs): Run lua with LUA_ENV set. + +2013-02-09 Reuben Thomas + + rockspecs.lua: add luadoc as a dependency for git rockspec. + +2013-02-08 Reuben Thomas + + rockspecs.lua: fix build command for building from git. + + mkrockspecs.lua: whitespace fix. + + Add a rockspec for building from git, and machinery for building variant rockspecs. + + tree.lua: add tree.merge. + + set.lua: fix broken elems iterator (issue #10) + + strict.lua: tweak formatting to match other modules + + base.lua: note availability of original tostring as _tostring. + +2013-02-07 Reuben Thomas + + set.lua: add missing dependency on list.lua + + Avoid rebuilding documentation in distributed sources, so users don't need luadoc installed. + +2013-02-06 Reuben Thomas + + Makefile.am: some more fixups to release-by-git. + + .gitignore: we have reverted from zip to tgz tarballs. + + Makefile.am: don't try to rebuild documentation if not necessary (doesn't work in VPATH build, so breaks distcheck). + + Makefile.am: distcheck is really a dependency of check-in-release, not of release. + + stdlib.rockspec.in: correct name of git tag to source release tag. + + Makefile.am: check in sources before trying to check them out for test build. + + Fix issue #8: members with the same names as class methods cause problems. + + Bump version to 29. + + Makefile.am: add check-in-release to push release files to git. + + Makefile.am: enable building of documentation from git checkout. + + Change to building (with LuaRocks) direct from git, not releasing a zip. + + README: update installation instructions and mention GitHub, not LuaForge. + + Update to latest ax_lua.m4. + +2013-02-05 Reuben Thomas + + README: Make Lua 5.2 compatibility definite, and update copyright years. + +2012-12-23 Reuben Thomas + + stdlib.rockspec.in: remove redundant dir setting + +2012-10-30 Reuben Thomas + + .gitignore: add luarocks directory + +2012-10-28 Reuben Thomas + + Add testing of luarock against uploaded archive before mailing announcement + + base.lua: user simpler default require_version pattern that works in more cases + +2012-10-28 Reuben Thomas + + Generate Lua version in rockspec from that in configure.ac + Bump version to 28 + + Use newer ax_lua.m4 + + Tweak pattern used to substitute MD5 sum into rockspec to be + compatible with gnulib syntax checks, should we ever use them. + +2012-10-28 Reuben Thomas + + Bump version to 28, and simplify slightly, requiring automake 1.11 + +2012-10-15 Reuben Thomas + + base.lua: move a documentation stanza to a more apt location + +2012-10-04 Reuben Thomas + + release: fix call to woger to generate correct URLs + +2012-10-03 Reuben Thomas + + rockspec: fix homepage URL + + Makefile.am: really distribute all docs + + rockspec: fix download URL (thanks, Hisham Muhammad) + +2012-10-01 Reuben Thomas + + base.lua: add require_version + +2012-09-26 Reuben Thomas + + Make build_command in rockspec more robust. + + Install documentation with 'make install' and from luarocks. + +2012-09-22 Reuben Thomas + + Rename table.indices to table.keys, and use term 'keys' more. + +2012-09-21 Reuben Thomas + + getopt.lua: output usage information to stdout, not stderr + + configure.ac: bump version to 27 + + configure.ac: accept Lua 5.2 + +2012-09-18 Reuben Thomas + + getopt.lua: remove func member of Option; simply gather all option values into a list + + getopt.lua: improve some comments and remove a redundant require + +2012-09-17 Reuben Thomas + + set.lua: fix last commit: elements should be elems + + base.lua: remove a spurious blank line + + set.lua: revert elements iterator to being pairs; leaves is wrong! + +2012-09-12 Reuben Thomas + + Turn on debugging by default and tweak what the global debug function does. + +2012-09-12 Reuben Thomas + + I misunderstood what finds did, and didn't spot that it was needed for split! + Revert "string_ext: remove finds; map should be used with string.find instead" + + This reverts commit 5a62e3ee7ad2514b681ff6f348c43b797088b089. + +2012-09-11 Reuben Thomas + + string_ext: remove finds; map should be used with string.find instead + +2012-09-06 Reuben Thomas + + Remove string.gsubs: the order of substitutions was undefined, and map can be used just as well. + +2012-07-06 Reuben Thomas + + build: Check MD5 sum of rockspec against tarball before releasing + +2012-05-31 Reuben Thomas + + object: fix an incorrect simplification in the previous commit. + + object: add the ability to have a constructor function. + +2012-05-31 Reuben Thomas + + Tweak a couple of table functions. + Rename table.rearrange to the more descriptive table.clone_rename, and + clarify the documentation. + + Make table.merge not clone its left-hand argument, but modify it, as + the user has reason to expect. + +2012-05-31 Reuben Thomas + + parser.lua: fix call to renamed method. + +2012-05-29 Reuben Thomas + + object.lua: fix inconsistency and missing HTML close tag in doc comments. + +2012-02-22 Reuben Thomas + + base.lua: Fix minor formatting variation. + + Merge pull request #5 from gvvaughan/pull-request/for-fame-and-glory + AUTHORS: Add myself. + +2012-02-22 Gary V. Vaughan + + base.lua: new memoize function. + * src/base.lua (memoize): Memoize a single result function by + wrapping it in a functable. + +2012-02-22 Reuben Thomas + + Update URL to point to github. + + Reformat some code to make lua-mode happier (most of the time, sigh). + +2012-02-22 Gary V. Vaughan + + AUTHORS: Add myself. + * AUTHORS: List the few small contributions I've made. + +2012-02-18 Reuben Thomas + + Update rockspec for github. + + Makefile.am: Update call to woger. + + Makefile.am: Update call to woger. + + Makefile.am: Update call to woger. + + Makefile.am: Update call to woger. + + Makefile.am: Update call to woger. + + Makefile.am: Update call to woger. + + Makefile.am: Update call to woger. + + Rename set.elements to set.elems for consistency with list.elems. + +2012-01-21 Reuben Thomas + + base.lua: add missing require of strbuf. + + strict.lua: improve error message. + +2012-01-19 Reuben Thomas + + Add leaves, ileaves and inodes tree iterators; simplify nodes slightly. + Use ileaves to simplify flatten. + + Rename io.writeline to io.writelines and allow it to flatten table + arguments. + + Use list.elems instead of ipairs in several places. + + Fix FIXME in set: use leaves as elements to return only the key. + +2012-01-18 Reuben Thomas + + src/io_ext.lua: Improve docstring for readlines. + +2012-01-17 Reuben Thomas + + string_ext: fix new string.__concat metamethod to run tostring on both args, thus avoiding infinite recursion. + + string_ext: add __concat metamethod for strings which runs tostring. + Reformat some code to please lua-mode and add some missing quotes in a + comment. + +2012-01-09 Reuben Thomas + + io_ext: Add slurp; use it in various places. + +2011-12-16 Reuben Thomas + + Bump version to 26. + +2011-12-16 Reuben Thomas + + Merge pull request #1 from gvvaughan/patch-1 + tree: fix bugs when using a list of tables as keys + + Thanks for this. I don't currently have another use case than Zile to test with. + +2011-12-16 Gary V. Vaughan + + tree: fix bugs when using a list of tables as keys + To be fully general, tree should allow table keys so that it's + possible to write: + $ lua -lstd + > t, k1, k2 = tree.new(), {key='a'}, {key='b'} + > t[{"k1", "k2"}] = "string keys"; t[{k1, k2}] = "table keys" + > = t[{"k1", "k2"}], t[{k1, k2}] + string keys table keys + This patch fixes 3 bugs that prevent that from working. + * src/tree.lua (metatable.__newindex): Detect subtrees correctly + by comparing against the tree metatable, rather than assuming any + "table" type is a correct match. + Use rawset to insert a new node without triggering the __index + metamethod. + (metatable.__index): Don't recurse into table key members, only + the list entries to be folded, by checking whether the table has + a length first - for t[{k1, k2}], {k1, k2} is a list (with length) + and should be folded, but k1 (k1 = {key='a'}) is not a list and + should not. + +2011-09-30 Reuben Thomas + + io_ext: unset prog.file at the end of io.processFiles + +2011-09-27 Reuben Thomas + + getopt: improve output and conformance to best practice + Make the short option for -version be -V, not -v. + + Remove short option -? for -help. + + In help, show short options first, so that display is easier to read. + + Remove publicly Options constructor, as it’s not needed externally. + +2011-09-19 Reuben Thomas + + Makefile.am: tweak rockspec's deps. + + src/.gitignore: add std.lua. + + Bump version to 25. + + std.lua: add a version string to the std module + + list: add list.compare, and __le and __lt metamethods. + + Makefile.am: make release message shorter and more precise. + + Makefile.am: distribute rockspec source. + + .gitignore: ignore correct zip name. + + configury: rename project to stdlib for consistency and to make luarocks happy. + + Make rockspec on release. + + Bump version to 24. + + string_ext.lua: fix old call of findl (is now called tfind). + +2011-09-17 Reuben Thomas + + Makefile.am: fix getting summary description, and reminder message output by make release. + + rockspec: Keep old name (stdlib) for the rock. + Also fix LuaForge URL, which of course hasn’t changed. + + .gitignore: Add Makefile. + + Build system: autotoolize and generate rockspec. + +2011-09-11 Reuben Thomas + + Rename findl to tfind to conform to lrexlib. + Also fix a bug in the LuaDoc documentation of the return values. + +2011-09-07 Reuben Thomas + + Remove posix_ext module (is going in luaposix instead). + Update documentation about LuaDoc. + + Add posix.creat. + +2011-09-02 Reuben Thomas + + Fix typo. + + Add a new module with some binary to number/string conversion routines. + + Add simple string buffers and use them for default table tostring. + + Fix mode on .gitignore. + + Remove some non-LuaDoc markup. + +2011-08-19 Reuben Thomas + + Fixup. + + Add some more LuaDoc stuff. + + Partial conversion of documentation to LuaDoc. + +2011-06-06 Reuben Thomas + + Push tags, but don’t tag until we’ve successfully released. + + Update woger call to new keyword style. + + Add fstable module for storing tables as file trees. + + Replace reference to ldoc with one to LuaDoc. + + Convert documentation to LuaDoc, and retire ldoc. + +2011-06-05 Reuben Thomas + + Add index.html. + +2011-05-22 Reuben Thomas + + Fix faulty ldoc tags. + +2011-05-21 Reuben Thomas + + Fix return code on --help to be 0. + Make dieWithUsage into plain usage, and don’t exit at end. + +2011-05-19 Reuben Thomas + + Add some missing param tags. + +2011-05-03 Reuben Thomas + + Comment out strict from default set to make co-existence with other code easier. + + Fix calls to writeLine to be to writeline. + +2011-05-02 Reuben Thomas + + Add readlines and writeline to file handle metatable. + + Fix capitalization of readlines and writeline in docstrings. + +2011-04-15 David Favro + + Fixed bug: ldoc used writeLine() rather than writeline(). + +2011-04-12 Reuben Thomas + + Always return nil on error, not -1. + +2011-04-04 Reuben Thomas + + Rename readLines to readlines and writeLine to writeline. + +2011-03-23 Reuben Thomas + + Only set _DEBUG to false if it’s not already initialised. + +2011-03-19 Reuben Thomas + + Fix splitdir. + +2011-03-12 Reuben Thomas + + Initialise _DEBUG early so that it can be overridden by app and still be strict.lua-compatible. + + Shorten a TODO. + +2011-03-09 Reuben Thomas + + Restore modules.lua as holding standard list, to un-break std.lua. + + Allow mk1file to generate customized sets of modules, with the standard set as the default. + +2011-03-08 Reuben Thomas + + Remove unnecessary posix. prefix. + + Correct name of package_ext. + + Remove redundant comments. + + Update documentation of standard set, and add prerequisites. + + Merge branch 'origin' of github.com:rrthomas/lua-stdlib into origin + + Remove posix_ext and object from standard set. + +2011-03-07 Reuben Thomas + + Remove posix prefix from function calls. + + Add euidaccess. + + Improve a comment. + + Merge branch 'origin' of github.com:rrthomas/lua-stdlib into origin + + Add __index method to allow OO syntax use of methods. + Add delete method. + +2011-03-01 Reuben Thomas + + Reverse order of list methods for convenient OO use. + + Merge from HEAD. + +2011-02-26 Reuben Thomas + + In future, make zip distros. + + Fix message. + + Use package.dirsep once more. + + Put package.config reflexion into a new package_ext module. + + Make message clearer. + + Leave dist tarball in source dir, not above. + + Add tarball to .gitignore. + + Add sensible access to package.config. + + Bump copyright year. + + Bump copyright year. + + Use undocumented package.config to get platform’s directory separator. + +2011-02-08 Reuben Thomas + + Improve release target to tag releases. + +2011-02-07 Reuben Thomas + + Fix missing math. prefix, and swap incorrect sign in sub. Thanks to Bob Chapman. + +2010-12-14 Reuben Thomas + + Speed up math.floor for case where p is 0 or absent (thanks, Lukáš Procházka). + +2010-12-09 Reuben Thomas + + Change rules from using CVS to using git. + + Reinstate string __index metamethod fallback so that OO uses of strings work. + Switch argument order of ltrim, rtrim and trim so they work in OO + syntax. + + Point to tree.clone for deep copies. + +2010-10-12 Reuben Thomas + + Restore 'dubious' but used string metamethod fallback. + +2010-10-09 Reuben Thomas + + Move .cvsignore's to .gitignore's. + +2010-10-08 Reuben Thomas + + Fix typo in io.catfile. + + Add commit that seems to be missing from import from CVS. + + Add new posix_ext module. + + Add posix_ext to list. + + Remove spurious full stop. + +2010-10-07 Reuben Thomas + + Add catfile and fix catdir to return `/' when necessary. + +2010-10-06 Reuben Thomas + + Fix permissions. + +2010-06-20 rrt + + Remove dubious metamethod fallback for string.__index. + +2010-06-13 rrt + + Fix and simplify tree.__newindex: there was a variable name typo, and sub-tables should also be initialised to trees, as otherwise the relevant metamethods are not called. + + Fix an incompatibility with strict.lua. + +2010-06-11 rrt + + Simplify nodes iterator and make it more efficient; thanks to Alistair Turnbull for the hint. + + Simplify, generalise and rename (from treeIter to nodes) tree iterator. + + Whitespace correction. + + Simplify implementation of empty, using next as per manual. + +2010-06-09 rrt + + Rename table.subscript to op["[]"], and move table.deepclone and table.lookup into a new module, tree, as the clone method and __index metamethod respectively. The tree module also has a constructor, new, and a __newindex metamethod. + Rename table.newDefault to table.new. + + Fix writeLine and add an explanatory comment. + + Make the set metatable a local variable. + +2010-06-08 rrt + + Add Lua distribution’s strict.lua to standard modules. + + Have a single list of modules in modules.lua and use it to load them in std.lua and generate the single-file version in mk1file, which latter is now a Lua script. + +2010-06-07 rrt + + Fix list.foldl, list.foldr, and the fold iterator combinator. + Simplify the op table functions to be exactly the primitive operators, + not list versions thereof. + + (Possibly) improve the commented-out simpler version of treeIter. + +2010-06-07 rrt + + Remove table.subscripts function: it’s easily replaced by subscript plus string.split, as in its definition. + +2010-06-07 rrt + + Initialise _DEBUG to nil so stdlib works with strict.lua. + Rename debug.traceCall to debug.trace (more Lua-ish). + + Use math.max rather than just (incorrectly) max. + + Improve some documentation. + +2010-06-02 rrt + + Remove redundant redefinition of print (it already calls tostring). + Fix op table so that base can require list without breaking; fixes + FIXME. + + Remove lcs module from std set. + Update FIXME about autogeneration of mk1file to make it more precise. + + Bump copyright year to 2010. + +2010-03-18 rrt + + Add missing dependency on list. + +2009-09-14 rrt + + Improve formatting slightly. + +2009-09-07 rrt + + Avoid using removed function io.changeSuffix. + + Remove rex module altogether. + + Don’t need pcre_rex any more. + Remove FIXME about external dependencies as we no longer have any, nor + intend to. + + Remove need for rex_pcre in xml. + Remove xml, rex, parser and mbox from standard list of libraries. + + Remove dependency on lposix; in the process remove addSuffix and changeSuffix, as they reliedon basename and dirname, and it wasn't worth reimplementing them as they're not used. + + Add explanatory comment. + +2009-08-31 rrt + + Add requirement of posix and copyright notice to output. + + Add lua-posix to list of dependencies. + +2009-08-24 rrt + + Fix basename and dirname calls + + Remove basename and dirname as they are now implemented in lposix. + Rename pathConcat to catdir and pathSplitDir to splitdir and make them + behave like the corresponding Perl functions. + + Add FIXME. + +2009-03-20 rrt + + Use the original _floor in round rather than chaining via floor for a bit of extra speed; thanks to David Kantowitz. + +2009-03-16 rrt + + Update 'usage' message. + + Make 'make release' do 'make dist' + + Simplify assert. + +2009-03-15 rrt + + Simplify string.format. + + Remove string.join, which is the same as table.concat. Thanks to David Kantowitz for spotting it. + +2009-03-14 rrt + + Add support for not cloning metatables. + +2009-03-13 rrt + + Check no outstanding changes and tag release. + + Fix typo. + + Update copyright year. + + Fix variable substitution in release target. + + Ignore release-notes-* + + Add release target, and exclude release notes from tarball. + + Copy metatables in deepclone, so that it does what it says. Patch from David Kantowitz. + +2009-02-19 rrt + + Add final newline for neatness. + + In the single file, make the special "require" function local so that other files can be required after the single-file std. + + Update object module to correspond with Lua Gems version. + +2008-09-04 rrt + + Fix Diego Nehab's name. Sorry Diego! Thanks to Shmuel Zeigerman for pointing out my error. + +2008-09-04 rrt + + Fix set.difference. + Add set.symmetric_difference. + + Make s * t do intersection for sets, and s / t do symmetric + difference, as in Modula 3 and (at least for *) "Programming in Lua". + +2008-09-04 rrt + + Fix equal. Thanks to report from Jiutian Yanling. + +2008-07-28 niklas + + Cope with nil values in map. + +2008-07-27 niklas + + Fix elems and relems + + Fix make dist; $REL -> ${REL}, add --exclude for .#*, and no longer exclude template-rrt.lua, which no longer lives in the tree. + +2008-06-20 niklas + + Add collect, from Patrick Walton, to run an iterator and collect the results in a table. + As a result, rewrite all the table functionals to be iterator + functionals (in base) and reimplement the list functionals in terms of + them. Add two iterators for lists, elems and relems, that return only + the elements and not the indices, in order to implement the list + functionals. + + A couple of old fixes where the Lua 5.1 table count is used (#). + + Simplify ripairs slightly. + + Fix a comment typo. + +2008-03-28 rrt + + Add some TODOs to make the prog structure a bit more sensible. + +2008-03-04 rrt + + Make a note to compare pathSplit and pathConcat with Perl equivalents. + + Add TODO to use LuaDoc instead. + +2008-03-03 rrt + + Add Makefile with dist target + + Fix typo in comment. + + No dependency on LFS. + + No longer have any sort of dependency on bitlib. + + Remove _INTEGER_BITS and unneeded dependency + + Update date and prerequisites. + +2008-03-01 rrt + + Require external deps before neutering "require". + + We may add bit to stdlib, but it's not currently there, nor is it actually used by anything in stdlib. + + rex is not an external dependency + + Simplify length function. + + Use LFS for length() function. + +2008-01-19 niklas + + Add pathSplit and pathConcat from nancy. + +2008-01-12 rrt + + Make calls to find and gsub get the function from the pattern, meaning that in theory they could work with other regex engines than Lua's. + + Remove pointless object notation on a string. + +2007-11-19 rrt + + Now that INTEGER_BITS is added to the math namespace, no need to prefix it with _. + + Note that bitlib is needed. + +2007-10-06 rrt + + Rename 'permute' to the more accurate 'rearrange' + + Update some comments to match changes in the Lua Gem about this code. + +2007-10-05 rrt + + Fix from Roberto Ierusalimschy. + +2007-05-11 rrt + + Fix up single-file stdlib + + Clarify TODO. + +2007-04-26 rrt + + Tidy length slightly. + + Clarify documentation a little further + + Ignore built docs + + Format prerequisites to allow for more than one! + Explain ldoc better. + + Revert to plain implementation of length to avoid using POSIX library which is currently unmaintained. + +2007-04-25 rrt + + Clear up uses of old vararg "arg" syntax (thanks Matt). + +2007-03-01 rrt + + Add list.rep + + Add FIXME for commented-out require + + Make join cope with empty lists. + + Remove default separator in string.split, and hence a TODO. + Add string.join. + +2007-02-25 rrt + + Mention the dependency on lrexlib. + +2007-02-24 rrt + + Use __append metamethod, not __concat, which was wrong + + Add __append metamethod and constructor for LCS + + Add __append metamethod for LCS + + Set had been left rather broken; fix it up. + + Remove no-longer-needed LCS method. Thanks to Enrico Tassi for noticing it. + +2007-02-22 rrt + + Cosmetic changes to finds (comments and a variable rename) for clarity. + Use list.flatten (l) instead of list.concat (unpack (l)) in split to + avoid overflowing the parameter stack (with the unpack) when splitting + large strings. Clarify the comment for this code. + +2007-02-21 rrt + + Fix indexKey and indexValue: the function passed to table.process wasn't returning the accumulator as it should have. + +2007-02-20 rrt + + Make a note to find better names for enpair and depair, which are useful but confusing. Something like pairsToTable and tableToPairs? + +2007-01-26 rrt + + Add missing dirname and basename + +2007-01-05 rrt + + Sync with reality. + +2007-01-04 rrt + + Document. + Set rex = rex_pcre, so that we actually have the functions we expect + under "rex". + + Now that lrexlib no longer has a Lua component or a default library, add a library to load it (currently just require rex_pcre). + +2006-12-06 rrt + + Remove TODOs that apply to lrexlib. + +2006-11-27 rrt + + Use non-list-capable math.max correctly + +2006-11-20 rrt + + Fix from Jerôme Vuarand to string subscription that deals with oldmetas that are functions. + +2006-11-08 rrt + + Stop string subscription operator from hiding other methods. + +2006-11-05 rrt + + Note that rex is now an external dependency. + + Note problem with external dependencies. + + Sort out adding to module metatables. + + Add a FIXME + + As 5.1 has all the metamethods we need, rewrite LCS to use them. Hence, no need for wrapper string.lcs. + Remove named string concat, need for which is mostly obviated by + concat metamethod. + + Remove @module from list of tags to add, as we already have it. + + Clarify Reuben's role. + + Remove rex.lua, now imported from lrexlib + + No longer need to lift std modules into global environment as they are already there. + + Remove std/ prefix for module files, and no longer include std.lua, which does nothing. + + Remove std directory, having all modules at the root, and with root names (no "std." prefix), so that stdlib can rely on external libraries, and the namespace is simplified. + + file.lua is no more + + Rewrite io.length using posix.stat and move it to io.lua. + + Move TODO from rex.lua + +2006-11-03 rrt + + No longer mention defunct bit.lua. + + Remove listable and listabled functions. This wasn't really that useful, and could confuse. + +2006-11-01 rrt + + Correct @function to @func + + Really cope with multiple params under a single @param. + + Add template for std-using scripts. + + Clarify gmatch metamethod + + Cope with multiple params under a single @param, by insisting on the : at the end. + +2006-10-30 rrt + + Fix list.concat (thanks to Avi Yagodnick for reporting the bug). + +2006-10-28 niklas + + Remove require loops by commenting out looping requires. This needs fixing properly (by permitting them). + Where possible without further change, remove the "std" prefix from + module declarations. + + Where possible, remove module prefixes from function definitions. + + Use ... in more places. + + Move pathSubscript to table.lua. + + Move assert.lua's contents to base.lua (we can't have a module called + assert, and this is in the base library in any case). + + Move function forms of operators to base.lua. + + Make headings of modules consistent (add @module lines). + + Remove io.exists, as it's dodgy, and posix.stat is much better. + Lighterweight environments are probably going to roll their own + anyway. + + Remove "zip" and "unzip" aliases for list.transpose. Add a note to the + documentation instead. + + In io.lua, remove some io. prefixes (that don't make the code less + clear) as we're already in io, and instead prefix type with _G (oops, + that's ugly). + + Improve changeSuffix, and make it use posix.dirname and + posix.basename. + + Add two functions to list module: flatten and shape, to flatten and + reshape arbitrarily nested lists. + + Add a paragraph of documentation to the top of the rex module in + preparation for lrexlib 1.20 (rex.lua will leave stdlib and move to + lrexlib). + + Add rex.gmatch (as well as adding a metatable method for rex objects). + + Update set.lua to current practices, including the (still + commented-out) metamethods. + + Probable bug-fixing in string module obscured by removal of string. + prefix from function definitions and calls. + + Make redefinitions of existing functions more consistent, and fix some + faulty ones. + + Add deepclone to table from Jamie Webb's code. + +2006-10-08 rrt + + Update to match stdlib. Remove revision history as it's in CVS, and replace version number with CVS Revision tag. + + table.getn --> # + +2006-09-30 rrt + + Remove io.readDir, as it is replaced by posix.dir. + + Remove io.dirname, as it is replaced by posix.dirname. + + Fix changeSuffix to work with paths containing dots by only operating on basename, not the whole file name. + +2006-09-18 rrt + + Fix ordering of deps + +2006-07-14 rrt + + Escape quotes and apostrophes in string.escapeShell. + + Escape square brackets too in string.escapeShell. + +2006-04-25 rrt + + Prepend redefinition of require to the output. + + Use string methods rather than functions so that the functions here work on regexs as well. Add a note to make the whole API work properly with regexs as well as Lua patterns. + + Add TODO + + Reformat and improve comments. + +2006-04-24 rrt + + Simplify assignment of retry. + + Correct name of table.filterItem (was table.mapItem). + +2006-04-15 rrt + + Reformat. + + Use variadic bit.or. + + Add table.filter and table.filterItem. Add list.filterItem and implement list.filter in terms of it. + + Fix more bugs, patch from Shmuel Zeigerman. + Call rex:flags() to inject flags into rex table. + + Remove unnecessary local line from gmatch, and initialise st to 1, not 0 (thanks to Shmuel Zeigerman). + +2006-04-09 rrt + + Reorganise libraries with simpler names + + Move all modules out of string + + Reflect simplified structure + + Move all modules out of io + + Rename io.getopt to getopt + + Rename string.xml to xml + + Add utility to make a single file stdlib + + Use env to run script and reverse Changelog order + + Rename algorithm.lcs to lcs + +2006-04-09 rrt + + Rewrite string.split to be regex-system-neutral. + Change string.findl to return from and to in list form, not {from = f, + to = t}. + + Update string.regex to Lua 5.1 vararg syntax. + + Make io.exists use stat if available. + + Add io.dirname. + +2006-04-09 rrt + + Update Lua code from Shmuel's version and write gmatch in Lua. + + Update concat to Lua 5.1 vararg syntax. + Remove flatten alias for concat, as concat doesn't flatten. + + Update to 5.1 vararg syntax + +2006-03-30 rrt + + string.gfind is now string.gmatch. + + Reactivate tests, but make them conditional on running in debug mode. + + Improve installation instructions. + + Fix mailing list address. + + Update to match reality. + + Fix handling of global arg table. + + Use new form of message-less error. + +2006-03-29 rrt + + Deal with C modules like Lua modules. + + Don't mention require for Lua 4 any more. + + Rename modules *-ext to *_ext to avoid problem with version number parser in require. + + Simplify adding functions to global table. + +2006-03-28 rrt + + Add module calls everywhere, and do some necessary renaming to avoid clashes + +2006-03-22 rrt + + Use module and require in properly 5.1-compatible way, and change module names to work better with 5.1. + This should all still work fine with 5.0 using compat-5.1.lua. + +2006-02-03 rrt + + More fixes and tests from Shmuel Zeigerman. + +2006-01-28 rrt + + Add tests from Shmuel Zeigerman, reorganised somewhat. They are run when the module is loaded. + + Rename sub to cap for clarity (Shmuel Zeigerman). + +2006-01-26 rrt + + More fixes from Shmuel to mimic string.gsub better. + +2006-01-24 rrt + + Fix endless loop when pattern is .* (bug reported by Shmuel Zeigerman). + + Cope with capture being false (Shmuel Zeigerman). + + Fix bug when n == 0 (thanks Shmuel Zeigerman), and tidy up. + +2006-01-23 rrt + + Fix from Shmuel Zeigerman to match string.gsub better: when the pattern contains no captures, pass the entire match to the replacement function. + +2006-01-21 rrt + + More bug fixes; thanks to Shmuel Zeigerman for reporting the bugs and in one case giving the fix. + + Fix bugs with %n replacements in rex.gsub + + Make rex.gsub a full gsub for rex. + +2006-01-19 rrt + + Don't escape & in entities + + Improve rex.gsub + +2006-01-16 rrt + + Escape <, > and & in XML output. + +2005-11-23 rrt + + Replace deepipairs with treeIter to iterate properly over trees. + +2005-11-22 rrt + + Remove table.filterItem, as it really only works for lists. Inline the function in list.filter. + Add table.map. + + Add XML output, assuming Lua tables created by luaexpat. + +2005-11-21 rrt + + Add generic printing framework, and use it to add prettytostring. + +2005-11-18 rrt + + Use table.process to implement list processing functions. + + Add generic table-processing function table.process and action functions for map, filter &c. + + Add two iterators: ripairs which is like ipairs, but in reverse, and deepipairs, which recurses into nested tables. + +2005-11-09 rrt + + Remove import. + + Update year to 2005. + + import has been removed. + + Fix bogus version in history + + Remove import, and instead use 5.1-style require (tested with compat-5.1.lua). + Assume a LUA_DIR of "/", hence rename files accordingly. + +2005-09-04 rrt + + Fix string.ltrim and string.rtrim to return only the advertised return value. + Fix string.trim to do what it says on the tin; it was completely + broken. + +2004-09-08 rrt + + Fix assert when called with only one argument: arg has the value {n=0} when there are no variadic arguments, not nil as I assumed. + +2004-02-17 rrt + + Comment the constructor. + + Check error return when loading file, so that if file is not found we don't abort immediately so that all LUA_PATH entries are checked. + +2004-02-04 rrt + + Tidy up abstract syntax rules: there's now only one per production. + Keep action functions for more complex rules. Looks as though we only + ever have one or the other (because simple rules don't take into + account any housekeeping info) so perhaps simplify this again. + + Make lists not have a ty field, but just be lists of whatever they + contain. + + Return false instead of nil for empty parse trees, so as not to upset + ipairs iterations over lists. + +2004-02-04 rrt + + Tidy up the code, mostly by shortening the names of frequently-used variables. + + Allow import to report errors during importing. + +2004-02-03 rrt + + Added Diego Nahab for his mbox parser. + + Added Diego Nahab's mbox parser. + +2004-02-03 rrt + + Rework the API: now has a single method exposed, parse, and the other methods have been moved inside parse as local functions. The constructor no longer takes a token list. + Also, provide support for producing an abstract parse tree rather than + the (default) concrete one. + + Full details in the all-new documentation. + +2004-02-03 rrt + + Clarify note about import and add TODO about markup tags. + +2004-02-01 rrt + + Massacre the object implementation, reverting to implementing sets as simple tables, which seems to be better for general use. A lot of the code in this file is now non-functional; I'll be making it work later, integrating it with Jamie Webb's code. The module may get folded back into table. + + Make list.map and list.filter work on lists that have nil elements. + + Fix __call metamethod. + + writeLine -> io.writeLine + + Fix io.writeLine (somehow two lines had become swapped). + + writeLine -> io.writeLine + + Various modifications prompted by Jamie Webb's submission of his own standard library. So far I have assimilated improvements that map directly on to existing code, and also removed some functions that didn't seem to be that useful. Looking at the code again provoked other miscellaneous improvements. + +2004-01-31 rrt + + In the previous commit, which had a bogus log message, I fixed io.readDir: split --> string.split. + In this one, I improved the formatting of io.readDir slightly. + + Improve spacing of comments. + + If -version is given and no command-line args, then terminate after showing the version message. + + Explain that although import is used internally, users should use require (as in README) and why. + + Removed bit._INTEGER_BITS, because it's the same as math._INTEGER_BITS. I suspect I meant to do this ages ago. + + Added a few more people I thought of. + + Formalise the README: this project is no longer just mine. + Add a list of AUTHORS: the first contributions have arrived. + + This script was trivial and wrong. + + Added math.floor and math.round, based on code from Johann Hibschman. + + Add math.floor and math.round, based on code from Johann Hibschman. + + Fix pickle: format->string.format (thanks to Johann Hibschman). + +2004-01-28 rrt + + Default the from and to parameters of list.slice to the start and end of the list respectively. + + Add primitive way to cope with missing non-standard C libraries, and a TODO to deal with missing C libraries properly. + + Fix table.newDefault to use correct name for __index metamethod. + + Improve module description in first line. + + Fix tostring to work on self-referential tables. + +2004-01-27 rrt + + Corrected misnaming of functions and added documentation. + +2004-01-26 rrt + + Add string.format extension to make it not try to format if there is only one argument. + +2004-01-25 rrt + + Update to Lua 5. This is an old change which I forgot to check in; ldoc is *not* the way forward for stdlib documentation. This checkin is just for completeness. + +2004-01-10 rrt + + *** empty log message *** + +2004-01-10 rrt + + Change "returns" lines to "@returns" for better LuaDoc-ness. + Add string.tonumbersi. + + Minor corrections and LuaDocification of some other comments. + +2003-10-22 rrt + + More Lua 5 tweaks, and a couple of minor bugfixes. + +2003-10-20 rrt + + Oops; added file with incorrect name; re-add with correct name. + + Add "import" from LTN 11 to overcome require's problem with circular dependencies. + Remove string.next, as string.gfind provides an equivalent iterator. + + More renaming for consistency, and move more code around. This introduced the first cyclic dependency between modules since I moved to Lua 5, and I've had to cure this with a C include-style trick, since Lua 5 require just overflows the stack when there's a recursive call of require. + + Add an iterator for the values in a set, and use it; methods are now organised into those that access the data structure and those that call other methods. + +2003-10-19 rrt + + Objectify the implementation, and add LuaDoc-style markup to the comments. + + Write methods outside object prototype (i.e. in more consistent form for stdlib). + +2003-10-18 rrt + + Rename std.data.logic to std.bit, as it extends the C bit library. + + Make a namespace for list routines. + + Fix bug in curry properly. + + Finish renaming in io.io. Update TODO for getopt in std.lua. + + Re-add mistakenly removed logic (I confused the ability to take multiple args with the ability to take lists). + + assert lives again! math added to hold math function extensions. + + Remove logic (no longer needed; _INTEGER_BITS moved to math, band &c. already work on lists). Various other movements and renaming of modules. + + Routines moves to std.list and std.base. + + More renaming. Remove boolean routines for when bitlib is not present. There's no excuse in Lua 5! + + Use ipairs instead of table.getn loops. + + Use pairs () instead of deprecated for "i, v in t" form. + + Rename table routines. Simplify compose. + + Renamed some libraries for Lua 5-ification reasons. + +2003-10-17 rrt + + Fix a minor bug and remove some debugging code. + +2003-10-17 rrt + + A new module (and family): algorithm, with first member algorithm.lcs, which implements the longest common subsequence algorithm needed for diff. + std.object has been reworked, and now fits much better with Lua 5, + although the interface to it is pretty much the same as before. + + Some other Lua 5-isation has been done, but not much; there's still a + lot left to do in std.data in particular. + +2003-10-13 rrt + + Fix call to writeLine (now io.writeLine). + +2003-09-27 rrt + + More Lua 5-ification changes, mostly to the io modules this time. + +2003-09-25 uid30086 + + Another round of changes for Lua 5-ification. This completes the changes to the string library (used to be the text library), and adds std.rex (complements my C rex library). Other changes are mostly to accomodate this; a few extras have snuck in. + +2003-09-24 rrt + + First set of changes moving to Lua 5-like naming conventions for the libraries. + +2003-09-14 rrt + + Use math.mod rather than bit.mod for wider compatibility. + + Wrap notes field of prog structure before output. + +2003-09-12 rrt + + More changes to update to Lua 5.0. Nearly there now, I think, as I have several scripts working! + +2003-09-11 rrt + + Another few search-and-replace function names to update to Lua 5. Mostly string functions this time. + +2003-09-09 rrt + + More search-and-replace and wholesale code removal (notably POSIX getopt) for Lua 5. + + Another swathe of Lua 5 updates. Now my little script that I'm testing nearly works, which means that quite a lot of the code in the libraries is at least vaguely correct! + +2003-09-08 rrt + + A slew of updates in the march to Lua-5-ify the libraries. I've just been working on a particular small script and changing things "on demand", and I've not even managed to make the script work yet, so there's almost certainly a lot of work still to go. + +2003-06-03 rrt + + Convert to Lua 5.0, and some slight tidying. + + Do TODOs for Lua 5 (use "le" tagmethod -- will have to become a metamethod) and ability to force a function to return only one result. + + Update std.patch40 to std.patch50 for Lua 5. Now none of the other modules need it. + + Make readDir return an unsorted list of files. Unfortunately, -U isn't supported by ls on all platforms. + +2003-03-13 rrt + + Update to match new directory structure (rather overdue!). + +2003-01-05 rrt + + Use endOfLine in chomp and wrap. + +2002-10-17 rrt + + Removed std.logic; now folded into std.data.logic + + std.logic now merged into std.data.logic. + + Merge the two logic modules. + +2002-09-28 rrt + + Add tabulate function to use tabulator methods, and use it. + +2002-09-10 rrt + + Sigh. New bnot didn't work. Next time I'll think and test rather better before straying from the path of righteousness. + + I was being very dim about bnot. Oops. Roberto pointed it out. I hang my head in shame. + + Revert to previous version to avoid losing precision (specifically, LSB). + +2002-09-09 rrt + + Shorter implementation of bxor, and bnot thanks to a remark by Paul Hsieh on lual (Message-Id: <0H26009OMQ9J59@mta5.snfc21.pbi.net>). + Kept band (as the primitive to make bxor) and bor (because de + Morganising it would involve a call to bnot and two to band, hence + making it three times slower). + +2002-09-08 rrt + + Poor man's logic functions (for those who can't use bitlib). Also calculate the number of bits in the word. + + Add std.logic + +2002-09-06 rrt + + Improve layout of usage message when no command line options (don't have trailing blank line). + +2002-09-05 rrt + + Add withFileOrHandle, which takes a filename, handle or uses a default handle, opens the file if appropriate, and passes the handle to a given function. + Use it to generalise readLines and readFile. + + It's tempting to generalise writeLine too, but writeLine ("foo", "bar") is + ambiguous: do we want to write "foo" and "bar" to _OUTPUT, or "bar" to + file "foo"? + +2002-09-05 rrt + + Make patches work with any version that starts with "Lua 4.0", to cope with 4.0.1 and any future point releases. + Replace unpack with a recursive version (based on code from John + Belmonte) that copes with any number of values. + + Change "key" to "index" everywhere for consistency. + + Improve comments for tinvert + + Change intersect tag method to division, and add TODO to implement proper subset tagmethod in Lua 5.0. + + Rename intersect to setintersect for consistency, and define setunion (= merge). + +2002-08-28 rrt + + Allow stringifier methods again, but they are now only used by tostring. Allows more cosmetic stringification, while not stopping pickling from working. + +2002-08-27 rrt + + Don't need stringify and pickler tables any more, and tostring and pickle can be simplified. They both use tabulator where necessary. + + Remove interaction between pickle and tostring, which is no longer needed, as they both now use tabulator methods where necessary. + + Add tabulator method table for turning arbitrary objects (typically tagged userdata) into tables. Use this to finally fix tostring and pickle. Oh, yes. + +2002-08-23 rrt + + A last gamble. Then I'll have to sit down and work it out again. + + Another desperate attempt. + + Fix up. I hope. This is starting to drive me insane. + + Typo. + + Fix default case for pickling. + + Fix typo. + + More fixes. + + More fixes to pickling. I got in a pickle with this one. + + Make pickling work properly on numbers and nil by having a "self" parameter for stringifier and pickler functions. They're more like classes now. + + Fix buglet (can't concat to nil). + + Make pickle work for numbers and nil. + + Add tinvert, and update some comments to LDoc format. + +2002-08-22 rrt + + Correct call of warn to expand arg list. + +2002-08-15 rrt + + Add utility for making zip dist of stdlib. + + Add empty to test whether a table is. + + Fix paths for new directory structure and get rid of one or two gremlins. + + Finish editing std.cfg into new form (configuration file with require implementation tacked on the end) and rename it. + + Update some TODOs. + + Rename std->modules and remove stray std.lua + +2002-08-14 rrt + + Standardize code style, and make changes (mostly to the comments) to prepare for renaming to std.config, as per John's suggestion. This file will only secondarily contain require, and will typically be built into the Lua system anyway. When we move to Lua 5.0, require will disappear, anyway. + + John Belmonte's replacement require implementation (5.0-compatible). + + Reorganise directory structure to a flat directory, to cope with Lua 5.0 require patterns (so that the libraries can be loaded without making assumptions about directory separators). + +2002-08-13 rrt + + Add the format library to text. + + Fix buglet in warn. + + Move some functions to format.lua to avoid dependency loop text<->assert and to make way for the new pretty printing functions described in a big TODO in format.lua. + Override format in text, so that if it's only passed one argument, it + just returns it. + + Use capability of warn to take format arguments. + +2002-08-13 rrt + + Rename affirm to assert, and pass its arguments to the new format function. + Remove the *f functions (which called format); these were unused. Now + warn, die and assert can all take format arguments. + + affirm->assert in file.lua + +2002-08-13 rrt + + Correct punctuation. + +2002-08-12 rrt + + Fix a typo loading pickle.lua in text.lua + Make pickle escape characters in strings that need it + + Restructure stringifying so that functions in stringifier can produce either a string or a table of stringified index = stringified value pairs. + Use this to write a simple pickle function that can pickle anything + that tostring can stringify. pickle and tostring are now effectively + just different renderers for the functions in stringifier. + + Fix spacing in comments. + + Allow new tostring methods to be registered. + + *** empty log message *** + + Debug defaultTable so it uses the initial value given rather than always creating an empty table. + + Add defaultTable to macro. + Move lookup to macro, reimplement it in terms of foldl and subscript, + and reimplement pathSubscript in terms of it. + + Add a logic module to extend band, bor and bxor to lists (just listable them). + + Comment list.lua in ldoc format. The other modules will probably follow. + + Include the new macro module. + + Move pathSubscript out of table.lua to avoid a circular dependency, into the new macro.lua module, which also includes some material moved from global.lua. + +2002-08-01 rrt + + Move print from assert to debug. This not only makes sense, but breaks a recursive dependency between assert and text/text. + +2002-07-30 rrt + + Remove require for now-defunct time.lua, and tidy up the TODOs. + + Correct endofLine -> endOfLine again; this must have crept back in with the last diff. Using CVS everywhere rather than manual copying will be a Good Thing in this respect! + +2002-07-29 rrt + + Generalised daySuffix to ordinalSuffix. Still English-specific :-( + +2002-07-27 rrt + + Improve comment for mapIter + +2002-07-26 rrt + + Add constant, a constant function generator. + + Use pathSubscript in methodify to allow more convenient macroization of tables. + + A simple documentation extracter, relying completely on specially formatted comments. There's no documentation at the moment except the patterns in the program, which should be obvious! I'll ldocify all the code shortly and check in instructions with it. + This tool is provisional, and subject to improvement. The TODOs in the + file indicate some of my first thoughts in that direction. + +2002-07-24 rrt + + Move methodify to table.lua where it belongs (it has nothing to do with lists!) + + Allow scripts to have no arguments. If you want to display help when just the script is run with no arguments, you'll have to do it manually. + + Throw away stderr from shell commands (we don't expect the output to clutter up the screen; it's always possible to capture it by adding a redirection to the command, which will override the one we add). + + Add subscript, which exposes [] as a function. + +2002-06-22 rrt + + Correct endofLine -> endOfLine + + Initial revision diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..f477d0a --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,82 @@ +# Maintainer rules. +# +# Copyright (C) 2013-2015 Gary V. Vaughan +# Written by Gary V. Vaughan, 2013 +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +ME = GNUmakefile + +# If the user runs GNU make but didn't ./configure yet, do it for them. +dont-forget-to-bootstrap = $(wildcard Makefile.in) + +ifeq ($(dont-forget-to-bootstrap),) + +## Don't redo any pedantic rock version checks, incase they derail +## a subdirectory bootstrap of slingshot. + +%:: + @echo '$(ME): rebootstrap' + @test -f Makefile.in || ./bootstrap --skip-rock-checks + @test -f Makefile || ./configure + $(MAKE) $@ + +else + + +include build-aux/release.mk +include build-aux/sanity.mk + +# Run sanity checks as part of distcheck. +distcheck: $(local-check) + + +## ------------------------- ## +## Copyright Notice Updates. ## +## ------------------------- ## + +# If you want to set UPDATE_COPYRIGHT_* environment variables, +# for the build-aux/update-copyright script: set the assignments +# in this variable in local.mk. +update_copyright_env ?= + +# Run this rule once per year (usually early in January) +# to update all FSF copyright year lists in your project. +# If you have an additional project-specific rule, +# add it in local.mk along with a line 'update-copyright: prereq'. +# By default, exclude all variants of COPYING; you can also +# add exemptions (such as ChangeLog..* for rotated change logs) +# in the file .x-update-copyright. +.PHONY: update-copyright +update-copyright: + $(AM_V_GEN)grep -l -w Copyright \ + $$(export VC_LIST_EXCEPT_DEFAULT=COPYING && $(VC_LIST_EXCEPT)) \ + | $(update_copyright_env) xargs $(srcdir)/build-aux/$@ + + +## ------ ## +## Specl. ## +## ------ ## + +# Use 'make check V=1' for verbose output, or set SPECL_OPTS to +# pass alternative options to specl command. + +SPECL_OPTS ?= +SPECL_OPTS += $(specl_verbose_$(V)) +specl_verbose_ = $(specl_verbose_$(AM_DEFAULT_VERBOSITY)) +specl_verbose_0 = +specl_verbose_1 = --verbose --formatter=report + + +endif diff --git a/INSTALL b/INSTALL index 7d1c323..2099840 100644 --- a/INSTALL +++ b/INSTALL @@ -1,8 +1,8 @@ Installation Instructions ************************* -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2007, 2008, 2009 Free Software Foundation, Inc. +Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, +Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright @@ -12,8 +12,8 @@ without warranty of any kind. Basic Installation ================== - Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following + Briefly, the shell command `./configure && make && make install' +should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented @@ -226,6 +226,11 @@ order to use an ANSI C compiler: and if that doesn't work, install pre-built binaries of GCC for HP-UX. + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended @@ -304,9 +309,10 @@ causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: +an Autoconf limitation. Until the limitation is lifted, you can use +this workaround: - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== @@ -362,4 +368,3 @@ operates. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. - diff --git a/Makefile.am b/Makefile.am index 578ea34..4ade870 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,66 +1,109 @@ -## Process this file with automake to produce Makefile.in +# Non-recursive Make rules. +# +# Copyright (C) 2013-2015 Gary V. Vaughan +# Written by Gary V. Vaughan, 2013 +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +## ------------ ## +## Environment. ## +## ------------ ## + +LUA_PATH ?= ; +LUA_CPATH ?= ; + +SPECL_ENV = +CHECK_ENV = +INSTALLCHECK_ENV = + + +## ---------- ## +## Bootstrap. ## +## ---------- ## ACLOCAL_AMFLAGS = -I m4 -LUA_PATH ?= ; -LUA_ENV = LUA_PATH="$(abs_srcdir)/src/?.lua;$(LUA_PATH)" - -SOURCES = $(wildcard $(srcdir)/src/*.lua) $(srcdir)/src/std.lua -dist_data_DATA = $(SOURCES) - -dist_doc_DATA = \ - $(top_srcdir)/src/index.html \ - $(top_srcdir)/src/luadoc.css -filesdir = $(docdir)/files -dist_files_DATA = $(wildcard $(top_srcdir)/src/files/*.html) -modulesdir = $(docdir)/modules -dist_modules_DATA = $(wildcard $(top_srcdir)/src/modules/*.html) - -EXTRA_DIST = \ - src/std.lua.in \ - $(PACKAGE).rockspec.in - -DISTCLEANFILES = $(PACKAGE).rockspec - -# In order to avoid regenerating std.lua at configure time, which -# causes the documentation to be rebuilt and hence requires users to -# have luadoc installed, put src/std.lua in as a Makefile dependency. -# (Strictly speaking, distributing an AC_CONFIG_FILE would be wrong.) -src/std.lua: src/std.lua.in - ./config.status --file=$@ - -$(dist_doc_DATA): $(SOURCES) - cd src && $(LUADOC) *.lua - -rockspecs: - rm -f *.rockspec - $(LUA_ENV) $(LUA) mkrockspecs.lua $(PACKAGE) $(VERSION) - $(LUA_ENV) $(LUA) mkrockspecs.lua $(PACKAGE) git - -bootstrap: - autoreconf -i && \ - ./configure - -tag-release: - git diff --exit-code && \ - git tag -a -m "Release tag" v$(VERSION) && \ - git push && git push --tags - -check-in-release: distcheck - git checkout release && \ - tar zxf $(PACKAGE)-$(VERSION).tar.gz && \ - cp -af $(PACKAGE)-$(VERSION)/* . && \ - git add . && git ci -m "Release v$(VERSION)" && \ - git tag -a -m "Full source release tag" release-v$(VERSION) && \ - git push && git push --tags && \ - git checkout master && \ - rm -rf $(PACKAGE)-$(VERSION)/ - -# After check-in-release we need to bootstrap to get the build files back -release: rockspecs - $(MAKE) tag-release && \ - $(MAKE) check-in-release && \ - $(MAKE) bootstrap && \ - $(MAKE) rockspecs && \ - LUAROCKS_CONFIG=$(abs_srcdir)/luarocks-config.lua luarocks --tree=$(abs_srcdir)/luarocks build $(PACKAGE)-$(VERSION)-1.rockspec && \ - woger lua package=$(PACKAGE) package_name=$(PACKAGE_NAME) version=$(VERSION) description="`LUA_INIT= LUA_PATH='$(abs_srcdir)/?.rockspec.in' $(LUA) -l$(PACKAGE) -e 'print (description.summary)'`" notes=release-notes-$(VERSION) home="`LUA_INIT= LUA_PATH='$(abs_srcdir)/?.rockspec.in' $(LUA) -l$(PACKAGE) -e 'print (description.homepage)'`" +AM_CPPFLAGS = $(LUA_INCLUDE) + + +## ------------- ## +## Declarations. ## +## ------------- ## + +EXTRA_DIST = +EXTRA_LTLIBRARIES = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = +NOTHING_ELSE = + +bin_SCRIPTS = +check_local = +dist_bin_SCRIPTS = +dist_lua_DATA = +doc_DATA = +installcheck_local = +install_exec_hooks = remove-luaexec-lafiles +uninstall_hooks = uninstall-luaexec-modules +lib_LTLIBRARIES = +luaexec_LTLIBRARIES = +man_MANS = +save_release_files = + +include local.mk +include build-aux/rockspecs.mk + + +## ------------ ## +## Local Tests. ## +## ------------ ## + +check-local: $(check_local) + + +## ------------- ## +## Installation. ## +## ------------- ## + +installcheck-local: $(installcheck_local) + +install-exec-hook: $(install_exec_hooks) + +# Neither Lua itself, nor LuaRocks can use .la files, and LuaRocks +# actually moves such files aside anyway, so we just remove them from +# the installation directory. +remove-luaexec-lafiles: + @for la in $(luaexec_LTLIBRARIES); do \ + f=`echo "$$la" |sed 's|^.*/||'`; \ + echo rm -f $(DESTDIR)$(luaexecdir)/$$f; \ + rm -f $(DESTDIR)$(luaexecdir)/$$f; \ + done + + +## --------------- ## +## Uninstallation. ## +## --------------- ## + +uninstall-hook: $(uninstall_hooks) + +# We removed the .la files from luaexecdir, so the standard uninstall, +# with libtool --mode=uninstall, can't find everything anymore. +uninstall-luaexec-modules: + @for la in $(luaexec_LTLIBRARIES); do \ + base=`echo "$$la" \ + |sed 's|^.*/\(.*\)\.la|\1|'`; \ + echo rm -f $(DESTDIR)$(luaexecdir)/$$base.so; \ + rm -f $(DESTDIR)$(luaexecdir)/$$base.so; \ + done diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..1082e7c --- /dev/null +++ b/Makefile.in @@ -0,0 +1,1244 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Non-recursive Make rules. +# +# Copyright (C) 2013-2015 Gary V. Vaughan +# Written by Gary V. Vaughan, 2013 +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Local Make rules. +# +# Copyright (C) 2013-2015 Gary V. Vaughan +# Written by Gary V. Vaughan, 2013 +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Specl specs make rules. + +# Slingshot specl rules for make. + +# This file is distributed with Slingshot, and licensed under the +# terms of the MIT license reproduced below. + +# ==================================================================== # +# Copyright (C) 2013-2015 Gary V. Vaughan # +# # +# 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. # +# ==================================================================== # + +# To use this file create a list of your spec files in specl_SPECS +# and then include this make fragment. + +# Slingshot rockspec rules for make. + +# This file is distributed with Slingshot, and licensed under the +# terms of the MIT license reproduced below. + +# ==================================================================== # +# Copyright (C) 2013-2015 Reuben Thomas and Gary V. Vaughan # +# # +# 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. # +# ==================================================================== # + +# This file is suitable for use from a portable Makefile, you might +# include it into the top-level Makefile.am with: +# +# include build-aux/rockspecs.mk + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lua.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(dist_bin_SCRIPTS) $(dist_classes_DATA) \ + $(dist_doc_DATA) $(dist_lua_DATA) $(dist_luastd_DATA) \ + $(dist_luastddebug_DATA) $(dist_modules_DATA) \ + $(am__DIST_COMMON) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = build-aux/config.ld +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(luaexecdir)" \ + "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(classesdir)" "$(DESTDIR)$(docdir)" \ + "$(DESTDIR)$(luadir)" "$(DESTDIR)$(luastddir)" \ + "$(DESTDIR)$(luastddebugdir)" "$(DESTDIR)$(modulesdir)" \ + "$(DESTDIR)$(docdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(luaexec_LTLIBRARIES) +SCRIPTS = $(bin_SCRIPTS) $(dist_bin_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(dist_classes_DATA) $(dist_doc_DATA) $(dist_lua_DATA) \ + $(dist_luastd_DATA) $(dist_luastddebug_DATA) \ + $(dist_modules_DATA) $(doc_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/build-aux/rockspecs.mk $(srcdir)/build-aux/specl.mk \ + $(srcdir)/local.mk $(srcdir)/specs/specs.mk \ + $(top_srcdir)/build-aux/config.ld.in \ + $(top_srcdir)/build-aux/install-sh \ + $(top_srcdir)/build-aux/missing AUTHORS COPYING ChangeLog \ + INSTALL NEWS README build-aux/install-sh build-aux/missing +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDOC = @LDOC@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LUA = @LUA@ +LUA_EXEC_PREFIX = @LUA_EXEC_PREFIX@ +LUA_PLATFORM = @LUA_PLATFORM@ +LUA_PREFIX = @LUA_PREFIX@ +LUA_SHORT_VERSION = @LUA_SHORT_VERSION@ +LUA_VERSION = @LUA_VERSION@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPECL = @SPECL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +am__leading_dot = @am__leading_dot@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +luadir = @luadir@ +luaexecdir = @luaexecdir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgluadir = @pkgluadir@ +pkgluaexecdir = @pkgluaexecdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SPECL_ENV = +CHECK_ENV = LUA='$(LUA)' PACKAGE_STRING='$(PACKAGE_STRING)' \ + abs_top_builddir='$(abs_top_builddir)' \ + abs_top_srcdir='$(abs_top_srcdir)' \ + top_builddir='$(top_builddir)' top_srcdir='$(top_srcdir)' \ + $(NOTHING_ELSE) +INSTALLCHECK_ENV = LUA='$(LUA)' PACKAGE_STRING='$(PACKAGE_STRING)' \ + installcheck='true' $(NOTHING_ELSE) +ACLOCAL_AMFLAGS = -I m4 +AM_CPPFLAGS = $(LUA_INCLUDE) +EXTRA_DIST = $(srcdir)/specs/spec_helper.lua $(NOTHING_ELSE) \ + $(specl_SPECS) $(NOTHING_ELSE) build-aux/config.ld.in \ + lib/std.lua.in $(NOTHING_ELSE) $(mkrockspecs) \ + $(package_rockspec) $(rockspec_conf) $(NOTHING_ELSE) +EXTRA_LTLIBRARIES = +CLEANFILES = +DISTCLEANFILES = $(luarocks_config) $(NOTHING_ELSE) +MAINTAINERCLEANFILES = +NOTHING_ELSE = +bin_SCRIPTS = +check_local = specl-check-local +dist_bin_SCRIPTS = +dist_lua_DATA = lib/std.lua $(NOTHING_ELSE) +doc_DATA = +installcheck_local = specl-installcheck-local +install_exec_hooks = remove-luaexec-lafiles +uninstall_hooks = uninstall-luaexec-modules +lib_LTLIBRARIES = +luaexec_LTLIBRARIES = +man_MANS = +save_release_files = $(scm_rockspec) +std_path = $(abs_srcdir)/lib/?.lua;$(abs_srcdir)/lib/?/init.lua +LUA_ENV = LUA_PATH="$(std_path);$(LUA_PATH)" +old_NEWS_hash = d41d8cd98f00b204e9800998ecf8427e +update_copyright_env = \ + UPDATE_COPYRIGHT_HOLDER='(Gary V. Vaughan|Reuben Thomas)' \ + UPDATE_COPYRIGHT_USE_INTERVALS=1 \ + UPDATE_COPYRIGHT_FORCE=1 + +classesdir = $(docdir)/classes +modulesdir = $(docdir)/modules +dist_doc_DATA = $(srcdir)/doc/index.html $(srcdir)/doc/ldoc.css +dist_classes_DATA = $(srcdir)/doc/classes/std.container.html \ + $(srcdir)/doc/classes/std.list.html \ + $(srcdir)/doc/classes/std.object.html \ + $(srcdir)/doc/classes/std.optparse.html \ + $(srcdir)/doc/classes/std.set.html \ + $(srcdir)/doc/classes/std.strbuf.html \ + $(srcdir)/doc/classes/std.tree.html $(NOTHING_ELSE) +dist_modules_DATA = $(srcdir)/doc/modules/std.html \ + $(srcdir)/doc/modules/std.debug.html \ + $(srcdir)/doc/modules/std.functional.html \ + $(srcdir)/doc/modules/std.io.html \ + $(srcdir)/doc/modules/std.math.html \ + $(srcdir)/doc/modules/std.operator.html \ + $(srcdir)/doc/modules/std.package.html \ + $(srcdir)/doc/modules/std.strict.html \ + $(srcdir)/doc/modules/std.string.html \ + $(srcdir)/doc/modules/std.table.html $(NOTHING_ELSE) +SPECL_OPTS = --unicode +specl_SPECS = \ + $(srcdir)/specs/container_spec.yaml \ + $(srcdir)/specs/debug_spec.yaml \ + $(srcdir)/specs/functional_spec.yaml \ + $(srcdir)/specs/io_spec.yaml \ + $(srcdir)/specs/list_spec.yaml \ + $(srcdir)/specs/math_spec.yaml \ + $(srcdir)/specs/object_spec.yaml \ + $(srcdir)/specs/operator_spec.yaml \ + $(srcdir)/specs/optparse_spec.yaml \ + $(srcdir)/specs/package_spec.yaml \ + $(srcdir)/specs/set_spec.yaml \ + $(srcdir)/specs/strbuf_spec.yaml \ + $(srcdir)/specs/string_spec.yaml \ + $(srcdir)/specs/table_spec.yaml \ + $(srcdir)/specs/tree_spec.yaml \ + $(srcdir)/specs/std_spec.yaml \ + $(NOTHING_ELSE) + +luastddir = $(luadir)/std +dist_luastd_DATA = \ + lib/std/base.lua \ + lib/std/container.lua \ + lib/std/debug.lua \ + lib/std/functional.lua \ + lib/std/io.lua \ + lib/std/list.lua \ + lib/std/math.lua \ + lib/std/object.lua \ + lib/std/operator.lua \ + lib/std/optparse.lua \ + lib/std/package.lua \ + lib/std/set.lua \ + lib/std/strbuf.lua \ + lib/std/strict.lua \ + lib/std/string.lua \ + lib/std/table.lua \ + lib/std/tree.lua \ + $(NOTHING_ELSE) + + +# For bugwards compatibility with LuaRocks 2.1, while ensuring that +# `require "std.debug_init"` continues to work, we have to install +# the former `$(luadir)/std/debug_init.lua` to `debug_init/init.lua`. +# When LuaRocks works again, move this file back to dist_luastd_DATA +# above and rename to debug_init.lua. +luastddebugdir = $(luastddir)/debug_init +dist_luastddebug_DATA = \ + lib/std/debug_init/init.lua \ + $(NOTHING_ELSE) + +mkrockspecs_args = --module-dir $(srcdir)/lib --repository lua-stdlib +luarocks_config = build-aux/luarocks-config.lua +rockspec_conf = $(srcdir)/rockspec.conf +mkrockspecs = $(srcdir)/build-aux/mkrockspecs +package_rockspec = $(srcdir)/$(PACKAGE)-$(VERSION)-$(rockspec_revision).rockspec +scm_rockspec = $(PACKAGE)-git-$(rockspec_revision).rockspec + +# If you need a different rockspec revision, override this on the make +# command line: +# +# make rockspecs rockspec_revision=2 +rockspec_revision = 1 +LUAROCKS = luarocks +MKROCKSPECS = $(MKROCKSPECS_ENV) $(LUA) $(mkrockspecs) +ROCKSPECS_DEPS = \ + $(luarocks_config) \ + $(mkrockspecs) \ + $(rockspec_conf) \ + $(NOTHING_ELSE) + +set_LUA_BINDIR = LUA_BINDIR=`which $(LUA) |$(SED) 's|/[^/]*$$||'` +LUA_INCDIR = `cd $$LUA_BINDIR/../include && pwd` +LUA_LIBDIR = `cd $$LUA_BINDIR/../lib && pwd` +all: all-am + +.SUFFIXES: +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/local.mk $(srcdir)/specs/specs.mk $(srcdir)/build-aux/specl.mk $(srcdir)/build-aux/rockspecs.mk $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; +$(srcdir)/local.mk $(srcdir)/specs/specs.mk $(srcdir)/build-aux/specl.mk $(srcdir)/build-aux/rockspecs.mk $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): +build-aux/config.ld: $(top_builddir)/config.status $(top_srcdir)/build-aux/config.ld.in + cd $(top_builddir) && $(SHELL) ./config.status $@ + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(INSTALL) $(INSTALL_STRIP_FLAG) $$list '$(DESTDIR)$(libdir)'"; \ + $(INSTALL) $(INSTALL_STRIP_FLAG) $$list "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +install-luaexecLTLIBRARIES: $(luaexec_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(luaexec_LTLIBRARIES)'; test -n "$(luaexecdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(luaexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(luaexecdir)" || exit 1; \ + echo " $(INSTALL) $(INSTALL_STRIP_FLAG) $$list '$(DESTDIR)$(luaexecdir)'"; \ + $(INSTALL) $(INSTALL_STRIP_FLAG) $$list "$(DESTDIR)$(luaexecdir)"; \ + } + +uninstall-luaexecLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(luaexec_LTLIBRARIES)'; test -n "$(luaexecdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(luaexecdir)/$$f'"; \ + rm -f "$(DESTDIR)$(luaexecdir)/$$f"; \ + done + +clean-luaexecLTLIBRARIES: + -test -z "$(luaexec_LTLIBRARIES)" || rm -f $(luaexec_LTLIBRARIES) + @list='$(luaexec_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +install-binSCRIPTS: $(bin_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) +install-dist_binSCRIPTS: $(dist_bin_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-dist_binSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) +install-dist_classesDATA: $(dist_classes_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_classes_DATA)'; test -n "$(classesdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(classesdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(classesdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(classesdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(classesdir)" || exit $$?; \ + done + +uninstall-dist_classesDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_classes_DATA)'; test -n "$(classesdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(classesdir)'; $(am__uninstall_files_from_dir) +install-dist_docDATA: $(dist_doc_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ + done + +uninstall-dist_docDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) +install-dist_luaDATA: $(dist_lua_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_lua_DATA)'; test -n "$(luadir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(luadir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(luadir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(luadir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(luadir)" || exit $$?; \ + done + +uninstall-dist_luaDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_lua_DATA)'; test -n "$(luadir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(luadir)'; $(am__uninstall_files_from_dir) +install-dist_luastdDATA: $(dist_luastd_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_luastd_DATA)'; test -n "$(luastddir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(luastddir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(luastddir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(luastddir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(luastddir)" || exit $$?; \ + done + +uninstall-dist_luastdDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_luastd_DATA)'; test -n "$(luastddir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(luastddir)'; $(am__uninstall_files_from_dir) +install-dist_luastddebugDATA: $(dist_luastddebug_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_luastddebug_DATA)'; test -n "$(luastddebugdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(luastddebugdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(luastddebugdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(luastddebugdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(luastddebugdir)" || exit $$?; \ + done + +uninstall-dist_luastddebugDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_luastddebug_DATA)'; test -n "$(luastddebugdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(luastddebugdir)'; $(am__uninstall_files_from_dir) +install-dist_modulesDATA: $(dist_modules_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_modules_DATA)'; test -n "$(modulesdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(modulesdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(modulesdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(modulesdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(modulesdir)" || exit $$?; \ + done + +uninstall-dist_modulesDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_modules_DATA)'; test -n "$(modulesdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(modulesdir)'; $(am__uninstall_files_from_dir) +install-docDATA: $(doc_DATA) + @$(NORMAL_INSTALL) + @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ + done + +uninstall-docDATA: + @$(NORMAL_UNINSTALL) + @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build/sub \ + && ../../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=../.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-local +check: check-am +all-am: Makefile $(LTLIBRARIES) $(SCRIPTS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(luaexecdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(classesdir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(luadir)" "$(DESTDIR)$(luastddir)" "$(DESTDIR)$(luastddebugdir)" "$(DESTDIR)$(modulesdir)" "$(DESTDIR)$(docdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-luaexecLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dist_classesDATA install-dist_docDATA \ + install-dist_luaDATA install-dist_luastdDATA \ + install-dist_luastddebugDATA install-dist_modulesDATA \ + install-docDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binSCRIPTS install-dist_binSCRIPTS \ + install-libLTLIBRARIES install-luaexecLTLIBRARIES + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: installcheck-local + +maintainer-clean: maintainer-clean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binSCRIPTS uninstall-dist_binSCRIPTS \ + uninstall-dist_classesDATA uninstall-dist_docDATA \ + uninstall-dist_luaDATA uninstall-dist_luastdDATA \ + uninstall-dist_luastddebugDATA uninstall-dist_modulesDATA \ + uninstall-docDATA uninstall-libLTLIBRARIES \ + uninstall-luaexecLTLIBRARIES + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-hook +.MAKE: check-am install-am install-exec-am install-strip uninstall-am + +.PHONY: all all-am am--refresh check check-am check-local clean \ + clean-generic clean-libLTLIBRARIES clean-luaexecLTLIBRARIES \ + cscopelist-am ctags-am dist dist-all dist-bzip2 dist-gzip \ + dist-lzip dist-shar dist-tarZ dist-xz dist-zip distcheck \ + distclean distclean-generic distcleancheck distdir \ + distuninstallcheck dvi dvi-am html html-am info info-am \ + install install-am install-binSCRIPTS install-data \ + install-data-am install-dist_binSCRIPTS \ + install-dist_classesDATA install-dist_docDATA \ + install-dist_luaDATA install-dist_luastdDATA \ + install-dist_luastddebugDATA install-dist_modulesDATA \ + install-docDATA install-dvi install-dvi-am install-exec \ + install-exec-am install-exec-hook install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-luaexecLTLIBRARIES install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installcheck-local installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-am uninstall-binSCRIPTS uninstall-dist_binSCRIPTS \ + uninstall-dist_classesDATA uninstall-dist_docDATA \ + uninstall-dist_luaDATA uninstall-dist_luastdDATA \ + uninstall-dist_luastddebugDATA uninstall-dist_modulesDATA \ + uninstall-docDATA uninstall-hook uninstall-libLTLIBRARIES \ + uninstall-luaexecLTLIBRARIES + +.PRECIOUS: Makefile + + +LUA_PATH ?= ; +LUA_CPATH ?= ; +specl-check-local: $(specl_SPECS) + $(CHECK_ENV) $(SPECL_ENV) $(SPECL) $(SPECL_OPTS) $(specl_SPECS) +specl-installcheck-local: $(specl_SPECS) + $(INSTALLCHECK_ENV) $(SPECL_ENV) $(SPECL) $(SPECL_OPTS) $(specl_SPECS) + +# In order to avoid regenerating std.lua at configure time, which +# causes the documentation to be rebuilt and hence requires users to +# have ldoc installed, put std/std.lua in as a Makefile dependency. +# (Strictly speaking, distributing an AC_CONFIG_FILE would be wrong.) +lib/std.lua: lib/std.lua.in + ./config.status --file=$@ + +$(dist_doc_DATA) $(dist_classes_DATA) $(dist_modules_DATA): $(srcdir)/doc + +$(srcdir)/doc: $(dist_lua_DATA) $(dist_luastd_DATA) + test -d $@ || mkdir $@ + $(LDOC) -c build-aux/config.ld -d $(abs_srcdir)/doc . + +$(luarocks_config): Makefile.am + @test -d build-aux || mkdir build-aux + $(AM_V_GEN){ \ + $(set_LUA_BINDIR); \ + echo 'rocks_trees = { "$(abs_srcdir)/luarocks" }'; \ + echo 'variables = {'; \ + echo ' LUA = "$(LUA)",'; \ + echo ' LUA_BINDIR = "'$$LUA_BINDIR'",'; \ + echo ' LUA_INCDIR = "'$(LUA_INCDIR)'",'; \ + echo ' LUA_LIBDIR = "'$(LUA_LIBDIR)'",'; \ + echo '}'; \ + } > '$@' + +$(package_rockspec): $(ROCKSPECS_DEPS) + $(AM_V_at)rm -f '$@' 2>/dev/null || : + $(AM_V_GEN)test -f '$@' || \ + $(MKROCKSPECS) $(mkrockspecs_args) \ + $(PACKAGE) $(VERSION) $(rockspec_revision) > '$@' + $(AM_V_at)$(LUAROCKS) lint '$@' + +$(scm_rockspec): $(ROCKSPECS_DEPS) + $(AM_V_at)rm '$@' 2>/dev/null || : + $(AM_V_GEN)test -f '$@' || \ + $(MKROCKSPECS) $(mkrockspecs_args) \ + $(PACKAGE) git 1 > '$@' + $(AM_V_at)$(LUAROCKS) lint '$@' + +.PHONY: rockspecs +rockspecs: + $(AM_V_at)rm -f *.rockspec + $(AM_V_at)$(MAKE) $(package_rockspec) $(scm_rockspec) + +check-local: $(check_local) + +installcheck-local: $(installcheck_local) + +install-exec-hook: $(install_exec_hooks) + +# Neither Lua itself, nor LuaRocks can use .la files, and LuaRocks +# actually moves such files aside anyway, so we just remove them from +# the installation directory. +remove-luaexec-lafiles: + @for la in $(luaexec_LTLIBRARIES); do \ + f=`echo "$$la" |sed 's|^.*/||'`; \ + echo rm -f $(DESTDIR)$(luaexecdir)/$$f; \ + rm -f $(DESTDIR)$(luaexecdir)/$$f; \ + done + +uninstall-hook: $(uninstall_hooks) + +# We removed the .la files from luaexecdir, so the standard uninstall, +# with libtool --mode=uninstall, can't find everything anymore. +uninstall-luaexec-modules: + @for la in $(luaexec_LTLIBRARIES); do \ + base=`echo "$$la" \ + |sed 's|^.*/\(.*\)\.la|\1|'`; \ + echo rm -f $(DESTDIR)$(luaexecdir)/$$base.so; \ + rm -f $(DESTDIR)$(luaexecdir)/$$base.so; \ + done + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..7bd2b78 --- /dev/null +++ b/NEWS @@ -0,0 +1,1325 @@ +# Stdlib NEWS - User visible changes + +## Noteworthy changes in release 41.2.2 (2018-09-03) [stable] + +### New Features + + - Initial support for Lua 5.4 + + +## Noteworthy changes in release 41.2.1 (2017-08-07) [stable] + +### Bug fixes + + - `std.version` reports the correct release again. + + +## 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 + 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 + :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", } + ``` + + - 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. diff --git a/README b/README index 41d1ff2..d5a82a5 100644 --- a/README +++ b/README @@ -1,28 +1,48 @@ - Standard Lua libraries - ---------------------- +Standard Lua libraries +====================== - by the stdlib project (http://github.com/rrthomas/lua-stdlib/) +by the [stdlib project][github] +[github]: http://github.com/lua-stdlib/lua-stdlib/ "Github repository" -This is a collection of Lua libraries for Lua 5.1 and 5.2. The -libraries are copyright by their authors 2000-2013 (see the AUTHORS +[![travis-ci status](https://secure.travis-ci.org/lua-stdlib/lua-stdlib.png?branch=master)](http://travis-ci.org/lua-stdlib/lua-stdlib/builds) +[![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, 5.2 and 5.3. The +libraries are copyright by their authors 2000-2015 (see the AUTHORS file for details), and released under the MIT license (the same license as Lua itself). There is no warranty. -The standard subset of stdlib has no prerequisites beyond a standard -Lua system. The following modules have extra dependencies: - - fstable: Lua 5.2 +Stdlib has no prerequisites beyond a standard Lua system. Installation ------------ -The simplest way to install stdlib is with LuaRocks -(http://www.luarocks.org/ ): +The simplest way to install stdlib is with [LuaRocks][]. To install the +latest release (recommended): + + luarocks install stdlib + +To install current git master (for testing): -luarocks install stdlib + luarocks install https://raw.githubusercontent.com/lua-stdlib/lua-stdlib/release/stdlib-git-1.rockspec +To install without LuaRocks, check out the sources from the +[repository][github], and then run the following commands: the +dependencies are listed in the dependencies entry of the file +`stdlib-rockspec.lua`. You will also need autoconf and automake. + + cd lua-stdlib + autoreconf --force --version --install + ./configure --prefix=INSTALLATION-ROOT-DIRECTORY + make all check install + +See [INSTALL][] for instructions for `configure`. + +[luarocks]: http://www.luarocks.org "LuaRocks Project" +[install]: https://raw.githubusercontent.com/lua-stdlib/lua-stdlib/master/INSTALL Use --- @@ -30,7 +50,7 @@ Use As well as requiring individual libraries, you can load the standard set with - require "std" + require "std" Modules not in the standard set may be removed from future versions of stdlib. @@ -39,13 +59,15 @@ stdlib. Documentation ------------- -The libraries are documented in LuaDoc. Pre-built HTML files are -included. +The libraries are [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 maintained and extended by their users. Please -make bug report and suggestions on GitHub (see URL at top of file). -Pull requests are especially appreciated. +These libraries are written and maintained by their users. Please make +bug report and suggestions on GitHub (see URL at top of file). Pull +requests are especially appreciated. diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..dfb1151 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,741 @@ +# generated automatically by aclocal 1.15 -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# Copyright (C) 2002-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.15' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.15], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.15])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +dnl The trailing newline in this macro's definition is deliberate, for +dnl backward compatibility and to allow trailing 'dnl'-style comments +dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([m4/ax_lua.m4]) diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..0b70e37 --- /dev/null +++ b/bootstrap @@ -0,0 +1,5796 @@ +#! /bin/sh +## DO NOT EDIT - This file generated from build-aux/bootstrap.in +## by inline-source v2014-01-03.01 + +# Bootstrap an Autotooled package from checked-out sources. +# Written by Gary V. Vaughan, 2010 + +# Copyright (C) 2010-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Originally written by Paul Eggert. The canonical version of this +# script is maintained as build-aux/bootstrap in gnulib, however, to +# be useful to your project, you should place a copy of it under +# version control in the top-level directory of your project. The +# intent is that all customization can be done with a bootstrap.conf +# file also maintained in your version control; gnulib comes with a +# template build-aux/bootstrap.conf to get you started. + +# Please report bugs or propose patches to bug-gnulib@gnu.org. + + +## ------ ## +## Usage. ## +## ------ ## + +# Most GNUish projects do not keep all of the generated Autotool +# files under version control, but running all of the right tools +# with the right arguments, in the correct order to regenerate +# all of those files in readiness for configuration and building +# can be surprisingly involved! Many projects have a 'bootstrap' +# script under version control to invoke Autotools and perform +# other assorted book-keeping with version numbers and the like. +# +# This bootstrap script aims to probe the configure.ac and top +# Makefile.am of your project to automatically determine what +# the correct ordering and arguments are and then run the tools for +# you. In order to use it, you can generate an initial standalone +# script with: +# +# gl/build-aux/inline-source gl/build-aux/bootstrap.in > bootstrap +# +# You should then store than script in version control for other +# developers in you project. It will give you instructions about +# how to keep it up to date if the sources change. +# +# See gl/doc/bootstrap.texi for documentation on how to write +# a bootstrap.conf to customize it for your project's +# idiosyncracies. + + +## ================================================================== ## +## ## +## DO NOT EDIT THIS FILE, CUSTOMIZE IT USING A BOOTSTRAP.CONF ## +## ## +## ================================================================== ## + +## ------------------------------- ## +## User overridable command paths. ## +## ------------------------------- ## + +# All uppercase denotes values stored in the environment. These +# variables should generally be overridden by the user - however, we do +# set them to 'true' in some parts of this script to prevent them being +# called at the wrong time by other tools that we call ('autoreconf', +# for example). +# +# We also allow 'LIBTOOLIZE', 'M4', 'SHA1SUM' and some others to be +# overridden, and export the result for child processes, but they are +# handled by the function 'func_find_tool' and not defaulted in this +# section. + +: ${ACLOCAL="aclocal"} +: ${AUTOCONF="autoconf"} +: ${AUTOHEADER="autoheader"} +: ${AUTOM4TE="autom4te"} +: ${AUTOHEADER="autoheader"} +: ${AUTOMAKE="automake"} +: ${AUTOPOINT="autopoint"} +: ${AUTORECONF="autoreconf"} +: ${CMP="cmp"} +: ${CONFIG_SHELL="/bin/sh"} +: ${DIFF="diff"} +: ${GIT="git"} +: ${LN_S="ln -s"} +: ${RM="rm"} + +export ACLOCAL +export AUTOCONF +export AUTOHEADER +export AUTOM4TE +export AUTOHEADER +export AUTOMAKE +export AUTOPOINT +export AUTORECONF +export CONFIG_SHELL + + +: ${LUAROCKS="luarocks"} + +export LUAROCKS + + +## -------------- ## +## Configuration. ## +## -------------- ## + +# A newline delimited list of triples of programs (that respond to +# --version), the minimum version numbers required (or just '-' in the +# version field if any version will be sufficient) and homepage URLs +# to help locate missing packages. +buildreq= + +# Name of a file containing instructions on installing missing packages +# required in 'buildreq'. +buildreq_readme=README-hacking + +# These are extracted from AC_INIT in configure.ac, though you can +# override those values in 'bootstrap.conf' if you prefer. +build_aux= +macro_dir= +package= +package_name= +package_version= +package_bugreport= + +# These are extracted from 'gnulib-cache.m4', or else fall-back +# automatically on the gnulib defaults; unless you set the values +# manually in 'bootstrap.conf'. +doc_base= +gnulib_mk= +gnulib_name= +local_gl_dir= +source_base= +tests_base= + +# The list of gnulib modules required at 'gnulib-tool' time. If you +# check 'gnulib-cache.m4' into your repository, then this list will be +# extracted automatically. +gnulib_modules= + +# Extra gnulib files that are not in modules, which override files of +# the same name installed by other bootstrap tools. +gnulib_non_module_files=" + build-aux/compile + build-aux/install-sh + build-aux/mdate-sh + build-aux/texinfo.tex + build-aux/depcomp + build-aux/config.guess + build-aux/config.sub + doc/INSTALL +" + +# Relative path to the local gnulib submodule, and url to the upstream +# git repository. If you have a gnulib entry in your .gitmodules file, +# these values are ignored. +gnulib_path= +gnulib_url= + +# Additional gnulib-tool options to use. +gnulib_tool_options=" + --no-changelog +" + +# bootstrap removes any macro-files that are not included by aclocal.m4, +# except for files listed in this variable that are always kept. +gnulib_precious=" + gnulib-tool.m4 +" + +# When truncating long commands for display, always allow at least this +# many characters before truncating. +min_cmd_len=160 + +# The command to download all .po files for a specified domain into +# a specified directory. Fill in the first %s is the domain name, and +# the second with the destination directory. Use rsync's -L and -r +# options because the latest/%s directory and the .po files within are +# all symlinks. +po_download_command_format=\ +"rsync --delete --exclude '*.s1' -Lrtvz \ +'translationproject.org::tp/latest/%s/' '%s'" + +# Other locale categories that need message catalogs. +extra_locale_categories= + +# Additional xgettext options to use. Gnulib might provide you with an +# extensive list of additional options to append to this, but gettext +# 0.16.1 and newer appends them automaticaly, so you can safely ignore +# the complaints from 'gnulib-tool' if your $configure_ac states: +# +# AM_GNU_GETTEXT_VERSION([0.16.1]) +xgettext_options=" + --flag=_:1:pass-c-format + --flag=N_:1:pass-c-format +" + +# Package copyright holder for gettext files. Defaults to FSF if unset. +copyright_holder= + +# File that should exist in the top directory of a checked out hierarchy, +# but not in a distribution tarball. +checkout_only_file= + +# Whether to use copies instead of symlinks by default (if set to true, +# the --copy option has no effect). +copy=false + +# Set this to ".cvsignore .gitignore" in 'bootstrap.conf' if you want +# those files to be generated in directories like 'lib/', 'm4/', and 'po/', +# or set it to "auto" to make this script select what to use based +# on what version control system (if any) is used in the source directory. +# Or set it to "none" to ignore VCS ignore files entirely. Default is +# "auto". +vc_ignore= + + +# List of slingshot files to link into stdlib tree before autotooling. +slingshot_files=$slingshot_files + +# Relative path to the local slingshot submodule, and url to the upsream +# git repository. If you have a slingshot entry in your .gitmodules file, +# these values are ignored. +slingshot_path=$slingshot_path +slingshot_url=$slingshot_url + +# NOTE: slingshot bootstrap will check rockspecs listed in $buildreq, +# according to the URL part of a specification triple ending in +# `.rockspec`. + + +## ------------------- ## +## External Libraries. ## +## ------------------- ## + +# Source required external libraries: +# Set a version string for this script. +scriptversion=2014-01-03.01; # UTC + +# General shell script boiler plate, and helper functions. +# Written by Gary V. Vaughan, 2004 + +# Copyright (C) 2004-2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +# As a special exception to the GNU General Public License, if you distribute +# this file as part of a program or library that is built using GNU Libtool, +# you may include this file under the same distribution terms that you use +# for the rest of that program. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Please report bugs or propose patches to gary@gnu.org. + + +## ------ ## +## Usage. ## +## ------ ## + +# Evaluate this file near the top of your script to gain access to +# the functions and variables defined here: +# +# . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh +# +# If you need to override any of the default environment variable +# settings, do that before evaluating this file. + + +## -------------------- ## +## Shell normalisation. ## +## -------------------- ## + +# Some shells need a little help to be as Bourne compatible as possible. +# Before doing anything else, make sure all that help has been provided! + +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac +fi + +# NLS nuisances: We save the old values in case they are required later. +_G_user_locale= +_G_safe_locale= +for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test set = \"\${$_G_var+set}\"; then + save_$_G_var=\$$_G_var + $_G_var=C + export $_G_var + _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" + _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" + fi" +done + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Make sure IFS has a sensible default +sp=' ' +nl=' +' +IFS="$sp $nl" + +# There are apparently some retarded systems that use ';' as a PATH separator! +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + + +## ------------------------- ## +## Locate command utilities. ## +## ------------------------- ## + + +# func_executable_p FILE +# ---------------------- +# Check that FILE is an executable regular file. +func_executable_p () +{ + test -f "$1" && test -x "$1" +} + + +# func_path_progs PROGS_LIST CHECK_FUNC [PATH] +# -------------------------------------------- +# Search for either a program that responds to --version with output +# containing "GNU", or else returned by CHECK_FUNC otherwise, by +# trying all the directories in PATH with each of the elements of +# PROGS_LIST. +# +# CHECK_FUNC should accept the path to a candidate program, and +# set $func_check_prog_result if it truncates its output less than +# $_G_path_prog_max characters. +func_path_progs () +{ + _G_progs_list=$1 + _G_check_func=$2 + _G_PATH=${3-"$PATH"} + + _G_path_prog_max=0 + _G_path_prog_found=false + _G_save_IFS=$IFS; IFS=$PATH_SEPARATOR + for _G_dir in $_G_PATH; do + IFS=$_G_save_IFS + test -z "$_G_dir" && _G_dir=. + for _G_prog_name in $_G_progs_list; do + for _exeext in '' .EXE; do + _G_path_prog=$_G_dir/$_G_prog_name$_exeext + func_executable_p "$_G_path_prog" || continue + case `"$_G_path_prog" --version 2>&1` in + *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; + *) $_G_check_func $_G_path_prog + func_path_progs_result=$func_check_prog_result + ;; + esac + $_G_path_prog_found && break 3 + done + done + done + IFS=$_G_save_IFS + test -z "$func_path_progs_result" && { + echo "no acceptable sed could be found in \$PATH" >&2 + exit 1 + } +} + + +# We want to be able to use the functions in this file before configure +# has figured out where the best binaries are kept, which means we have +# to search for them ourselves - except when the results are already set +# where we skip the searches. + +# Unless the user overrides by setting SED, search the path for either GNU +# sed, or the sed that truncates its output the least. +test -z "$SED" && { + _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for _G_i in 1 2 3 4 5 6 7; do + _G_sed_script=$_G_sed_script$nl$_G_sed_script + done + echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed + _G_sed_script= + + func_check_prog_sed () + { + _G_path_prog=$1 + + _G_count=0 + printf 0123456789 >conftest.in + while : + do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo '' >> conftest.nl + "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break + diff conftest.out conftest.nl >/dev/null 2>&1 || break + _G_count=`expr $_G_count + 1` + if test "$_G_count" -gt "$_G_path_prog_max"; then + # Best one so far, save it but keep looking for a better one + func_check_prog_result=$_G_path_prog + _G_path_prog_max=$_G_count + fi + # 10*(2^10) chars as input seems more than enough + test 10 -lt "$_G_count" && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out + } + + func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin + rm -f conftest.sed + SED=$func_path_progs_result +} + + +# Unless the user overrides by setting GREP, search the path for either GNU +# grep, or the grep that truncates its output the least. +test -z "$GREP" && { + func_check_prog_grep () + { + _G_path_prog=$1 + + _G_count=0 + _G_path_prog_max=0 + printf 0123456789 >conftest.in + while : + do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo 'GREP' >> conftest.nl + "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break + diff conftest.out conftest.nl >/dev/null 2>&1 || break + _G_count=`expr $_G_count + 1` + if test "$_G_count" -gt "$_G_path_prog_max"; then + # Best one so far, save it but keep looking for a better one + func_check_prog_result=$_G_path_prog + _G_path_prog_max=$_G_count + fi + # 10*(2^10) chars as input seems more than enough + test 10 -lt "$_G_count" && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out + } + + func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin + GREP=$func_path_progs_result +} + + +## ------------------------------- ## +## User overridable command paths. ## +## ------------------------------- ## + +# All uppercase variable names are used for environment variables. These +# variables can be overridden by the user before calling a script that +# uses them if a suitable command of that name is not already available +# in the command search PATH. + +: ${CP="cp -f"} +: ${ECHO="printf %s\n"} +: ${EGREP="$GREP -E"} +: ${FGREP="$GREP -F"} +: ${LN_S="ln -s"} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} + + +## -------------------- ## +## Useful sed snippets. ## +## -------------------- ## + +sed_dirname='s|/[^/]*$||' +sed_basename='s|^.*/||' + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Same as above, but do not quote variable references. +sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution that turns a string into a regex matching for the +# string literally. +sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' + +# Sed substitution that converts a w32 file name or path +# that contains forward slashes, into one that contains +# (escaped) backslashes. A very naive implementation. +sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + +# Re-'\' parameter expansions in output of sed_double_quote_subst that +# were '\'-ed in input to the same. If an odd number of '\' preceded a +# '$' in input to sed_double_quote_subst, that '$' was protected from +# expansion. Since each input '\' is now two '\'s, look for any number +# of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. +_G_bs='\\' +_G_bs2='\\\\' +_G_bs4='\\\\\\\\' +_G_dollar='\$' +sed_double_backslash="\ + s/$_G_bs4/&\\ +/g + s/^$_G_bs2$_G_dollar/$_G_bs&/ + s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g + s/\n//g" + + +## ----------------- ## +## Global variables. ## +## ----------------- ## + +# Except for the global variables explicitly listed below, the following +# functions in the '^func_' namespace, and the '^require_' namespace +# variables initialised in the 'Resource management' section, sourcing +# this file will not pollute your global namespace with anything +# else. There's no portable way to scope variables in Bourne shell +# though, so actually running these functions will sometimes place +# results into a variable named after the function, and often use +# temporary variables in the '^_G_' namespace. If you are careful to +# avoid using those namespaces casually in your sourcing script, things +# should continue to work as you expect. And, of course, you can freely +# overwrite any of the functions or variables defined here before +# calling anything to customize them. + +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +# Allow overriding, eg assuming that you follow the convention of +# putting '$debug_cmd' at the start of all your functions, you can get +# bash to show function call trace with: +# +# debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name +debug_cmd=${debug_cmd-":"} +exit_cmd=: + +# By convention, finish your script with: +# +# exit $exit_status +# +# so that you can set exit_status to non-zero if you want to indicate +# something went wrong during execution without actually bailing out at +# the point of failure. +exit_status=$EXIT_SUCCESS + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath=$0 + +# The name of this program. +progname=`$ECHO "$progpath" |$SED "$sed_basename"` + +# Make sure we have an absolute progpath for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` + progdir=`cd "$progdir" && pwd` + progpath=$progdir/$progname + ;; + *) + _G_IFS=$IFS + IFS=${PATH_SEPARATOR-:} + for progdir in $PATH; do + IFS=$_G_IFS + test -x "$progdir/$progname" && break + done + IFS=$_G_IFS + test -n "$progdir" || progdir=`pwd` + progpath=$progdir/$progname + ;; +esac + + +## ----------------- ## +## Standard options. ## +## ----------------- ## + +# The following options affect the operation of the functions defined +# below, and should be set appropriately depending on run-time para- +# meters passed on the command line. + +opt_dry_run=false +opt_quiet=false +opt_verbose=false + +# Categories 'all' and 'none' are always available. Append any others +# you will pass as the first argument to func_warning from your own +# code. +warning_categories= + +# By default, display warnings according to 'opt_warning_types'. Set +# 'warning_func' to ':' to elide all warnings, or func_fatal_error to +# treat the next displayed warning as a fatal error. +warning_func=func_warn_and_continue + +# Set to 'all' to display all warnings, 'none' to suppress all +# warnings, or a space delimited list of some subset of +# 'warning_categories' to display only the listed warnings. +opt_warning_types=all + + +## -------------------- ## +## Resource management. ## +## -------------------- ## + +# This section contains definitions for functions that each ensure a +# particular resource (a file, or a non-empty configuration variable for +# example) is available, and if appropriate to extract default values +# from pertinent package files. Call them using their associated +# 'require_*' variable to ensure that they are executed, at most, once. +# +# It's entirely deliberate that calling these functions can set +# variables that don't obey the namespace limitations obeyed by the rest +# of this file, in order that that they be as useful as possible to +# callers. + + +# require_term_colors +# ------------------- +# Allow display of bold text on terminals that support it. +require_term_colors=func_require_term_colors +func_require_term_colors () +{ + $debug_cmd + + test -t 1 && { + # COLORTERM and USE_ANSI_COLORS environment variables take + # precedence, because most terminfo databases neglect to describe + # whether color sequences are supported. + test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} + + if test 1 = "$USE_ANSI_COLORS"; then + # Standard ANSI escape sequences + tc_reset='' + tc_bold=''; tc_standout='' + tc_red=''; tc_green='' + tc_blue=''; tc_cyan='' + else + # Otherwise trust the terminfo database after all. + test -n "`tput sgr0 2>/dev/null`" && { + tc_reset=`tput sgr0` + test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` + tc_standout=$tc_bold + test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` + test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` + test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` + test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` + test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` + } + fi + } + + require_term_colors=: +} + + +# require_rockspecs_req +# --------------------- +# Remove rockspecs from $buildreq, and add them to $rockspecs_req. +require_rockspecs_req=slingshot_require_rockspecs_req +slingshot_require_rockspecs_req () +{ + $debug_cmd + + test -n "$rockspecs_req" || { + _G_non_rockspecs= + + set dummy $buildreq; shift + + while test $# -gt 2; do + case $3 in + *.rockspec) + func_append rockspecs_req " $1 $2 $3" + ;; + [a-z]*://*) + func_append _G_non_rockspecs " $1 $2 $3" + ;; + *) func_fatal_error "\ +'$3' from the buildreq table in +'bootstrap.conf' does not look like the URL for downloading +$1. Please ensure that buildreq is a strict newline +delimited list of triples; 'program min-version url'." + ;; + esac + shift; shift; shift + done + + buildreq=$_G_non_rockspecs + } + + require_rockspecs_req=: +} + + +# require_slingshot_dotgitmodules +# ------------------------------- +# Ensure we have a '.gitmodules' file, with appropriate 'slingshot' settings. +require_slingshot_dotgitmodules=slingshot_require_slingshot_dotgitmodules +slingshot_require_slingshot_dotgitmodules () +{ + $debug_cmd + + $require_git + + test true = "$GIT" || { + # A slingshot entry in .gitmodules always takes precedence. + _G_path=`$GIT config --file .gitmodules submodule.slingshot.path 2>/dev/null` + + test -n "$_G_path" || { + $require_vc_ignore_files + + func_verbose "adding slingshot entries to '.gitmodules'" + + test -n "$slingshot_path" || slingshot_path=slingshot + test -n "$slingshot_url" || slingshot_url=git://github.com/gvvaughan/slingshot.git + + { + echo '[submodule "slingshot"]' + echo " path=$slingshot_path" + echo " url=$slingshot_url" + } >> .gitmodules + + test -n "$vc_ignore_files" \ + || func_insert_if_absent ".gitmodules" $vc_ignore_files + } + } + + require_slingshot_dotgitmodules=: +} + + +# require_slingshot_path +# require_slingshot_url +# ---------------------- +# Ensure 'slingshot_path' and 'slingshot_url' are set. +require_slingshot_path=slingshot_require_slingshot_dotgitmodules_parameters +require_slingshot_url=slingshot_require_slingshot_dotgitmodules_parameters +slingshot_require_slingshot_dotgitmodules_parameters () +{ + $debug_cmd + + $require_git + $require_slingshot_dotgitmodules + + test -f .gitmodules \ + || func_fatal_error "Unable to update '.gitmodules' with slingshot submodule" + + test true = "$GIT" || { + slingshot_path=`$GIT config --file=.gitmodules --get submodule.slingshot.path` + slingshot_url=`$GIT config --file=.gitmodules --get submodule.slingshot.url` + + func_verbose "slingshot_path='$slingshot_path'" + func_verbose "slingshot_url='$slingshot_url'" + } + + require_slingshot_path=: + require_slingshot_url=: +} + + +# require_slingshot_submodule +# --------------------------- +# Ensure that there is a current slingshot submodule. +require_slingshot_submodule=slingshot_require_slingshot_submodule +slingshot_require_slingshot_submodule () +{ + $debug_cmd + + $require_git + + if test true = "$GIT"; then + func_warning recommend \ + "No 'git' found; imported slingshot modules may be missing." + else + $require_slingshot_dotgitmodules + + if test -f .gitmodules; then + $require_slingshot_path + $require_slingshot_url + + if test -f "slingshot/src/mkrockspecs.in"; then + : All present and correct. + + else + trap slingshot_cleanup 1 2 13 15 + + shallow= + $GIT clone -h 2>&1 |func_grep_q -- --depth \ + && shallow='--depth 365' + + func_show_eval "$GIT clone $shallow '$slingshot_url' '$slingshot_path'" \ + slingshot_cleanup + + # FIXME: Solaris /bin/sh will try to execute '-' if any of + # these signals are caught after this. + trap - 1 2 13 15 + fi + + # Make sure we've checked out the correct revision of slingshot. + func_show_eval "$GIT submodule init -- $slingshot_path" \ + && func_show_eval "$GIT submodule update -- $slingshot_path" \ + || func_fatal_error "Unable to update slingshot submodule." + fi + fi + + require_slingshot_submodule=: +} + + +# require_bootstrap_uptodate +# -------------------------- +# Complain if the version of bootstrap in the build-aux directory differs +# from the one we are running. +require_bootstrap_uptodate=slingshot_require_bootstrap_uptodate +slingshot_require_bootstrap_uptodate () +{ + $debug_cmd + + $require_slingshot_submodule + + _G_slingshot_bootstrap=slingshot/bootstrap + + rm -f $progname.new + + if test -f "$_G_slingshot_bootstrap"; then + if func_cmp_s "$progpath" "$_G_slingshot_bootstrap"; then + func_verbose "bootstrap script up to date" + else + cp -f $_G_slingshot_bootstrap $progname.new + func_warning upgrade "\ +An updated slingshot bootstrap script is ready for you in +'$progname.new'. After you've verified that you want the +changes, you can update with: + mv -f $progname.new $progname + ./$progname + +Or you can disable this check permanently by adding the +following to 'bootstrap.conf': + require_bootstrap_uptodate=:" + fi + else + func_warning upgrade "\ +Your slingshot submodule appears to be damagedi, so I can't tell +whether your bootstrap has gone out of sync. Please check for +and undo any local changes, or revert to the slingshot revision +you were using previously, and rerun this script." + fi + + require_bootstrap_uptodate=: +} + + +# slingshot_cleanup +# ----------------- +# Recursively delete everything at $slingshot_path. +slingshot_cleanup () +{ + $debug_cmd + + $require_slingshot_path + + _G_status=$? + $RM -fr $slingshot_path + exit $_G_status +} + + +## ----------------- ## +## Function library. ## +## ----------------- ## + +# This section contains a variety of useful functions to call in your +# scripts. Take note of the portable wrappers for features provided by +# some modern shells, which will fall back to slower equivalents on +# less featureful shells. + + +# func_append VAR VALUE +# --------------------- +# Append VALUE onto the existing contents of VAR. + + # We should try to minimise forks, especially on Windows where they are + # unreasonably slow, so skip the feature probes when bash or zsh are + # being used: + if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then + : ${_G_HAVE_ARITH_OP="yes"} + : ${_G_HAVE_XSI_OPS="yes"} + # The += operator was introduced in bash 3.1 + case $BASH_VERSION in + [12].* | 3.0 | 3.0*) ;; + *) + : ${_G_HAVE_PLUSEQ_OP="yes"} + ;; + esac + fi + + # _G_HAVE_PLUSEQ_OP + # Can be empty, in which case the shell is probed, "yes" if += is + # useable or anything else if it does not work. + test -z "$_G_HAVE_PLUSEQ_OP" \ + && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ + && _G_HAVE_PLUSEQ_OP=yes + +if test yes = "$_G_HAVE_PLUSEQ_OP" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_append () + { + $debug_cmd + + eval "$1+=\$2" + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_append () + { + $debug_cmd + + eval "$1=\$$1\$2" + } +fi + + +# func_append_quoted VAR VALUE +# ---------------------------- +# Quote VALUE and append to the end of shell variable VAR, separated +# by a space. +if test yes = "$_G_HAVE_PLUSEQ_OP"; then + eval 'func_append_quoted () + { + $debug_cmd + + func_quote_for_eval "$2" + eval "$1+=\\ \$func_quote_for_eval_result" + }' +else + func_append_quoted () + { + $debug_cmd + + func_quote_for_eval "$2" + eval "$1=\$$1\\ \$func_quote_for_eval_result" + } +fi + + +# func_append_uniq VAR VALUE +# -------------------------- +# Append unique VALUE onto the existing contents of VAR, assuming +# entries are delimited by the first character of VALUE. For example: +# +# func_append_uniq options " --another-option option-argument" +# +# will only append to $options if " --another-option option-argument " +# is not already present somewhere in $options already (note spaces at +# each end implied by leading space in second argument). +func_append_uniq () +{ + $debug_cmd + + eval _G_current_value='`$ECHO $'$1'`' + _G_delim=`expr "$2" : '\(.\)'` + + case $_G_delim$_G_current_value$_G_delim in + *"$2$_G_delim"*) ;; + *) func_append "$@" ;; + esac +} + + +# func_arith TERM... +# ------------------ +# Set func_arith_result to the result of evaluating TERMs. + test -z "$_G_HAVE_ARITH_OP" \ + && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ + && _G_HAVE_ARITH_OP=yes + +if test yes = "$_G_HAVE_ARITH_OP"; then + eval 'func_arith () + { + $debug_cmd + + func_arith_result=$(( $* )) + }' +else + func_arith () + { + $debug_cmd + + func_arith_result=`expr "$@"` + } +fi + + +# func_basename FILE +# ------------------ +# Set func_basename_result to FILE with everything up to and including +# the last / stripped. +if test yes = "$_G_HAVE_XSI_OPS"; then + # If this shell supports suffix pattern removal, then use it to avoid + # forking. Hide the definitions single quotes in case the shell chokes + # on unsupported syntax... + _b='func_basename_result=${1##*/}' + _d='case $1 in + */*) func_dirname_result=${1%/*}$2 ;; + * ) func_dirname_result=$3 ;; + esac' + +else + # ...otherwise fall back to using sed. + _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' + _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` + if test "X$func_dirname_result" = "X$1"; then + func_dirname_result=$3 + else + func_append func_dirname_result "$2" + fi' +fi + +eval 'func_basename () +{ + $debug_cmd + + '"$_b"' +}' + + +# func_dirname FILE APPEND NONDIR_REPLACEMENT +# ------------------------------------------- +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +eval 'func_dirname () +{ + $debug_cmd + + '"$_d"' +}' + + +# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT +# -------------------------------------------------------- +# Perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# For efficiency, we do not delegate to the functions above but instead +# duplicate the functionality here. +eval 'func_dirname_and_basename () +{ + $debug_cmd + + '"$_b"' + '"$_d"' +}' + + +# func_echo ARG... +# ---------------- +# Echo program name prefixed message. +func_echo () +{ + $debug_cmd + + _G_message=$* + + func_echo_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_IFS + $ECHO "$progname: $_G_line" + done + IFS=$func_echo_IFS +} + + +# func_echo_all ARG... +# -------------------- +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + + +# func_echo_infix_1 INFIX ARG... +# ------------------------------ +# Echo program name, followed by INFIX on the first line, with any +# additional lines not showing INFIX. +func_echo_infix_1 () +{ + $debug_cmd + + $require_term_colors + + _G_infix=$1; shift + _G_indent=$_G_infix + _G_prefix="$progname: $_G_infix: " + _G_message=$* + + # Strip color escape sequences before counting printable length + for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" + do + test -n "$_G_tc" && { + _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` + _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` + } + done + _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes + + func_echo_infix_1_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_infix_1_IFS + $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 + _G_prefix=$_G_indent + done + IFS=$func_echo_infix_1_IFS +} + + +# func_error ARG... +# ----------------- +# Echo program name prefixed message to standard error. +func_error () +{ + $debug_cmd + + $require_term_colors + + func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 +} + + +# func_fatal_error ARG... +# ----------------------- +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + $debug_cmd + + func_error "$*" + exit $EXIT_FAILURE +} + + +# func_grep EXPRESSION FILENAME +# ----------------------------- +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $debug_cmd + + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_len STRING +# --------------- +# Set func_len_result to the length of STRING. STRING may not +# start with a hyphen. + test -z "$_G_HAVE_XSI_OPS" \ + && (eval 'x=a/b/c; + test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ + && _G_HAVE_XSI_OPS=yes + +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_len () + { + $debug_cmd + + func_len_result=${#1} + }' +else + func_len () + { + $debug_cmd + + func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` + } +fi + + +# func_mkdir_p DIRECTORY-PATH +# --------------------------- +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + $debug_cmd + + _G_directory_path=$1 + _G_dir_list= + + if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then + + # Protect directory names starting with '-' + case $_G_directory_path in + -*) _G_directory_path=./$_G_directory_path ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$_G_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + _G_dir_list=$_G_directory_path:$_G_dir_list + + # If the last portion added has no slash in it, the list is done + case $_G_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` + done + _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` + + func_mkdir_p_IFS=$IFS; IFS=: + for _G_dir in $_G_dir_list; do + IFS=$func_mkdir_p_IFS + # mkdir can fail with a 'File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$_G_dir" 2>/dev/null || : + done + IFS=$func_mkdir_p_IFS + + # Bail out if we (or some other process) failed to create a directory. + test -d "$_G_directory_path" || \ + func_fatal_error "Failed to create '$1'" + fi +} + + +# func_mktempdir [BASENAME] +# ------------------------- +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, BASENAME is the basename for that directory. +func_mktempdir () +{ + $debug_cmd + + _G_template=${TMPDIR-/tmp}/${1-$progname} + + if test : = "$opt_dry_run"; then + # Return a directory name, but don't create it in dry-run mode + _G_tmpdir=$_G_template-$$ + else + + # If mktemp works, use that first and foremost + _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` + + if test ! -d "$_G_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + _G_tmpdir=$_G_template-${RANDOM-0}$$ + + func_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$_G_tmpdir" + umask $func_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$_G_tmpdir" || \ + func_fatal_error "cannot create temporary directory '$_G_tmpdir'" + fi + + $ECHO "$_G_tmpdir" +} + + +# func_normal_abspath PATH +# ------------------------ +# Remove doubled-up and trailing slashes, "." path components, +# and cancel out any ".." path components in PATH after making +# it an absolute path. +func_normal_abspath () +{ + $debug_cmd + + # These SED scripts presuppose an absolute path with a trailing slash. + _G_pathcar='s|^/\([^/]*\).*$|\1|' + _G_pathcdr='s|^/[^/]*||' + _G_removedotparts=':dotsl + s|/\./|/|g + t dotsl + s|/\.$|/|' + _G_collapseslashes='s|/\{1,\}|/|g' + _G_finalslash='s|/*$|/|' + + # Start from root dir and reassemble the path. + func_normal_abspath_result= + func_normal_abspath_tpath=$1 + func_normal_abspath_altnamespace= + case $func_normal_abspath_tpath in + "") + # Empty path, that just means $cwd. + func_stripname '' '/' "`pwd`" + func_normal_abspath_result=$func_stripname_result + return + ;; + # The next three entries are used to spot a run of precisely + # two leading slashes without using negated character classes; + # we take advantage of case's first-match behaviour. + ///*) + # Unusual form of absolute path, do nothing. + ;; + //*) + # Not necessarily an ordinary path; POSIX reserves leading '//' + # and for example Cygwin uses it to access remote file shares + # over CIFS/SMB, so we conserve a leading double slash if found. + func_normal_abspath_altnamespace=/ + ;; + /*) + # Absolute path, do nothing. + ;; + *) + # Relative path, prepend $cwd. + func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath + ;; + esac + + # Cancel out all the simple stuff to save iterations. We also want + # the path to end with a slash for ease of parsing, so make sure + # there is one (and only one) here. + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` + while :; do + # Processed it all yet? + if test / = "$func_normal_abspath_tpath"; then + # If we ascended to the root using ".." the result may be empty now. + if test -z "$func_normal_abspath_result"; then + func_normal_abspath_result=/ + fi + break + fi + func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_pathcar"` + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_pathcdr"` + # Figure out what to do with it + case $func_normal_abspath_tcomponent in + "") + # Trailing empty path component, ignore it. + ;; + ..) + # Parent dir; strip last assembled component from result. + func_dirname "$func_normal_abspath_result" + func_normal_abspath_result=$func_dirname_result + ;; + *) + # Actual path component, append it. + func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" + ;; + esac + done + # Restore leading double-slash if one was found on entry. + func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result +} + + +# func_notquiet ARG... +# -------------------- +# Echo program name prefixed message only when not in quiet mode. +func_notquiet () +{ + $debug_cmd + + $opt_quiet || func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + + +# func_relative_path SRCDIR DSTDIR +# -------------------------------- +# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. +func_relative_path () +{ + $debug_cmd + + func_relative_path_result= + func_normal_abspath "$1" + func_relative_path_tlibdir=$func_normal_abspath_result + func_normal_abspath "$2" + func_relative_path_tbindir=$func_normal_abspath_result + + # Ascend the tree starting from libdir + while :; do + # check if we have found a prefix of bindir + case $func_relative_path_tbindir in + $func_relative_path_tlibdir) + # found an exact match + func_relative_path_tcancelled= + break + ;; + $func_relative_path_tlibdir*) + # found a matching prefix + func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" + func_relative_path_tcancelled=$func_stripname_result + if test -z "$func_relative_path_result"; then + func_relative_path_result=. + fi + break + ;; + *) + func_dirname $func_relative_path_tlibdir + func_relative_path_tlibdir=$func_dirname_result + if test -z "$func_relative_path_tlibdir"; then + # Have to descend all the way to the root! + func_relative_path_result=../$func_relative_path_result + func_relative_path_tcancelled=$func_relative_path_tbindir + break + fi + func_relative_path_result=../$func_relative_path_result + ;; + esac + done + + # Now calculate path; take care to avoid doubling-up slashes. + func_stripname '' '/' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + func_stripname '/' '/' "$func_relative_path_tcancelled" + if test -n "$func_stripname_result"; then + func_append func_relative_path_result "/$func_stripname_result" + fi + + # Normalisation. If bindir is libdir, return '.' else relative path. + if test -n "$func_relative_path_result"; then + func_stripname './' '' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + fi + + test -n "$func_relative_path_result" || func_relative_path_result=. + + : +} + + +# func_quote_for_eval ARG... +# -------------------------- +# Aesthetically quote ARGs to be evaled later. +# This function returns two values: +# i) func_quote_for_eval_result +# double-quoted, suitable for a subsequent eval +# ii) func_quote_for_eval_unquoted_result +# has all characters that are still active within double +# quotes backslashified. +func_quote_for_eval () +{ + $debug_cmd + + func_quote_for_eval_unquoted_result= + func_quote_for_eval_result= + while test 0 -lt $#; do + case $1 in + *[\\\`\"\$]*) + _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; + *) + _G_unquoted_arg=$1 ;; + esac + if test -n "$func_quote_for_eval_unquoted_result"; then + func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" + else + func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" + fi + + case $_G_unquoted_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and variable expansion + # for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + _G_quoted_arg=\"$_G_unquoted_arg\" + ;; + *) + _G_quoted_arg=$_G_unquoted_arg + ;; + esac + + if test -n "$func_quote_for_eval_result"; then + func_append func_quote_for_eval_result " $_G_quoted_arg" + else + func_append func_quote_for_eval_result "$_G_quoted_arg" + fi + shift + done +} + + +# func_quote_for_expand ARG +# ------------------------- +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + $debug_cmd + + case $1 in + *[\\\`\"]*) + _G_arg=`$ECHO "$1" | $SED \ + -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; + *) + _G_arg=$1 ;; + esac + + case $_G_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + _G_arg=\"$_G_arg\" + ;; + esac + + func_quote_for_expand_result=$_G_arg +} + + +# func_stripname PREFIX SUFFIX NAME +# --------------------------------- +# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_stripname () + { + $debug_cmd + + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary variable first. + func_stripname_result=$3 + func_stripname_result=${func_stripname_result#"$1"} + func_stripname_result=${func_stripname_result%"$2"} + }' +else + func_stripname () + { + $debug_cmd + + case $2 in + .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; + *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; + esac + } +fi + + +# func_show_eval CMD [FAIL_EXP] +# ----------------------------- +# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + $debug_cmd + + _G_cmd=$1 + _G_fail_exp=${2-':'} + + func_quote_for_expand "$_G_cmd" + eval "func_notquiet $func_quote_for_expand_result" + + $opt_dry_run || { + eval "$_G_cmd" + _G_status=$? + if test 0 -ne "$_G_status"; then + eval "(exit $_G_status); $_G_fail_exp" + fi + } +} + + +# func_show_eval_locale CMD [FAIL_EXP] +# ------------------------------------ +# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + $debug_cmd + + _G_cmd=$1 + _G_fail_exp=${2-':'} + + $opt_quiet || { + func_quote_for_expand "$_G_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + $opt_dry_run || { + eval "$_G_user_locale + $_G_cmd" + _G_status=$? + eval "$_G_safe_locale" + if test 0 -ne "$_G_status"; then + eval "(exit $_G_status); $_G_fail_exp" + fi + } +} + + +# func_tr_sh +# ---------- +# Turn $1 into a string suitable for a shell variable name. +# Result is stored in $func_tr_sh_result. All characters +# not in the set a-zA-Z0-9_ are replaced with '_'. Further, +# if $1 begins with a digit, a '_' is prepended as well. +func_tr_sh () +{ + $debug_cmd + + case $1 in + [0-9]* | *[!a-zA-Z0-9_]*) + func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` + ;; + * ) + func_tr_sh_result=$1 + ;; + esac +} + + +# func_verbose ARG... +# ------------------- +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $debug_cmd + + $opt_verbose && func_echo "$*" + + : +} + + +# func_warn_and_continue ARG... +# ----------------------------- +# Echo program name prefixed warning message to standard error. +func_warn_and_continue () +{ + $debug_cmd + + $require_term_colors + + func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 +} + + +# func_warning CATEGORY ARG... +# ---------------------------- +# Echo program name prefixed warning message to standard error. Warning +# messages can be filtered according to CATEGORY, where this function +# elides messages where CATEGORY is not listed in the global variable +# 'opt_warning_types'. +func_warning () +{ + $debug_cmd + + # CATEGORY must be in the warning_categories list! + case " $warning_categories " in + *" $1 "*) ;; + *) func_internal_error "invalid warning category '$1'" ;; + esac + + _G_category=$1 + shift + + case " $opt_warning_types " in + *" $_G_category "*) $warning_func ${1+"$@"} ;; + esac +} + + +# func_sort_ver VER1 VER2 +# ----------------------- +# 'sort -V' is not generally available. +# Note this deviates from the version comparison in automake +# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a +# but this should suffice as we won't be specifying old +# version formats or redundant trailing .0 in bootstrap.conf. +# If we did want full compatibility then we should probably +# use m4_version_compare from autoconf. +func_sort_ver () +{ + $debug_cmd + + printf '%s\n%s\n' "$1" "$2" \ + | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n +} + +# func_lt_ver PREV CURR +# --------------------- +# Return true if PREV and CURR are in the correct order according to +# func_sort_ver, otherwise false. Use it like this: +# +# func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." +func_lt_ver () +{ + $debug_cmd + + test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` +} + + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: +#! /bin/sh + +# Set a version string for this script. +scriptversion=2014-01-07.03; # UTC + +# A portable, pluggable option parser for Bourne shell. +# Written by Gary V. Vaughan, 2010 + +# Copyright (C) 2010-2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Please report bugs or propose patches to gary@gnu.org. + + +## ------ ## +## Usage. ## +## ------ ## + +# This file is a library for parsing options in your shell scripts along +# with assorted other useful supporting features that you can make use +# of too. +# +# For the simplest scripts you might need only: +# +# #!/bin/sh +# . relative/path/to/funclib.sh +# . relative/path/to/options-parser +# scriptversion=1.0 +# func_options ${1+"$@"} +# eval set dummy "$func_options_result"; shift +# ...rest of your script... +# +# In order for the '--version' option to work, you will need to have a +# suitably formatted comment like the one at the top of this file +# starting with '# Written by ' and ending with '# warranty; '. +# +# For '-h' and '--help' to work, you will also need a one line +# description of your script's purpose in a comment directly above the +# '# Written by ' line, like the one at the top of this file. +# +# The default options also support '--debug', which will turn on shell +# execution tracing (see the comment above debug_cmd below for another +# use), and '--verbose' and the func_verbose function to allow your script +# to display verbose messages only when your user has specified +# '--verbose'. +# +# After sourcing this file, you can plug processing for additional +# options by amending the variables from the 'Configuration' section +# below, and following the instructions in the 'Option parsing' +# section further down. + +## -------------- ## +## Configuration. ## +## -------------- ## + +# You should override these variables in your script after sourcing this +# file so that they reflect the customisations you have added to the +# option parser. + +# The usage line for option parsing errors and the start of '-h' and +# '--help' output messages. You can embed shell variables for delayed +# expansion at the time the message is displayed, but you will need to +# quote other shell meta-characters carefully to prevent them being +# expanded when the contents are evaled. +usage='$progpath [OPTION]...' + +# Short help message in response to '-h' and '--help'. Add to this or +# override it after sourcing this library to reflect the full set of +# options your script accepts. +usage_message="\ + --debug enable verbose shell tracing + -W, --warnings=CATEGORY + report the warnings falling in CATEGORY [all] + -v, --verbose verbosely report processing + --version print version information and exit + -h, --help print short or long help message and exit +" + +# Additional text appended to 'usage_message' in response to '--help'. +long_help_message=" +Warning categories include: + 'all' show all warnings + 'none' turn off all the warnings + 'error' warnings are treated as fatal errors" + +# Help message printed before fatal option parsing errors. +fatal_help="Try '\$progname --help' for more information." + + + +## ------------------------- ## +## Hook function management. ## +## ------------------------- ## + +# This section contains functions for adding, removing, and running hooks +# to the main code. A hook is just a named list of of function, that can +# be run in order later on. + +# func_hookable FUNC_NAME +# ----------------------- +# Declare that FUNC_NAME will run hooks added with +# 'func_add_hook FUNC_NAME ...'. +func_hookable () +{ + $debug_cmd + + func_append hookable_fns " $1" +} + + +# func_add_hook FUNC_NAME HOOK_FUNC +# --------------------------------- +# Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must +# first have been declared "hookable" by a call to 'func_hookable'. +func_add_hook () +{ + $debug_cmd + + case " $hookable_fns " in + *" $1 "*) ;; + *) func_fatal_error "'$1' does not accept hook functions." ;; + esac + + eval func_append ${1}_hooks '" $2"' +} + + +# func_remove_hook FUNC_NAME HOOK_FUNC +# ------------------------------------ +# Remove HOOK_FUNC from the list of functions called by FUNC_NAME. +func_remove_hook () +{ + $debug_cmd + + eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' +} + + +# func_run_hooks FUNC_NAME [ARG]... +# --------------------------------- +# Run all hook functions registered to FUNC_NAME. +# It is assumed that the list of hook functions contains nothing more +# than a whitespace-delimited list of legal shell function names, and +# no effort is wasted trying to catch shell meta-characters or preserve +# whitespace. +func_run_hooks () +{ + $debug_cmd + + case " $hookable_fns " in + *" $1 "*) ;; + *) func_fatal_error "'$1' does not support hook funcions.n" ;; + esac + + eval _G_hook_fns=\$$1_hooks; shift + + for _G_hook in $_G_hook_fns; do + eval $_G_hook '"$@"' + + # store returned options list back into positional + # parameters for next 'cmd' execution. + eval _G_hook_result=\$${_G_hook}_result + eval set dummy "$_G_hook_result"; shift + done + + func_quote_for_eval ${1+"$@"} + func_run_hooks_result=$func_quote_for_eval_result +} + + + +## --------------- ## +## Option parsing. ## +## --------------- ## + +# In order to add your own option parsing hooks, you must accept the +# full positional parameter list in your hook function, remove any +# options that you action, and then pass back the remaining unprocessed +# options in '_result', escaped suitably for +# 'eval'. Like this: +# +# my_options_prep () +# { +# $debug_cmd +# +# # Extend the existing usage message. +# usage_message=$usage_message' +# -s, --silent don'\''t print informational messages +# ' +# +# func_quote_for_eval ${1+"$@"} +# my_options_prep_result=$func_quote_for_eval_result +# } +# func_add_hook func_options_prep my_options_prep +# +# +# my_silent_option () +# { +# $debug_cmd +# +# # Note that for efficiency, we parse as many options as we can +# # recognise in a loop before passing the remainder back to the +# # caller on the first unrecognised argument we encounter. +# while test $# -gt 0; do +# opt=$1; shift +# case $opt in +# --silent|-s) opt_silent=: ;; +# # Separate non-argument short options: +# -s*) func_split_short_opt "$_G_opt" +# set dummy "$func_split_short_opt_name" \ +# "-$func_split_short_opt_arg" ${1+"$@"} +# shift +# ;; +# *) set dummy "$_G_opt" "$*"; shift; break ;; +# esac +# done +# +# func_quote_for_eval ${1+"$@"} +# my_silent_option_result=$func_quote_for_eval_result +# } +# func_add_hook func_parse_options my_silent_option +# +# +# my_option_validation () +# { +# $debug_cmd +# +# $opt_silent && $opt_verbose && func_fatal_help "\ +# '--silent' and '--verbose' options are mutually exclusive." +# +# func_quote_for_eval ${1+"$@"} +# my_option_validation_result=$func_quote_for_eval_result +# } +# func_add_hook func_validate_options my_option_validation +# +# You'll alse need to manually amend $usage_message to reflect the extra +# options you parse. It's preferable to append if you can, so that +# multiple option parsing hooks can be added safely. + + +# func_options [ARG]... +# --------------------- +# All the functions called inside func_options are hookable. See the +# individual implementations for details. +func_hookable func_options +func_options () +{ + $debug_cmd + + func_options_prep ${1+"$@"} + eval func_parse_options \ + ${func_options_prep_result+"$func_options_prep_result"} + eval func_validate_options \ + ${func_parse_options_result+"$func_parse_options_result"} + + eval func_run_hooks func_options \ + ${func_validate_options_result+"$func_validate_options_result"} + + # save modified positional parameters for caller + func_options_result=$func_run_hooks_result +} + + +# func_options_prep [ARG]... +# -------------------------- +# All initialisations required before starting the option parse loop. +# Note that when calling hook functions, we pass through the list of +# positional parameters. If a hook function modifies that list, and +# needs to propogate that back to rest of this script, then the complete +# modified list must be put in 'func_run_hooks_result' before +# returning. +func_hookable func_options_prep +func_options_prep () +{ + $debug_cmd + + # Option defaults: + opt_verbose=false + opt_warning_types= + + func_run_hooks func_options_prep ${1+"$@"} + + # save modified positional parameters for caller + func_options_prep_result=$func_run_hooks_result +} + + +# func_parse_options [ARG]... +# --------------------------- +# The main option parsing loop. +func_hookable func_parse_options +func_parse_options () +{ + $debug_cmd + + func_parse_options_result= + + # this just eases exit handling + while test $# -gt 0; do + # Defer to hook functions for initial option parsing, so they + # get priority in the event of reusing an option name. + func_run_hooks func_parse_options ${1+"$@"} + + # Adjust func_parse_options positional parameters to match + eval set dummy "$func_run_hooks_result"; shift + + # Break out of the loop if we already parsed every option. + test $# -gt 0 || break + + _G_opt=$1 + shift + case $_G_opt in + --debug|-x) debug_cmd='set -x' + func_echo "enabling shell trace mode" + $debug_cmd + ;; + + --no-warnings|--no-warning|--no-warn) + set dummy --warnings none ${1+"$@"} + shift + ;; + + --warnings|--warning|-W) + test $# = 0 && func_missing_arg $_G_opt && break + case " $warning_categories $1" in + *" $1 "*) + # trailing space prevents matching last $1 above + func_append_uniq opt_warning_types " $1" + ;; + *all) + opt_warning_types=$warning_categories + ;; + *none) + opt_warning_types=none + warning_func=: + ;; + *error) + opt_warning_types=$warning_categories + warning_func=func_fatal_error + ;; + *) + func_fatal_error \ + "unsupported warning category: '$1'" + ;; + esac + shift + ;; + + --verbose|-v) opt_verbose=: ;; + --version) func_version ;; + -\?|-h) func_usage ;; + --help) func_help ;; + + # Separate optargs to long options (plugins may need this): + --*=*) func_split_equals "$_G_opt" + set dummy "$func_split_equals_lhs" \ + "$func_split_equals_rhs" ${1+"$@"} + shift + ;; + + # Separate optargs to short options: + -W*) + func_split_short_opt "$_G_opt" + set dummy "$func_split_short_opt_name" \ + "$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + # Separate non-argument short options: + -\?*|-h*|-v*|-x*) + func_split_short_opt "$_G_opt" + set dummy "$func_split_short_opt_name" \ + "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + --) break ;; + -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; + *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; + esac + done + + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + func_parse_options_result=$func_quote_for_eval_result +} + + +# func_validate_options [ARG]... +# ------------------------------ +# Perform any sanity checks on option settings and/or unconsumed +# arguments. +func_hookable func_validate_options +func_validate_options () +{ + $debug_cmd + + # Display all warnings if -W was not given. + test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" + + func_run_hooks func_validate_options ${1+"$@"} + + # Bail if the options were screwed! + $exit_cmd $EXIT_FAILURE + + # save modified positional parameters for caller + func_validate_options_result=$func_run_hooks_result +} + + + + +# slingshot_options_prep +# ---------------------- +# Preparation for additional slingshot option parsing. +slingshot_options_prep () +{ + $debug_cmd + + # Option defaults: + opt_skip_rock_checks=false + # opt_luarocks_tree default in *unset*! + + # Extend the existing usage message. + usage_message=$usage_message' +Slingshot Options: + + --luarocks-tree=DIR + check a non-default tree for prerequisite rocks + --skip-rock-checks + ignore Lua rocks in bootstrap.conf:buildreq' + + func_quote_for_eval ${1+"$@"} + slingshot_options_prep_result=$func_quote_for_eval_result +} +func_add_hook func_options_prep slingshot_options_prep + + +# slingshot_parse_options OPT... +# ------------------------------ +# Called at the end of each main option parse loop to process any +# additional slingshot options. +slingshot_parse_options () +{ + $debug_cmd + + # Perform our own loop to consume as many options as possible in + # each iteration. + while test $# -gt 0; do + _G_opt=$1 + shift + case $_G_opt in + --luarocks-tree) + test $# = 0 && func_missing_arg $_G_opt && break + opt_luarocks_tree=$1 + shift + ;; + + --skip-rock-checks) + opt_skip_rock_checks=: + ;; + + # Separate optargs to long options (plugins may need this): + --*=*) func_split_equals "$_G_opt" + set dummy "$func_split_equals_lhs" \ + "$func_split_equals_rhs" ${1+"$@"} + shift + ;; + + *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; + esac + done + + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + slingshot_parse_options_result=$func_quote_for_eval_result +} +func_add_hook func_parse_options slingshot_parse_options + + +# slingshot_option_validation +# --------------------------- +# Flag any inconsistencies in users' selection of slingshot options. +slingshot_option_validation () +{ + $debug_cmd + + test -z "$opt_luarocks_tree" \ + || test -d "$opt_luarocks_tree" \ + || func_fatal_help "$opt_luarocks_tree: not a directory" +} +func_add_hook func_validate_options slingshot_option_validation + + +## ----------------- ## +## Helper functions. ## +## ----------------- ## + +# This section contains the helper functions used by the rest of the +# hookable option parser framework in ascii-betical order. + + +# func_fatal_help ARG... +# ---------------------- +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + $debug_cmd + + eval \$ECHO \""Usage: $usage"\" + eval \$ECHO \""$fatal_help"\" + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + + +# func_help +# --------- +# Echo long help message to standard output and exit. +func_help () +{ + $debug_cmd + + func_usage_message + $ECHO "$long_help_message" + exit 0 +} + + +# func_missing_arg ARGNAME +# ------------------------ +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + $debug_cmd + + func_error "Missing argument for '$1'." + exit_cmd=exit +} + + +# func_split_equals STRING +# ------------------------ +# Set func_split_equals_lhs and func_split_equals_rhs shell variables after +# splitting STRING at the '=' sign. +test -z "$_G_HAVE_XSI_OPS" \ + && (eval 'x=a/b/c; + test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ + && _G_HAVE_XSI_OPS=yes + +if test yes = "$_G_HAVE_XSI_OPS" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_split_equals () + { + $debug_cmd + + func_split_equals_lhs=${1%%=*} + func_split_equals_rhs=${1#*=} + test "x$func_split_equals_lhs" = "x$1" \ + && func_split_equals_rhs= + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_split_equals () + { + $debug_cmd + + func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` + func_split_equals_rhs= + test "x$func_split_equals_lhs" = "x$1" \ + || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` + } +fi #func_split_equals + + +# func_split_short_opt SHORTOPT +# ----------------------------- +# Set func_split_short_opt_name and func_split_short_opt_arg shell +# variables after splitting SHORTOPT after the 2nd character. +if test yes = "$_G_HAVE_XSI_OPS" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_split_short_opt () + { + $debug_cmd + + func_split_short_opt_arg=${1#??} + func_split_short_opt_name=${1%"$func_split_short_opt_arg"} + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_split_short_opt () + { + $debug_cmd + + func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` + func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` + } +fi #func_split_short_opt + + +# func_usage +# ---------- +# Echo short help message to standard output and exit. +func_usage () +{ + $debug_cmd + + func_usage_message + $ECHO "Run '$progname --help |${PAGER-more}' for full usage" + exit 0 +} + + +# func_usage_message +# ------------------ +# Echo short help message to standard output. +func_usage_message () +{ + $debug_cmd + + eval \$ECHO \""Usage: $usage"\" + echo + $SED -n 's|^# || + /^Written by/{ + x;p;x + } + h + /^Written by/q' < "$progpath" + echo + eval \$ECHO \""$usage_message"\" +} + + +# func_version +# ------------ +# Echo version message to standard output and exit. +func_version () +{ + $debug_cmd + + printf '%s\n' "$progname $scriptversion" + $SED -n ' + /(C)/!b go + :more + /\./!{ + N + s|\n# | | + b more + } + :go + /^# Written by /,/# warranty; / { + s|^# || + s|^# *$|| + s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| + p + } + /^# Written by / { + s|^# || + p + } + /^warranty; /q' < "$progpath" + + exit $? +} + + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: +#! /bin/sh + +# Extract macro arguments from autotools input with GNU M4. +# Written by Gary V. Vaughan, 2010 +# +# Copyright (C) 2010-2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# Make sure we've evaluated scripts we depend on. +test -z "$progpath" && . `echo "$0" |${SED-sed} 's|[^/]*$||'`/funclib.sh +test extract-trace = "$progname" && . `echo "$0" |${SED-sed} 's|[^/]*$||'`/options-parser + +# Set a version string. +scriptversion=2014-12-03.16; # UTC + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Please report bugs or propose patches to gary@gnu.org. + + +# slingshot_copy FILENAME SRCDIR DESTDIR +# -------------------------------------- +# If option '--copy' was specified, or soft-linking SRCFILE to DESTFILE +# fails, then try to copy SRCFILE to DESTFILE (making sure to update the +# timestamp so that a series of files with dependencies can be copied +# in the right order that their timestamps won't trigger rebuilds). +slingshot_copy () +{ + $debug_cmd + + slingshot_srcfile=`echo "$2/$1" |sed -e 's|/\./|/|g'` + slingshot_destfile=`echo "$3/$1" |sed -e 's|/\./|/|g'` + + $opt_force || { + # Nothing to do if the files are already identical. + if func_cmp_s "$slingshot_srcfile" "$slingshot_destfile"; then + func_verbose "'$slingshot_destfile' is up to date." + return 0 + fi + } + + # Require --force to remove existing $slingshot_destfile. + $opt_force && $RM "$slingshot_destfile" + test -f "$slingshot_destfile" && { + func_warn_and_continue "'$slingshot_destfile' exists: use '--force' to overwrite" + return 0 + } + + # Be careful to support 'func_copy dir/target srcbase destbase'. + func_dirname "$slingshot_destfile" + func_mkdir_p "$func_dirname_result" + + # Copy or link according to '--copy' option. + if $opt_copy; then + slingshot_copycmd=$CP + slingshot_copy_type=copying + else + slingshot_copycmd=$LN_S + slingshot_copy_type=linking + + func_relative_path "$3" "$2" + slingshot_srcfile=$func_relative_path_result/$1 + fi + slingshot_copy_msg="$slingshot_copy_type file '$slingshot_destfile'" + $opt_verbose && \ + slingshot_copy_msg="$slingshot_copy_type $slingshot_srcfile $3" + + if $opt_dry_run || { + ( umask 0 + $slingshot_copycmd "$slingshot_srcfile" "$slingshot_destfile" + ) >/dev/null 2>&1 + } + then + echo "$slingshot_copy_msg" + else + func_error "$slingshot_copy_type '$2/$1' to '$3/' failed" + return 1 + fi +} + + +# slingshot_rockspec_error +# ------------------------ +# Called by zile_check_rockspecs for missing rocks. +slingshot_rockspec_error () +{ + $debug_cmd + + _G_strippedver=`expr "$_G_reqver" : '=*\(.*\)'` + func_error "\ +Prerequisite LuaRock '$_G_rock $_G_strippedver' not found. Please install it." + + rockspecs_uptodate_result=false +} + + +## ------ ## +## Usage. ## +## ------ ## + +# Run './extract-trace --help' for help with using this script from the +# command line. +# +# Or source first 'options-parser' and then this file into your own +# scripts in order to make use of the function and variable framework +# they define, and also to avoid the overhead of forking to run this +# script in its own process on every call. + + + +## ----------------- ## +## Helper functions. ## +## ----------------- ## + +# This section contains the helper functions used by the rest of +# 'extract-trace'. + + +# func_autoconf_configure MAYBE-CONFIGURE-FILE +# -------------------------------------------- +# Ensure that MAYBE-CONFIGURE-FILE is the name of a file in the current +# directory that contains an uncommented call to AC_INIT. +func_autoconf_configure () +{ + $debug_cmd + + _G_sed_no_comment=' + s|#.*$|| + s|^dnl .*$|| + s| dnl .*$||' + _G_ac_init= + + # If we were passed a genuine file, make sure it calls AC_INIT. + test -f "$1" \ + && _G_ac_init=`$SED "$_G_sed_no_comment" "$1" |$GREP AC_INIT` + + # Otherwise it is not a genuine Autoconf input file. + test -n "$_G_ac_init" + _G_status=$? + + test 0 -ne "$_G_status" \ + && func_verbose "'$1' not using Autoconf" + + (exit $_G_status) +} + + +# func_tool_version_output CMD [FATAL-ERROR-MSG] +# ---------------------------------------------- +# Attempt to run 'CMD --version', discarding errors. The output can be +# ignored by redirecting stdout, and this function used simply to test +# whether the command exists and exits normally when passed a +# '--version' argument. +# When FATAL-ERROR-MSG is given, then this function will display the +# message and exit if running 'CMD --version' returns a non-zero exit +# status. +func_tool_version_output () +{ + $debug_cmd + + _G_cmd=$1 + _G_fatal_error_msg=$2 + + # Some tools, like 'git2cl' produce thousands of lines of output + # unless stdin is /dev/null - in that case we want to return + # successfully without saving all of that output. Other tools, + # such as 'help2man' exit with a non-zero status when stdin comes + # from /dev/null, so we re-execute without /dev/null if that + # happens. This means that occasionally, the output from both calls + # ends up in the result, but the alternative would be to discard the + # output from one call, and hope the other produces something useful. + { $_G_cmd --version /dev/null + _G_status=$? + + test 0 -ne "$_G_status" && test -n "$_G_fatal_error_msg" \ + && func_fatal_error "$_G_fatal_error_msg" + + (exit $_G_status) +} + + +# func_tool_version_number CMD [FATAL-ERROR-MSG] +# ---------------------------------------------- +# Pass arguments to func_tool_version_output, but set +# $func_tool_version_number_result to the last dot delimited digit string +# on the first line of output. +func_tool_version_number () +{ + $debug_cmd + + _G_verout=`func_tool_version_output "$@"` + _G_status=$? + + # A version number starts with a digit following a space on the first + # line of output from `--version`. + _G_verout=`echo "$_G_verout" |sed 1q` + if test -n "$_G_verout"; then + _G_vernum=`expr "$_G_verout" : '.* \([0-9][^ ]*\)'` + fi + + if test -n "$_G_vernum"; then + printf '%s\n' "$_G_vernum" + else + printf '%s\n' "$_G_verout" + fi + + (exit $_G_status) +} + + +# func_find_tool ENVVAR NAMES... +# ------------------------------ +# Search for a required program. Use the value of ENVVAR, if set, +# otherwise find the first of the NAMES that can be run (i.e., +# supports --version). If found, set ENVVAR to the program name, +# die otherwise. +func_find_tool () +{ + $debug_cmd + + _G_find_tool_envvar=$1 + shift + _G_find_tool_names=$@ + eval "_G_find_tool_res=\$$_G_find_tool_envvar" + if test -n "$_G_find_tool_res"; then + _G_find_tool_error_prefix="\$$find_tool_envvar: " + else + _G_find_tool_res= + _G_bestver= + for _G_prog + do + _G_find_tool_save_IFS=$IFS + IFS=: + for _G_dir in $PATH; do + IFS=$_G_find_tool_save_IFS + _G_progpath=$_G_dir/$_G_prog + test -r "$_G_progpath" && { + _G_curver=`func_tool_version_number $_G_progpath` + case $_G_bestver,$_G_curver in + ,) + # first non--version responsive prog sticks! + test -n "$_G_progpath" || _G_find_tool_res=$_G_progpath + ;; + ,*) + # first --version responsive prog beats non--version responsive! + _G_find_tool_res=$_G_progpath + _G_bestver=$_G_curver + ;; + *,*) + # another --version responsive prog must be newer to beat previous one! + test "x$_G_curver" = "x$_G_bestver" \ + || func_lt_ver "$_G_curver" "$_G_bestver" \ + || { + _G_find_tool_res=$_G_progpath + _G_bestver=$_G_curver + } + ;; + esac + } + done + IFS=$_G_find_tool_save_IFS + done + fi + if test -n "$_G_find_tool_res"; then + func_tool_version_number >/dev/null $_G_find_tool_res "\ +${_G_find_tool_error_prefix}Cannot run '$_G_find_tool_res --version'" + + # Make sure the result is exported to the environment for children + # to use. + eval "$_G_find_tool_envvar=\$_G_find_tool_res" + eval "export $_G_find_tool_envvar" + else + func_error "\ +One of these is required: + $_G_find_tool_names" + fi +} + + + +## -------------------- ## +## Resource management. ## +## -------------------- ## + +# This section contains definitions for functions that each ensure a +# particular resource (a file, or a non-empty configuration variable for +# example) is available, and if appropriate to extract default values +# from pertinent package files. Where a variable already has a non- +# empty value (as set by the package's 'bootstrap.conf'), that value is +# used in preference to deriving the default. Call them using their +# associated 'require_*' variable to ensure that they are executed, at +# most, once. +# +# It's entirely deliberate that calling these functions can set +# variables that don't obey the namespace limitations obeyed by the rest +# of this file, in order that that they be as useful as possible to +# callers. + + +# require_configure_ac +# -------------------- +# Ensure that there is a 'configure.ac' or 'configure.in' file in the +# current directory that contains an uncommented call to AC_INIT, and +# that '$configure_ac' contains its name. +require_configure_ac=func_require_configure_ac +func_require_configure_ac () +{ + $debug_cmd + + test -z "$configure_ac" \ + && func_autoconf_configure configure.ac && configure_ac=configure.ac + test -z "$configure_ac" \ + && func_autoconf_configure configure.in && configure_ac=configure.in + test -z "$configure_ac" \ + || func_verbose "found '$configure_ac'" + + require_configure_ac=: +} + + +# require_gnu_m4 +# -------------- +# Search for GNU M4, and export it in $M4. +require_gnu_m4=func_require_gnu_m4 +func_require_gnu_m4 () +{ + $debug_cmd + + test -n "$M4" || { + # Find the first m4 binary that responds to --version. + func_find_tool M4 gm4 gnum4 m4 + } + + test -n "$M4" || func_fatal_error "\ +Please install GNU M4, or 'export M4=/path/to/gnu/m4'." + + func_verbose "export M4='$M4'" + + # Make sure the search result is visible to subshells + export M4 + + require_gnu_m4=: +} + + +## --------------- ## +## Core functions. ## +## --------------- ## + +# This section contains the high level functions used when calling this +# file as a script. 'func_extract_trace' is probably the only one that you +# won't want to replace if you source this file into your own script. + + +# func_extract_trace MACRO_NAMES [FILENAME]... +# -------------------------------------------- +# set '$func_extract_trace_result' to a colon delimited list of arguments +# to any of the comma separated list of MACRO_NAMES in FILENAME. If no +# FILENAME is given, then '$configure_ac' is assumed. +func_extract_trace () +{ + $debug_cmd + + $require_configure_ac + $require_gnu_m4 + + _G_m4_traces=`$ECHO "--trace=$1" |$SED 's%,% --trace=%g'` + _G_re_macros=`$ECHO "($1)" |$SED 's%,%|%g'` + _G_macros="$1"; shift + test $# -gt 0 || { + set dummy $configure_ac + shift + } + + # Generate an error if the first file is missing + <"$1" + + # Sadly, we can't use 'autom4te' tracing to extract macro arguments, + # because it complains about things we want to ignore at bootstrap + # time - like missing m4_include files; AC_PREREQ being newer than + # the installed autoconf; and returns nothing when tracing + # 'AM_INIT_AUTOMAKE' when aclocal hasn't been generated yet. + # + # The following tries to emulate a less persnickety version of (and + # due to not having to wait for Perl startup on every invocation, + # it's probably faster too): + # + # autom4te --language=Autoconf --trace=$my_macro:\$% "$@" + # + # First we give a minimal set of macro declarations to M4 to prime + # it for reading Autoconf macros, while still providing some of the + # functionality generally used at m4-time to supply dynamic + # arguments to Autocof functions, but without following + # 'm4_s?include' files. + _G_mini=' + # Initialisation. + m4_changequote([,]) + m4_define([m4_copy], [m4_define([$2], m4_defn([$1]))]) + m4_define([m4_rename], [m4_copy([$1], [$2])m4_undefine([$1])]) + + # Disable these macros. + m4_undefine([m4_dnl]) + m4_undefine([m4_include]) + m4_undefine([m4_m4exit]) + m4_undefine([m4_m4wrap]) + m4_undefine([m4_maketemp]) + + # Copy and rename macros not handled by "m4 --prefix". + m4_define([dnl], [m4_builtin([dnl])]) + m4_copy([m4_define], [m4_defun]) + m4_rename([m4_ifelse], [m4_if]) + m4_ifdef([m4_mkstemp], [m4_undefine([m4_mkstemp])]) + m4_rename([m4_patsubst], [m4_bpatsubst]) + m4_rename([m4_regexp], [m4_bregexp]) + + # "m4sugar.mini" - useful m4-time macros for dynamic arguments. + # If we discover packages that need more m4 macros defined in + # order to bootstrap correctly, add them here: + m4_define([m4_bmatch], + [m4_if([$#], 0, [], [$#], 1, [], [$#], 2, [$2], + [m4_if(m4_bregexp([$1], [$2]), -1, + [$0([$1], m4_shift3($@))], [$3])])]) + m4_define([m4_ifndef], [m4_ifdef([$1], [$3], [$2])]) + m4_define([m4_ifset], + [m4_ifdef([$1], [m4_ifval(m4_defn([$1]), [$2], [$3])], [$3])]) + m4_define([m4_require], [$1]) + m4_define([m4_shift3], [m4_shift(m4shift(m4shift($@)))]) + + # "autoconf.mini" - things from autoconf macros we care about. + m4_copy([m4_defun], [AC_DEFUN]) + + # Dummy definitions for the macros we want to trace. + # AM_INIT_AUTOMAKE at least produces no trace without this. + ' + + _G_save=$IFS + IFS=, + for _G_macro in $_G_macros; do + IFS=$_G_save + func_append _G_mini "AC_DEFUN([$_G_macro])$nl" + done + IFS=$_G_save + + # We discard M4's stdout, but the M4 trace output from reading our + # "autoconf.mini" followed by any other files passed to this + # function is then scanned by sed to transform it into a colon + # delimited argument list assigned to a shell variable. + _G_transform='s|#.*$||; s|^dnl .*$||; s| dnl .*$||;' + + # Unfortunately, alternation in regexp addresses doesn't work in at + # least BSD (and hence Mac OS X) sed, so we have to append a capture + # and print block for each traced macro to the sed transform script. + _G_save=$IFS + IFS=, + for _G_macro in $_G_macros; do + IFS=$_G_save + func_append _G_transform ' + /^m4trace: -1- '"$_G_macro"'/ { + s|^m4trace: -1- '"$_G_macro"'[([]*|| + s|], [[]|:|g + s|[])]*$|:| + s|\(.\):$|\1| + p + }' + done + IFS=$_G_save + + # Save the command pipeline results for further use by callers of + # this function. + func_extract_trace_result=`$ECHO "$_G_mini" \ + |$M4 -daq --prefix $_G_m4_traces - "$@" 2>&1 1>/dev/null \ + |$SED -n -e "$_G_transform"` +} + + +# func_extract_trace_first MACRO_NAMES [FILENAME]... +# -------------------------------------------------- +# Exactly like func_extract_trace, except that only the first argument +# to the first invocation of one of the comma separated MACRO_NAMES is +# returned in '$func_extract_trace_first_result'. +func_extract_trace_first () +{ + $debug_cmd + + func_extract_trace ${1+"$@"} + func_extract_trace_first_result=`$ECHO "$func_extract_trace_result" \ + |$SED -e 's|:.*$||g' -e 1q` +} + + +# func_main [ARG]... +# ------------------ +func_main () +{ + $debug_cmd + + # Configuration. + usage='$progname MACRO_NAME FILE [...]' + + long_help_message=' +The first argument to this program is the name of an autotools macro +whose arguments you want to extract by examining the files listed in the +remaining arguments using the same tool that Autoconf and Automake use, +GNU M4. + +The arguments are returned separated by colons, with each traced call +on a separate line.' + + # Option processing. + func_options "$@" + eval set dummy "$func_options_result"; shift + + # Validate remaining non-option arguments. + test $# -gt 1 \ + || func_fatal_help "not enough arguments" + + # Pass non-option arguments to extraction function. + func_extract_trace "$@" + + # Display results. + test -n "$func_extract_trace_result" \ + && $ECHO "$func_extract_trace_result" + + # The End. + exit $EXIT_SUCCESS +} + + +## --------------------------- ## +## Actually perform the trace. ## +## --------------------------- ## + +# Only call 'func_main' if this script was called directly. +test extract-trace = "$progname" && func_main "$@" + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "20/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: + +# Set a version string for *this* script. +scriptversion=2014-11-04.13; # UTC + + +## ------------------- ## +## Hookable functions. ## +## ------------------- ## + +# After 'bootstrap.conf' has been sourced, execution proceeds by calling +# 'func_bootstrap'. Wherever a function is decorated with +# 'func_hookable func_name', you will find a matching 'func_run_hooks +# func_name', which executes all functions added with 'func_add_hook +# func_name my_func'. +# +# You might notice that many of these functions begin with a series of +# '$require_foo' lines. See the docu-comments at the start of the +# 'Resource management' section for a description of what these are. + + +# func_bootstrap [ARG]... +# ----------------------- +# All the functions called inside func_bootstrap are hookable. See the +# the individual implementations for details. +func_bootstrap () +{ + $debug_cmd + + # Save the current positional parameters to prevent them being + # corrupted by calls to 'set' in 'func_init'. + func_quote_for_eval ${1+"$@"} + _G_saved_positional_parameters=$func_quote_for_eval_result + + # Initialisation. + func_init + + # Option processing. + eval func_options "$_G_saved_positional_parameters" + + # Post-option preparation. + func_prep + + # Reconfigure the package. + func_reconfigure + + # Ensure .version is up-to-date. + func_update_dotversion + + # Finalisation. + func_fini +} + + +# func_init +# --------- +# Any early initialisations can be hooked to this function. Consider +# whether you can hook onto 'func_prep' instead, because if you hook +# any slow to execute code in here, it will also add to the time before +# './bootstrap --version' can respond. +func_hookable func_init +func_init () +{ + $debug_cmd + + func_run_hooks func_init +} + + +# func_prep +# --------- +# Function to perform preparation for remaining bootstrap process. If +# your hooked code relies on the outcome of 'func_options' hook it here +# rather than to 'func_init'. +# +# All the functions called inside func_prep are hookable. See the +# individual implementations for details. +func_hookable func_prep +func_prep () +{ + $debug_cmd + + $require_buildtools_uptodate + $require_checkout_only_file + + $require_gnulib_merge_changelog + + # Report the results of SED and GREP searches from funclib.sh. + func_verbose "GREP='$GREP'" + func_verbose "SED='$SED'" + + # fetch update files from the translation project + func_update_translations + + func_run_hooks func_prep +} + + +# func_update_translations +# ------------------------ +# Update package po files and translations. +func_hookable func_update_translations +func_update_translations () +{ + $debug_cmd + + $opt_skip_po || { + test -d po && { + $require_package + + func_update_po_files po $package || exit $? + } + + func_run_hooks func_update_translations + } +} + + +# func_reconfigure +# ---------------- +# Reconfigure the current package by running the appropriate autotools in a +# suitable order. +func_hookable func_reconfigure +func_reconfigure () +{ + $debug_cmd + + $require_automake_options + + # Automake (without 'foreign' option) requires that NEWS & README exist. + case " $automake_options " in + " foreign ") ;; + *) + func_ensure_NEWS + func_ensure_README + ;; + esac + + # Ensure ChangeLog presence. + if test -n "$gnulib_modules"; then + func_ifcontains "$gnulib_modules" gitlog-to-changelog \ + func_ensure_changelog + else + $require_gnulib_cache + if $SED -n '/^gl_MODULES(\[/,/^])$/p' $gnulib_cache 2>/dev/null | + func_grep_q gitlog-to-changelog + then + func_ensure_changelog + fi + fi + + # Released 'autopoint' has the tendency to install macros that have + # been obsoleted in current 'gnulib', so run this before 'gnulib-tool'. + func_autopoint + + # Autoreconf runs 'aclocal' before 'libtoolize', which causes spurious + # warnings if the initial 'aclocal' is confused by the libtoolized + # (or worse: out-of-date) macro directory. + func_libtoolize + + # If you need to do anything after 'gnulib-tool' is done, but before + # 'autoreconf' runs, you don't need to override this whole function, + # because 'func_gnulib_tool' is hookable. + func_gnulib_tool + + func_autoreconf + + func_run_hooks func_reconfigure +} + + +# func_gnulib_tool +# ---------------- +# Run 'gnulib-tool' to fetch gnulib modules into the current package. +# +# It's assumed that since you are using gnulib's 'bootstrap' script, +# you're also using gnulib elsewhere in your package. If not, then +# you can replace this function in 'bootstrap.conf' with: +# +# func_gnulib_tool () { :; } +# +# (although the function returns immediately if $gnulib_tool is set to +# true in any case). +func_hookable func_gnulib_tool +func_gnulib_tool () +{ + $debug_cmd + + $require_gnulib_tool + $require_libtoolize + + test true = "$gnulib_tool" || { + # bootstrap.conf written for gnulib bootstrap expects + # gnulib_tool_option_extras to which --no-changelog is appended, + # but libtool bootstrap expects you to append to gnulib_tool_options + # so that you can override the --no-changelog default: make sure we + # support both styles so users can migrate between them easily. + gnulib_tool_all_options="$gnulib_tool_options $gnulib_tool_option_extras" + + if test -n "$gnulib_modules"; then + $require_gnulib_cache + $require_gnulib_tool_base_options + + gnulib_mode=--import + + # Try not to pick up any stale values from 'gnulib-cache.m4'. + rm -f "$gnulib_cache" + + test -n "$gnulib_tool_base_options" \ + && func_append_uniq gnulib_tool_all_options " $gnulib_tool_base_options" + test -n "$gnulib_mk" \ + && func_append_uniq gnulib_tool_all_options " --makefile-name=$gnulib_mk" + test -n "$tests_base" && { + func_append_uniq gnulib_tool_all_options " --tests-base=$tests_base" + func_append_uniq gnulib_tool_all_options " --with-tests" + } + else + + # 'gnulib_modules' and others are cached in 'gnulib-cache.m4': + # Use 'gnulib --update' to fetch gnulib modules. + gnulib_mode=--update + fi + + # Add a sensible default libtool option to gnulib_tool_options. + # The embedded echo is to squash whitespace before globbing. + case `echo " "$gnulib_tool_all_options" "` in + *" --no-libtool "*|*" --libtool "*) ;; + *) if test true = "$LIBTOOLIZE"; then + func_append_uniq gnulib_tool_all_options " --no-libtool" + else + func_append_uniq gnulib_tool_all_options " --libtool" + fi + ;; + esac + + $opt_copy || func_append_uniq gnulib_tool_all_options " --symlink" + + func_append_uniq gnulib_tool_all_options " $gnulib_mode" + func_append gnulib_tool_all_options " $gnulib_modules" + + # The embedded echo is to squash whitespace before display. + gnulib_cmd=`echo $gnulib_tool $gnulib_tool_all_options` + + func_show_eval "$gnulib_cmd" 'exit $?' + + # Use 'gnulib-tool --copy-file' to install non-module files. + func_install_gnulib_non_module_files + } + + func_run_hooks func_gnulib_tool +} + + +# func_fini +# --------- +# Function to perform all finalisation for the bootstrap process. +func_hookable func_fini +func_fini () +{ + $debug_cmd + + func_gettext_configuration + func_clean_dangling_symlinks + func_clean_unused_macros + func_skip_po_recommendation + + func_run_hooks func_fini + + $require_bootstrap_uptodate + + func_echo "Done. Now you can run './configure'." +} + + +# func_gettext_configuration +# -------------------------- +# Edit configuration values into po/Makevars. +func_hookable func_gettext_configuration +func_gettext_configuration () +{ + $debug_cmd + + $require_autopoint + + test true = "$AUTOPOINT" || { + $require_copyright_holder + $require_extra_locale_categories + $require_package_bugreport + + # Escape xgettext options for sed Makevars generation below. + # We have to delete blank lines in a separate script so that we don't + # append \\\ to the penultimate line, and then delete the last empty + # line, which messes up the variable substitution later in this + # function. Note that adding a literal \\\ requires double escaping + # here, once for the execution subshell, and again for the assignment, + # which is why there are actually 12 (!!) backslashes in the script. + _G_xgettext_options=`echo "$xgettext_options$nl" |$SED '/^$/d' |$SED ' + $b + s|$| \\\\\\\\\\\\|'` + + # Create gettext configuration. + func_echo "Creating po/Makevars from po/Makevars.template ..." + $RM -f po/Makevars + $SED ' + /^EXTRA_LOCALE_CATEGORIES *=/s|=.*|= '"$extra_locale_categories"'| + /^COPYRIGHT_HOLDER *=/s|=.*|= '"$copyright_holder"'| + /^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$package_bugreport"'| + /^XGETTEXT_OPTIONS *=/{ + s|$| \\| + a\ + '"$_G_xgettext_options"' \\\ + $${end_of_xgettext_options+} + } + ' po/Makevars.template >po/Makevars || exit 1 + } + + func_run_hooks func_gettext_configuration +} + + + +## The section title above is chosen for what section of bootstrap +## these functions will be merged to, so that the invocations of +## `func_add_hook` are guaranteed not to be executed until after +## the hook management functions are defined. + + +# slingshot_split_buildreq +# ------------------------ +# For convenience, let the user add rockspec requirements to $buildreq. +# Note that this is for *build-time* requirements (e.g. ldoc), so that +# make can complete without error. You should add *run-time* rockspec +# requirements (e.g. stdlib) to rockspec.conf. +slingshot_split_buildreq () +{ + $debug_cmd + + $require_rockspecs_req +} +func_add_hook func_init slingshot_split_buildreq + + +# slingshot_check_rockspecs +# ------------------------- +# Check build-time rockspecs from $buildreq are uptodate. +# It would be nice if we could rely on luarock binaries to respond to +# `--version` like GNU apps, but there is no reliable consensus, so we +# have to check installed luarock versions directly, and warn the user +# if the apps we're checking for are not somewhere along PATH. +slingshot_check_rockspecs () +{ + $debug_cmd + + $opt_skip_rock_checks && return + + $require_rockspecs_req + + _G_req= + rockspecs_uptodate_result=: + + set dummy $rockspecs_req; shift + while test $# -gt 0; do + _G_rock=$1; shift + _G_reqver=$1; shift + _G_url=$1; shift + + func_append _G_req " $_G_rock $_G_url" + + # Honor $APP variables ($LDOC, $SPECL, etc.) + _G_appvar=`echo $_G_rock |tr '[a-z]' '[A-Z]'` + eval "_G_rock=\${$_G_appvar-$_G_rock}" + + # Trust the user will ensure the binaries will arive at the + # specified location before they are needed if they set these. + if eval 'test -n "${'$_G_appvar'+set}"'; then + eval test -f '"${'$_G_appvar'}"' \ + || eval 'func_warning settings "\ +not checking whether $'$_G_appvar' has version $_G_reqver; +configure or make may fail because you set $_G_appvar, but +$'$_G_appvar' does not yet exist!"' + else + _G_instver=`$LUAROCKS ${opt_luarocks_tree+--tree=$opt_luarocks_tree} \ + show $_G_rock 2>/dev/null \ + |sed -n '/^'"$_G_rock"' .* - /{s/^'"$_G_rock"' \(.*\) - .*$/\1/p;}'` + + if test -z "$_G_instver"; then + slingshot_rockspec_error + else + func_verbose "found '$_G_rock' version $_G_instver." + + case $_G_reqver in + =*) + test "x$_G_reqver" = "x=$_G_instver" || slingshot_rockspec_error + ;; + *) + func_lt_ver "$_G_reqver" "$_G_instver" || slingshot_rockspec_error + ;; + esac + fi + fi + done + + $rockspecs_uptodate_result || { + func_strtable 0 10 48 \ + "Program" "Rockspec_URL" $_G_req + func_fatal_error "Missing rocks: +$func_strtable_result +Install missing rockspecs with: + $LUAROCKS ${opt_luarocks_tree+--tree=$opt_luarocks_tree }install \$Rockspec_URL +and then rerun bootstrap with the --luarocks-tree option set +appropriately, or if you're sure that the missing rocks will +be installed before running make by exporting: + APPNAME=/path/to/app. +" + } +} +func_add_hook func_prep slingshot_check_rockspecs + + +# slingshot_copy_files +# -------------------- +# Update files from slingshot subproject. +slingshot_copy_files () +{ + $debug_cmd + + $require_package + + test slingshot = "$package" || { + func_check_configuration slingshot_files + + $require_slingshot_submodule + + # Make sure we have the latest mkrockspecs + # (the rebootstrap rule in slingshot/GNUmakefile autoruns). + make -C slingshot build-aux/mkrockspecs + + # Update in-tree links. + for file in $slingshot_files; do + func_dirname_and_basename "./$file" + slingshot_copy "$func_basename_result" \ + "slingshot/$func_dirname_result" "$func_dirname_result" + done + } +} +func_add_hook func_prep slingshot_copy_files + + +# slingshot_ensure_changelog +# -------------------------- +# Slingshot project probably won't have a gnulib_modules list. +# So we redo the ChangeLog check against slingshot_files. +slingshot_ensure_changelog () +{ + $debug_cmd + + if test -n "$slingshot_files"; then + func_ifcontains "$slingshot_files" build-aux/gitlog-to-changelog \ + func_ensure_changelog + fi + + return 0 +} +func_add_hook func_prep slingshot_ensure_changelog + + +# slingshot_update_travis_yml +# --------------------------- +# When 'travis.yml.in' is listed in $slingshot_files, complain if +# regenerating '.travis.yml' would change it. +slingshot_update_travis_yml () +{ + $debug_cmd + + $require_git + + _G_travis_yml_in=travis.yml.in + _G_travis_yml_out=.travis.yml + + rm -f "$_G_travis_yml_out.new" + + test true = "$GIT" || { + case " "`echo $slingshot_files`" " in + *" travis.yml.in "*) + # Remove trailing blanks so as not to trip sc_trailing_blank in syntax check + test -f "$_G_travis_yml_in" && { + $slingshot_require_travis_extra_rocks + + eval `grep '^ *PACKAGE=' configure | sed 1q` + eval `grep '^ *VERSION=' configure | sed 1q` + + cat "$_G_travis_yml_in" | + sed 's| *$||' | + sed "s|@EXTRA_ROCKS@|`echo $travis_extra_rocks`|g" | + sed "s|@PACKAGE@|$PACKAGE|g" | + sed "s|@VERSION@|$VERSION|g" + + if test -f .slackid; then + read slackid < .slackid + printf '%s\n' '' 'notifications:' " slack: $slackid" + fi + } > "$_G_travis_yml_out.new" + + if test -f "$_G_travis_yml_out"; then + if func_cmp_s "$_G_travis_yml_out" "$_G_travis_yml_out.new"; then + func_verbose "$_G_travis_yml_out is up to date" + rm -f "$_G_travis_yml_out.new" + else + func_warning upgrade "\ +An updated $_G_travis_yml_out script is ready for you in +'$_G_travis_yml_out.new'. After you've verified that you want +the changes, you can update with: + mv -f $_G_travis_yml_out.new $_G_travis_yml_out" + fi + else + func_verbose "creating '$_G_travis_yml_out'" + mv -f "$_G_travis_yml_out.new" "$_G_travis_yml_out" + fi + ;; + esac + } +} +func_add_hook func_fini slingshot_update_travis_yml + + +# slingshot_check_rockstree_path +# ------------------------------ +# Show a warning at the end of bootstrap if --luarocks-tree was passed +# set, but $opt_luarocks_tree/bin is not in the command PATH. +slingshot_check_rockstree_path () +{ + $debug_cmd + + test -z "$rockspecs_req" || { + case :$PATH: in + *:$opt_luarocks_tree/bin:*) ;; + *) func_warning recommend \ + "If configure or make fail, try adding $opt_luarocks_tree/bin to PATH" ;; + esac + } +} +func_add_hook func_fini slingshot_check_rockstree_path + + +## --------------- ## +## Core functions. ## +## --------------- ## + +# This section contains the main functions called from the 'Hookable +# functions' (shown above), and are the ones you're most likely +# to want to replace with your own implementations in 'bootstrap.conf'. + + +# func_autopoint +# -------------- +# If this package uses gettext, then run 'autopoint'. +func_autopoint () +{ + $debug_cmd + + $require_autopoint + + test true = "$AUTOPOINT" \ + || func_show_eval "$AUTOPOINT --force" 'exit $?' +} + + +# func_libtoolize +# --------------- +# If this package uses libtool, then run 'libtoolize'. +func_libtoolize () +{ + $debug_cmd + + $require_libtoolize + + test true = "$LIBTOOLIZE" || { + _G_libtoolize_options= + $opt_copy && func_append _G_libtoolize_options " --copy" + $opt_force && func_append _G_libtoolize_options " --force" + $opt_verbose || func_append _G_libtoolize_options " --quiet" + func_show_eval "$LIBTOOLIZE$_G_libtoolize_options" 'exit $?' + } +} + + +# func_gnulib_tool_copy_file SRC DEST +# ----------------------------------- +# Copy SRC, a path relative to the gnulib sub-tree, to DEST, a path +# relative to the top-level source directory using gnulib-tool so that +# any patches or replacements in $local_gl_dir are applied. +func_gnulib_tool_copy_file () +{ + $debug_cmd + + $require_gnulib_tool + $require_patch + + if test true = "$gnulib_tool"; then + # If gnulib-tool is not available (e.g. bootstrapping in a + # distribution tarball), make sure that at least we have some + # version of the required file already in place. + test -f "$2" || func_fatal_error "\ +Can't find, copy or download '$2', a required +gnulib supplied file, please provide the location of a +complete 'gnulib' tree by setting 'gnulib_path' in your +'bootstrap.conf' or with the '--gnulib-srcdir' option - +or else specify the location of your 'git' binary by +setting 'GIT' in the environment so that a fresh +'gnulib' submodule can be cloned." + else + $require_gnulib_copy_cmd + + $gnulib_copy_cmd $1 $2 2>/dev/null || { + $require_gnulib_path + + func_error "'$gnulib_path/$1' does not exist" + return 1 + } + fi +} + + +# func_install_gnulib_non_module_files +# ------------------------------------ +# Get additional non-module files from gnulib, overriding existing files. +func_install_gnulib_non_module_files () +{ + $debug_cmd + + $require_build_aux + $require_gnulib_tool + + test -n "$gnulib_non_module_files" && { + maybe_exit_cmd=: + + for file in $gnulib_non_module_files; do + case $file in + */COPYING*) dest=COPYING;; + */INSTALL) dest=INSTALL;; + build-aux/missing) dest= + func_warning settings "\ +Please remove build-aux/missing from gnulib_module_files in +'bootstrap.conf', as it may clash with Automake's version." + ;; + build-aux/*) dest=$build_aux/`expr "$file" : 'build-aux/\(.*\)'`;; + *) dest=$file;; + esac + + # Be sure to show all copying errors before bailing out + test -z "$dest" \ + || func_gnulib_tool_copy_file "$file" "$dest" \ + || maybe_exit_cmd="exit $EXIT_FAILURE" + done + + $maybe_exit_cmd + } +} + + +# func_ensure_changelog +# --------------------- +# Even with 'gitlog-to-changelog' generated ChangeLogs, automake +# will not run to completion with no ChangeLog file. +func_ensure_changelog () +{ + $debug_cmd + + test -f ChangeLog && mv -f ChangeLog ChangeLog~ + + cat >ChangeLog <<'EOT' +## ---------------------- ## +## DO NOT EDIT THIS FILE! ## +## ---------------------- ## + +ChangeLog is generated by gitlog-to-changelog. +EOT + + _G_message="creating dummy 'ChangeLog'" + test -f ChangeLog~ \ + && func_append _G_message ' (backup in ChangeLog~)' + func_verbose "$_G_message" + + return 0 +} + + +# func_ensure_NEWS +# ---------------- +# Without AM_INIT_AUTOMAKE([foreign]), automake will not run to +# completion with no NEWS file, even though NEWS.md or NEWS.txt +# is often preferable. +func_ensure_NEWS () +{ + $debug_cmd + + test -f NEWS || { + _G_NEWS= + for _G_news in NEWS.txt NEWS.md NEWS.rst; do + test -f "$_G_news" && break + done + + test -f "$_G_news" && $LN_S $_G_news NEWS + func_verbose "$LN_S $_G_news NEWS" + } + + return 0 +} + + +# func_ensure_README +# ------------------ +# Without AM_INIT_AUTOMAKE([foreign]), automake will not run to +# completion with no README file, even though README.md or README.txt +# is often preferable. +func_ensure_README () +{ + $debug_cmd + + test -f README || { + _G_README= + for _G_readme in README.txt README.md README.rst; do + test -f "$_G_readme" && break + done + + test -f "$_G_readme" && $LN_S $_G_readme README + func_verbose "$LN_S $_G_readme README" + } + + return 0 +} + + +# func_autoreconf [SUBDIR] +# ------------------------ +# Being careful not to re-run 'autopoint' or 'libtoolize', and not to +# try to run 'autopoint', 'libtoolize' or 'autoheader' on packages that +# don't use them, defer to 'autoreconf' for execution of the remaining +# autotools to bootstrap this package. +# +# Projects with multiple trees to reconfigure can hook another call to +# this function onto func_reconfigure: +# +# my_autoreconf_foo () +# { +# func_autoreconf foo +# } +# func_add_hook func_reconfigure my_autoreconf_foo +func_autoreconf () +{ + $debug_cmd + + $require_autoheader + $require_build_aux # automake and others put files in here + $require_macro_dir # aclocal and others put files in here + + # We ran these manually already, and autoreconf won't exec ':' + save_AUTOPOINT=$AUTOPOINT; AUTOPOINT=true + save_LIBTOOLIZE=$LIBTOOLIZE; LIBTOOLIZE=true + + _G_autoreconf_options= + $opt_copy || func_append _G_autoreconf_options " --symlink" + $opt_force && func_append _G_autoreconf_options " --force" + $opt_verbose && func_append _G_autoreconf_options " --verbose" + func_show_eval "$AUTORECONF$_G_autoreconf_options --install${1+ $1}" 'exit $?' + + AUTOPOINT=$save_AUTOPOINT + LIBTOOLIZE=$save_LIBTOOLIZE +} + + +# func_check_configuration VARNAME [CONFIGURE_MACRO] +# -------------------------------------------------- +# Exit with a suitable diagnostic for an important configuration change +# that needs to be made before bootstrap can run correctly. +func_check_configuration () +{ + $debug_cmd + + $require_configure_ac + + eval 'test -n "$'$1'"' || { + _G_error_msg="please set '$1' in 'bootstrap.conf'" + if test -n "$configure_ac" && test -n "$2"; then + func_append _G_error_msg " +or add the following (or similar) to your '$configure_ac': +$2" + fi + + func_fatal_error "$_G_error_msg" + } +} + + +# func_clean_dangling_symlinks +# ---------------------------- +# Remove any dangling symlink matching "*.m4" or "*.[ch]" in some +# gnulib-populated directories. Such .m4 files would cause aclocal to +# fail. The following requires GNU find 4.2.3 or newer. Considering +# the usual portability constraints of this script, that may seem a very +# demanding requirement, but it should be ok. Ignore any failure, +# which is fine, since this is only a convenience to help developers +# avoid the relatively unusual case where a symlinked-to .m4 file is +# git-removed from gnulib between successive runs of this script. +func_clean_dangling_symlinks () +{ + $debug_cmd + + $require_macro_dir + $require_source_base + + func_verbose "cleaning dangling symlinks" + + find "$macro_dir" "$source_base" \ + -depth \( -name '*.m4' -o -name '*.[ch]' \) \ + -type l -xtype l -delete > /dev/null 2>&1 +} + + +# func_clean_unused_macros +# ------------------------ +# Autopoint can result in over-zealously adding macros into $macro_dir +# even though they are not actually used, for example tests to help +# build the 'intl' directory even though you have specified +# 'AM_GNU_GETTEXT([external])' in your configure.ac. This function +# looks removes any macro files that can be found in gnulib, but +# are not 'm4_include'd by 'aclocal.m4'. +func_clean_unused_macros () +{ + $debug_cmd + + $require_gnulib_path + $require_macro_dir + + test -n "$gnulib_path" && test -f aclocal.m4 && { + aclocal_m4s=`find . -name aclocal.m4 -print` + + # We use 'ls|grep' instead of 'ls *.m4' to avoid exceeding + # command line length limits in some shells. + for file in `cd "$macro_dir" && ls -1 |$GREP '\.m4$'`; do + + # Remove a macro file when aclocal.m4 does not m4_include it... + func_grep_q 'm4_include([[]'$macro_dir/$file'])' $aclocal_m4s \ + || test ! -f "$gnulib_path/m4/$file" || { + + # ...and there is an identical file in gnulib... + if func_cmp_s "$gnulib_path/m4/$file" "$macro_dir/$file"; then + + # ...and it's not in the precious list ('echo' is needed + # here to squash whitespace for the match expression). + case " "`echo $gnulib_precious`" " in + *" $file "*) ;; + *) rm -f "$macro_dir/$file" + func_verbose \ + "removing unused gnulib file '$macro_dir/$file'" + esac + fi + } + done + } +} + + +# func_skip_po_recommendation +# --------------------------- +# If there is a po directory, and '--skip-po' wasn't passed, let the +# user know that they can use '--skip-po' on subsequent invocations. +func_skip_po_recommendation () +{ + $debug_cmd + + test ! -d po \ + || $opt_skip_po \ + || func_warning recommend "\ +If your pofiles are up-to-date, you can rerun bootstrap +as '$progname --skip-po' to avoid redownloading." +} + + +# func_update_dotversion +# ---------------------- +# Even with 'gitlog-to-changelog' generated ChangeLogs, automake +# will not run to completion with no ChangeLog file. +func_update_dotversion () +{ + $debug_cmd + + test -f "$build_aux/git-version-gen" && { + _G_message="updating .version" + test -f .version && { + mv .version .version~ + func_append _G_message " (backup in .version~)" + } + func_verbose "updating .version" + + $build_aux/git-version-gen dummy-arg > .version + } +} + + + +## -------------------- ## +## Resource management. ## +## -------------------- ## + +# This section contains definitions for functions that each ensure a +# particular resource (a file, or a non-empty configuration variable for +# example) is available, and if appropriate to extract default values +# from pertinent package files. Where a variable already has a non- +# empty value (as set by the package's 'bootstrap.conf'), that value is +# used in preference to deriving the default. Call them using their +# associated 'require_*' variable to ensure that they are executed, at +# most, once. + + +# require_checkout_only_file +# -------------------------- +# Bail out if this package only bootstraps properly from a repository +# checkout. +require_checkout_only_file=func_require_checkout_only_file +func_require_checkout_only_file () +{ + $debug_cmd + + $opt_force || { + test -n "$checkout_only_file" && test ! -f "$checkout_only_file" \ + && func_fatal_error "\ +Bootstrapping from a non-checked-out distribution is risky. +If you wish to bootstrap anyway, use the '--force' option." + } + + require_checkout_only_file=: +} + + +# require_aclocal_amflags +# ----------------------- +# Ensure '$aclocal_amflags' has a sensible default, extracted from +# 'Makefile.am' if necessary. +require_aclocal_amflags=func_require_aclocal_amflags +func_require_aclocal_amflags () +{ + $debug_cmd + + $require_makefile_am + + _G_sed_extract_aclocal_amflags='s|#.*$|| + /^[ ]*ACLOCAL_AMFLAGS[ ]*=/ { + s|^.*=[ ]*\(.*\)|aclocal_amflags="\1"| + p + }' + + _G_aclocal_flags_cmd=`$SED -n "$_G_sed_extract_aclocal_amflags" \ + "$makefile_am"` + eval "$_G_aclocal_flags_cmd" + + func_verbose "ACLOCAL_AMFLAGS='$aclocal_amflags'" + + require_aclocal_amflags=: +} + + +# require_autoheader +# ------------------ +# Skip autoheader if it's not needed. +require_autoheader=func_require_autoheader +func_require_autoheader () +{ + $debug_cmd + + test true = "$AUTOHEADER" || { + func_extract_trace AC_CONFIG_HEADERS + test -n "$func_extract_trace_result" \ + || func_extract_trace AC_CONFIG_HEADER + + test -n "$func_extract_trace_result" || { + AUTOHEADER=true + + func_verbose "export AUTOHEADER='$AUTOHEADER'" + + # Make sure the search result is visible to subshells + export AUTOHEADER + } + } + + require_autoheader=: +} + + +# require_automake_options +# ------------------------ +# Extract options from AM_AUTOMAKE_INIT. +require_automake_options=func_require_automake_options +func_require_automake_options () +{ + $debug_cmd + + func_extract_trace AM_INIT_AUTOMAKE + automake_options=$func_extract_trace_result + + require_automake_options=: +} + + +# require_autopoint +# ----------------- +# Skip autopoint if it's not needed. +require_autopoint=func_require_autopoint +func_require_autopoint () +{ + $debug_cmd + + test true = "$AUTOPOINT" || { + func_extract_trace AM_GNU_GETTEXT_VERSION + + test -n "$func_extract_trace_result" || { + AUTOPOINT=true + + func_verbose "export AUTOPOINT='$AUTOPOINT'" + + # Make sure the search result is visible to subshells + export AUTOPOINT + } + } + + require_autopoint=: +} + + +# require_bootstrap_uptodate +# -------------------------- +# Complain if the version of bootstrap in the gnulib directory differs +# from the one we are running. + +func_require_bootstrap_uptodate () +{ + $debug_cmd + + $require_build_aux + + _G_bootstrap_sources=" + $build_aux/bootstrap.in + $build_aux/extract-trace + $build_aux/funclib.sh + $build_aux/options-parser + " + + _G_missing_bootstrap_sources=false + for _G_src in $_G_bootstrap_sources; do + test -f "$_G_src" || _G_missing_bootstrap_sources=: + done + + if $_G_missing_bootstrap_sources; then + func_warning upgrade "\ +Please add bootstrap to your gnulib_modules list in +'bootstrap.conf', so that I can tell you when there are +updates available." + else + rm -f bootstrap.new + $build_aux/inline-source $build_aux/bootstrap.in > bootstrap.new + + if func_cmp_s "$progpath" bootstrap.new; then + rm -f bootstrap.new + func_verbose "bootstrap script up to date" + else + chmod 555 bootstrap.new + func_warning upgrade "\ +An updated bootstrap script has been generated for you in +'bootstrap.new'. After you've verified that you want +the changes, you can update with: + mv -f bootstrap.new $progname + ./$progname + +Or you can disable this check permanently by adding the +following to 'bootstrap.conf': + require_bootstrap_uptodate=:" + fi + fi + + require_bootstrap_uptodate=: +} + + +# require_build_aux +# ----------------- +# Ensure that '$build_aux' is set, and if it doesn't already point to an +# existing directory, create one. +require_build_aux=func_require_build_aux +func_require_build_aux () +{ + $debug_cmd + + test -n "$build_aux" || { + func_extract_trace_first AC_CONFIG_AUX_DIR + build_aux=$func_extract_trace_first_result + func_check_configuration build_aux \ + "AC_CONFIG_AUX_DIR([name of a directory for build scripts])" + + func_verbose "build_aux='$build_aux'" + } + + $require_vc_ignore_files + + # If the build_aux directory doesn't exist, create it now, and mark it + # as ignored for the VCS. + if test ! -d "$build_aux"; then + func_show_eval "mkdir '$build_aux'" + + test -n "$vc_ignore_files" \ + || func_insert_if_absent "$build_aux" $vc_ignore_files + fi + + require_build_aux=: +} + + +# require_buildreq_autobuild +# -------------------------- +# Try to find whether the bootstrap requires autobuild. +require_buildreq_autobuild=func_require_buildreq_autobuild +func_require_buildreq_autobuild () +{ + $debug_cmd + + $require_macro_dir + + test -f "$macro_dir/autobuild.m4" \ + || printf '%s\n' "$buildreq" |func_grep_q '^[ ]*autobuild' \ + || { + func_extract_trace AB_INIT + test -n "$func_extract_trace_result" && { + func_append buildreq 'autobuild - http://josefsson.org/autobuild/ +' + func_verbose "auto-adding 'autobuild' to build requirements" + } + } + + require_buildreq_autobuild=: +} + + +# require_buildreq_autoconf +# require_buildreq_autopoint +# require_buildreq_libtoolize +# --------------------------- +# Try to find the minimum compatible version of autoconf/libtool +# required to bootstrap successfully, and add it to '$buildreq'. +for tool in autoconf libtoolize autopoint; do + b=$tool + v=require_buildreq_${tool} + f=func_$v + case $tool in + autoconf) m=AC_PREREQ ;; + libtoolize) m=LT_PREREQ; b=libtool ;; + autopoint) m=AM_GNU_GETTEXT_VERSION b=gettext ;; + esac + + eval $v'='$f' + '$f' () + { + $debug_cmd + + # The following is ignored if undefined, but might be necessary + # in order for `func_find_tool` to run. + ${require_'$tool'-:} + + printf '\''%s\n'\'' "$buildreq" |func_grep_q '\''^[ ]*'$tool\'' || { + func_extract_trace '$m' + _G_version=$func_extract_trace_result + test -n "$_G_version" && { + func_append buildreq "\ + '$tool' $_G_version http://www.gnu.org/s/'$b' +" + func_verbose \ + "auto-adding '\'$tool'-$_G_version'\'' to build requirements" + } + } + + '$v'=: + } +' +done + + +# require_buildreq_automake +# ------------------------- +# Try to find the minimum compatible version of automake required to +# bootstrap successfully, and add it to '$buildreq'. +require_buildreq_automake=func_require_buildreq_automake +func_require_buildreq_automake () +{ + $debug_cmd + + # if automake is not already listed in $buildreq... + printf '%s\n' "$buildreq" |func_grep_q automake || { + func_extract_trace AM_INIT_AUTOMAKE + + # ...and AM_INIT_AUTOMAKE is declared... + test -n "$func_extract_trace_result" && { + automake_version=`$ECHO "$func_extract_trace_result" \ + |$SED -e 's|[^0-9]*||' -e 's| .*$||'` + test -n "$automake_version" || automake_version=- + + func_append buildreq "\ + automake $automake_version http://www.gnu.org/s/automake +" + func_verbose \ + "auto-adding 'automake-$automake_version' to build requirements" + } + } + + require_buildreq_automake=: +} + + +# require_buildreq_patch +# ---------------------- +# Automatically add a patch build-requirement if there are diff files +# in $local_gl_dir. +require_buildreq_patch=func_require_buildreq_patch +func_require_buildreq_patch () +{ + $debug_cmd + + $require_local_gl_dir + + # This ensures PATCH is set appropriately by the time + # func_check_versions enforces $buildreq. + $require_patch + + # If patch is not already listed in $buildreq... + printf '%s\n' "$buildreq" |func_grep_q '^[ ]*patch' || { + # The ugly find invocation is necessary to exit with non-zero + # status for old find binaries that don't support -exec fully. + if test ! -d "$local_gl_dir" \ + || find "$local_gl_dir" -name "*.diff" -exec false {} \; ; then : + else + func_append buildreq 'patch - http://www.gnu.org/s/patch +' + fi + } + + require_buildreq_patch=: +} + + +# require_buildtools_uptodate +# --------------------------- +# Ensure all the packages listed in BUILDREQS are available on the build +# machine at the minimum versions or better. +require_buildtools_uptodate=func_require_buildtools_uptodate +func_require_buildtools_uptodate () +{ + $debug_cmd + + $require_buildreq_autobuild + $require_buildreq_autoconf + $require_buildreq_automake + $require_buildreq_libtoolize + $require_buildreq_autopoint + $require_buildreq_patch + + test -n "$buildreq" && { + _G_error_hdr= + + func_check_versions $buildreq + $func_check_versions_result || { + test -n "$buildreq_readme" \ + && test -f "$buildreq_readme" \ + && _G_error_hdr="\ +$buildreq_readme explains how to obtain these prerequisite programs: +" + func_strtable 0 11 12 36 \ + "Program" "Min_version" "Homepage" $buildreq + func_fatal_error "$_G_error_hdr$func_strtable_result" + } + } + + require_buildtools_uptodate=: +} + + +# require_copyright_holder +# ------------------------ +# Ensure there is a sensible non-empty default value in '$copyright_holder'. +require_copyright_holder=func_require_copyright_holder +func_require_copyright_holder () +{ + $debug_cmd + + test -n "$copyright_holder" || { + copyright_holder='Free Software Foundation, Inc.' + func_warning settings "\ +Please set copyright_holder explicitly in 'bootstrap.conf'; +defaulting to '$copyright_holder'." + } + + require_copyright_holder=: +} + + +# require_doc_base +# ---------------- +# Ensure doc_base has a sensible value, extracted from 'gnulib-cache.m4' +# if possible, otherwise letting 'gnulib-tool' pick a default. +require_doc_base=func_require_doc_base +func_require_doc_base () +{ + $debug_cmd + + $require_gnulib_cache + + test -f "$gnulib_cache" && test -z "$doc_base" && { + func_extract_trace_first "gl_DOC_BASE" "$gnulib_cache" + doc_base=$func_extract_trace_first_result + + test -n "$doc_base" && func_verbose "doc_base='$doc_base'" + } + + require_doc_base=: +} + + +# require_dotgitmodules +# --------------------- +# Ensure we have a '.gitmodules' file, with appropriate 'gnulib' settings. +require_dotgitmodules=func_require_dotgitmodules +func_require_dotgitmodules () +{ + $debug_cmd + + $require_git + + test true = "$GIT" || { + # A gnulib entry in .gitmodules always takes precedence. + _G_path=`$GIT config --file .gitmodules submodule.gnulib.path 2>/dev/null` + + test -n "$_G_path" || { + $require_vc_ignore_files + + func_verbose "creating '.gitmodules'" + + # If the .gitmodules file doesn't exist, create it now, and mark + # it as ignored for the VCS. + test -n "$gnulib_path" || gnulib_path=gnulib + test -n "$gnulib_url" || gnulib_url=git://git.sv.gnu.org/gnulib + + { + echo '[submodule "gnulib"]' + echo " path = $gnulib_path" + echo " url = $gnulib_url" + } >> .gitmodules + + test -n "$vc_ignore_files" \ + || func_insert_if_absent ".gitmodules" $vc_ignore_files + } + } + + require_dotgitmodules=: +} + + +# require_extra_locale_categories +# ------------------------------- +# Ensure there is a default value in '$extra_locale_categories' +require_extra_locale_categories=func_require_extra_locale_categories +func_require_extra_locale_categories () +{ + $debug_cmd + + # Defaults to empty, so run with whatever value may have been set in + # 'bootstrap.conf'. + require_extra_locale_categories=: +} + + +# require_git +# ----------- +# Ignore git if it's not available, or we're not in a git checkout tree. +require_git=func_require_git +func_require_git () +{ + $debug_cmd + + $opt_skip_git && GIT=true + + test true = "$GIT" || { + if test -d .git/.; then + ($GIT --version) >/dev/null 2>&1 || GIT=true + fi + } + + func_verbose "GIT='$GIT'" + + require_git=: +} + + +# require_gnulib_cache +# -------------------- +# Ensure there is a non-empty default for '$gnulib_cache', and that it +# names an existing file. +require_gnulib_cache=func_require_gnulib_cache +func_require_gnulib_cache () +{ + $debug_cmd + + $require_macro_dir + + test -n "$gnulib_cache" \ + || gnulib_cache=$macro_dir/gnulib-cache.m4 + + func_verbose "found '$gnulib_cache'" + + require_gnulib_cache=: +} + + +# require_gnulib_copy_cmd +# ----------------------- +# Only calculate the options for copying files with gnulib once. +require_gnulib_copy_cmd=func_require_gnulib_copy_cmd +func_require_gnulib_copy_cmd () +{ + $debug_cmd + + $require_gnulib_tool + $require_gnulib_tool_base_options + + gnulib_copy_cmd="$gnulib_tool $gnulib_tool_base_options --copy-file" + $opt_copy || func_append gnulib_copy_cmd " --symlink" + $opt_quiet || func_append gnulib_copy_cmd " --verbose" + + require_gnulib_copy_cmd=: +} + + +# require_gnulib_merge_changelog +# ------------------------------ +# See if we can use gnulib's git-merge-changelog merge driver. +require_gnulib_merge_changelog=func_require_gnulib_merge_changelog +func_require_gnulib_merge_changelog () +{ + $debug_cmd + + test -f ChangeLog && { + $require_git + + func_grep_q '^\(/\|\)ChangeLog$' .gitignore || test true = "$GIT" || { + if $GIT config merge.merge-changelog.driver >/dev/null; then + : + elif (git-merge-changelog --version) >/dev/null 2>&1; then + func_echo "initializing git-merge-changelog driver" + $GIT config merge.merge-changelog.name 'GNU-style ChangeLog merge driver' + $GIT config merge.merge-changelog.driver 'git-merge-changelog %O %A %B' + else + func_warning recommend \ + "Consider installing git-merge-changelog from gnulib." + fi + } + } + + require_gnulib_merge_changelog=: +} + + +# require_gnulib_mk +# ----------------- +# Ensure gnulib_mk has a sensible value, extracted from 'gnulib-cache.m4' +# if possible, otherwise letting 'gnulib-tool' pick a default. +require_gnulib_mk=func_require_gnulib_mk +func_require_gnulib_mk () +{ + $debug_cmd + + $require_gnulib_cache + + test -f "$gnulib_cache" && test -z "$gnulib_mk" && { + func_extract_trace_first "gl_MAKEFILE_NAME" "$gnulib_cache" + gnulib_mk=$func_extract_trace_first_result + + test -n "$gnulib_mk" && func_verbose "gnulib_mk='$gnulib_mk'" + } + + require_gnulib_mk=: +} + + +# require_gnulib_name +# ------------------- +# Ensure gnulib_name has a sensible value, extracted from 'gnulib-cache.m4' +# if possible, otherwise letting 'gnulib-tool' pick a default. +require_gnulib_name=func_require_gnulib_name +func_require_gnulib_name () +{ + $debug_cmd + + $require_gnulib_cache + + test -f "$gnulib_cache" && test -z "$gnulib_name" && { + func_extract_trace_first "gl_LIB" "$gnulib_cache" + gnulib_name=$func_extract_trace_first_result + + test -n "$gnulib_name" && func_verbose "gnulib_name='$gnulib_name'" + } + + require_gnulib_name=: +} + + +# require_gnulib_path +# require_gnulib_url +# ------------------- +# Ensure 'gnulib_path' and 'gnulib_url' are set. +require_gnulib_path=func_require_dotgitmodules_parameters +require_gnulib_url=func_require_dotgitmodules_parameters +func_require_dotgitmodules_parameters () +{ + $debug_cmd + + $require_git + + test true = "$GIT" && { + # If we can't find git (or if the user specified '--skip-git'), + # then use an existing gnulib directory specified with + # '--gnulib-srcdir' if possible. + test -n "$gnulib_path" \ + || test ! -x "$opt_gnulib_srcdir/gnulib-tool" \ + || gnulib_path=$opt_gnulib_srcdir + } + + + $require_dotgitmodules + + test -f .gitmodules && { + # Extract the parameters with sed, since git may be missing + test -n "$gnulib_path" \ + || gnulib_path=`$SED -e '/^.submodule "gnulib".$/,${ + /[ ]*path *= */{ + s|[ ]*||g;s|^[^=]*=||;p + } + } + d' .gitmodules |$SED 1q` + test -n "$gnulib_url" \ + || gnulib_url=`$SED -e '/^.submodule "gnulib".$/,${ + /[ ]*url *= */{ + s|[ ]*||g;s|^[^=]*=||;p + } + } + d' .gitmodules |$SED 1q` + + func_verbose "gnulib_path='$gnulib_path'" + func_verbose "gnulib_url='$gnulib_url'" + } + + require_gnulib_path=: + require_gnulib_url=: +} + + +# require_gnulib_submodule +# ------------------------ +# Ensure that there is a current gnulib submodule at '$gnulib_path'. +require_gnulib_submodule=func_require_gnulib_submodule +func_require_gnulib_submodule () +{ + $debug_cmd + + $require_git + + if test true = "$GIT"; then + func_warning recommend \ + "No 'git' found; imported gnulib modules may be outdated." + else + $require_gnulib_path + $require_gnulib_url + + if test -f .gitmodules && test -f "$gnulib_path/gnulib-tool"; then + : All present and correct. + + elif test -n "$opt_gnulib_srcdir"; then + # Older git can't clone into an empty directory. + rmdir "$gnulib_path" 2>/dev/null + func_show_eval "$GIT clone --reference '$opt_gnulib_srcdir' \ + '$gnulib_url' '$gnulib_path'" \ + || func_fatal_error "Unable to fetch gnulib submodule." + + # Without --gnulib-srcdir, and no existing checked out submodule, we + # create a new shallow clone of the remote gnulib repository. + else + trap func_cleanup_gnulib 1 2 13 15 + + shallow= + $GIT clone -h 2>&1 |func_grep_q -- --depth \ + && shallow='--depth 365' + + func_show_eval "$GIT clone $shallow '$gnulib_url' '$gnulib_path'" \ + func_cleanup_gnulib + + # FIXME: Solaris /bin/sh will try to execute '-' if any of + # these signals are caught after this. + trap - 1 2 13 15 + fi + + # Make sure we've checked out the correct revision of gnulib. + func_show_eval "$GIT submodule init -- $gnulib_path" \ + && func_show_eval "$GIT submodule update -- $gnulib_path" \ + || func_fatal_error "Unable to update gnulib submodule." + fi + + require_gnulib_submodule=: +} + + +# require_gnulib_tool +# ------------------- +# Ensure that '$gnulib_tool' is set, and points to an executable file, +# or else fall back to using the binary 'true' if the main gnulib +# files appear to have been imported already. +require_gnulib_tool=func_require_gnulib_tool +func_require_gnulib_tool () +{ + $debug_cmd + + test true = "$gnulib_tool" || { + $require_gnulib_submodule + $require_gnulib_path + + test -n "$gnulib_tool" \ + || gnulib_tool=$gnulib_path/gnulib-tool + + test -x "$gnulib_tool" || { + gnulib_tool=true + func_warning recommend \ + "No 'gnulib-tool' found; gnulib modules may be missing." + } + + test true = "$gnulib_tool" \ + || func_verbose "found '$gnulib_tool'" + } + + require_gnulib_tool=: +} + + +# require_gnulib_tool_base_options +# -------------------------------- +# Ensure that '$gnulib_tool_base_options' contains all the base options +# required according to user configuration from bootstrap.conf. +require_gnulib_tool_base_options=func_require_gnulib_tool_base_options +func_require_gnulib_tool_base_options () +{ + $debug_cmd + + $require_gnulib_tool + + gnulib_tool_base_options= + + test true = "$gnulib_tool" || { + # 'gnulib_modules' and others are maintained in 'bootstrap.conf': + # Use 'gnulib --import' to fetch gnulib modules. + $require_build_aux + test -n "$build_aux" \ + && func_append_uniq gnulib_tool_base_options " --aux-dir=$build_aux" + $require_macro_dir + test -n "$macro_dir" \ + && func_append_uniq gnulib_tool_base_options " --m4-base=$macro_dir" + $require_doc_base + test -n "$doc_base" \ + && func_append_uniq gnulib_tool_base_options " --doc-base=$doc_base" + $require_gnulib_name + test -n "$gnulib_name" \ + && func_append_uniq gnulib_tool_base_options " --lib=$gnulib_name" + $require_local_gl_dir + test -n "$local_gl_dir" \ + && func_append_uniq gnulib_tool_base_options " --local-dir=$local_gl_dir" + $require_source_base + test -n "$source_base" \ + && func_append_uniq gnulib_tool_base_options " --source-base=$source_base" + } + + require_gnulib_tool_base_options=: +} + + +# require_libtoolize +# ------------------ +# Skip libtoolize if it's not needed. +require_libtoolize=func_require_libtoolize +func_require_libtoolize () +{ + $debug_cmd + + # Unless we're not searching for libtool use by this package, set + # LIBTOOLIZE to true if none of 'LT_INIT', 'AC_PROG_LIBTOOL' and + # 'AM_PROG_LIBTOOL' are used in configure. + test true = "$LIBTOOLIZE" || { + func_extract_trace LT_INIT + test -n "$func_extract_trace_result" || func_extract_trace AC_PROG_LIBTOOL + test -n "$func_extract_trace_result" || func_extract_trace AM_PROG_LIBTOOL + test -n "$func_extract_trace_result" || LIBTOOLIZE=true + } + + test -n "$LIBTOOLIZE" || { + # Find libtoolize, named glibtoolize in Mac Ports, but prefer + # user-installed libtoolize to ancient glibtoolize shipped by + # Apple with Mac OS X when Mac Ports is not installed. + func_find_tool LIBTOOLIZE libtoolize glibtoolize + } + + test -n "$LIBTOOLIZE" || func_fatal_error "\ +Please install GNU Libtool, or 'export LIBTOOLIZE=/path/to/libtoolize'." + + func_verbose "export LIBTOOLIZE='$LIBTOOLIZE'" + + # Make sure the search result is visible to subshells + export LIBTOOLIZE + + require_libtoolize=: +} + + +# require_local_gl_dir +# -------------------- +# Ensure local_gl_dir has a sensible value, extracted from 'gnulib-cache.m4' +# if possible, otherwise letting 'gnulib-tool' pick a default. +require_local_gl_dir=func_require_local_gl_dir +func_require_local_gl_dir () +{ + $debug_cmd + + $require_gnulib_cache + + test -f "$gnulib_cache" && test -z "$local_gl_dir" && { + func_extract_trace_first "gl_LOCAL_DIR" "$gnulib_cache" + local_gl_dir=$func_extract_trace_first_result + + test -n "$local_gl_dir" && func_verbose "local_gl_dir='$local_gl_dir'" + } + + require_local_gl_dir=: +} + + +# require_macro_dir +# ----------------- +# Ensure that '$macro_dir' is set, and if it doesn't already point to an +# existing directory, create one. +require_macro_dir=func_require_macro_dir +func_require_macro_dir () +{ + $debug_cmd + + # Sometimes this is stored in 'configure.ac'. + test -n "$macro_dir" || { + # AC_CONFIG_MACRO_DIRS takes a space delimited list of directories, + # but we only care about the first one in bootstrap. + func_extract_trace_first AC_CONFIG_MACRO_DIRS + macro_dir=`expr "x$func_extract_trace_first_result" : 'x\([^ ]*\)'` + } + test -n "$macro_dir" || { + func_extract_trace_first AC_CONFIG_MACRO_DIR + macro_dir=$func_extract_trace_first_result + } + + # Otherwise we might find it in 'Makefile.am'. + test -n "$macro_dir" || { + $require_aclocal_amflags + + # Take the argument following the first '-I', if any. + _G_minus_I_seen=false + for _G_arg in $aclocal_amflags; do + case $_G_minus_I_seen,$_G_arg in + :,*) macro_dir=$_G_arg; break ;; + *,-I) _G_minus_I_seen=: ;; + *,-I*) macro_dir=`expr x$_G_arg : 'x-I\(.*\)$'`; break ;; + esac + done + } + + func_verbose "macro_dir='$macro_dir'" + + func_check_configuration macro_dir \ + "AC_CONFIG_MACRO_DIRS([name of a directory for configure m4 files])" + + $require_vc_ignore_files + + # If the macro_dir directory doesn't exist, create it now, and mark it + # as ignored for the VCS. + if test ! -d "$macro_dir"; then + mkdir "$macro_dir" || func_permissions_error "$macro_dir" + + test -n "$vc_ignore_files" \ + || func_insert_if_absent "$macro_dir" $vc_ignore_files + fi + + require_macro_dir=: +} + + +# require_makefile_am +# ------------------- +# Ensure there is a 'Makefile.am' in the current directory. +require_makefile_am=func_require_makefile_am +func_require_makefile_am () +{ + $debug_cmd + + test -n "$makefile_am" \ + || makefile_am=Makefile.am + + <"$makefile_am" + + func_verbose "found '$makefile_am'" + + require_makefile_am=: +} + + +# require_package +# --------------- +# Ensure that '$package' contains a sensible default value. +require_package=func_require_package +func_require_package () +{ + $debug_cmd + + test -n "$package" || { + $require_package_name + + package=`echo "$package_name" \ + |$SED -e 's/GNU //' \ + -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` + } + + func_verbose "package='$package'" + + require_package=: +} + + +# require_package_bugreport +# ------------------------- +# Ensure that this has a sensible value, extracted from 'configure.ac' +# if appropriate (and possible!). +require_package_bugreport=func_require_package_bugreport +func_require_package_bugreport () +{ + $debug_cmd + + func_extract_trace AC_INIT + + save_ifs=$IFS + IFS=: + set dummy $func_extract_trace_result + IFS=$save_ifs + shift + + test -n "$package_bugreport" || package_bugreport=$3 + func_check_configuration package_bugreport \ + "AC_INIT([$package_name], [$package_version], [bug-$package@gnu.org])" + func_verbose "package_bugreport='$package_bugreport'" + + require_package_bugreport=: +} + + +# require_package_name +# -------------------- +# Ensure that this has a sensible value, extracted from 'configure.ac' +# if appropriate (and possible!). +require_package_name=func_require_package_name +func_require_package_name () +{ + $debug_cmd + + func_extract_trace AC_INIT + + save_ifs=$IFS + IFS=: + set dummy $func_extract_trace_result + IFS=$save_ifs + shift + + test -n "$package_name" || package_name=$1 + func_check_configuration package_name \ + "AC_INIT([name of your package], [package version number])" + func_verbose "package_name='$package_name'" + + require_package_name=: +} + + +# require_package_version +# ----------------------- +# Ensure that this has a sensible value, extracted from 'configure.ac' +# if appropriate (and possible!). While we might have set all the +# parameters extracted from AC_INIT at once, 'package_version' in +# particular is not necessarily available as early as the others, since +# 'git-version-gen' is often involved, and until then we can't rely on +# getting a correct version number from an AC_INIT extraction. +require_package_version=func_require_package_version +func_require_package_version () +{ + $debug_cmd + + func_extract_trace AC_INIT + + save_ifs=$IFS + IFS=: + set dummy $func_extract_trace_result + IFS=$save_ifs + shift + + test -n "$package_version" || package_version=$2 + test -n "$package_version" || { + # The embedded echo is to squash whitespace before globbing. + case " "`echo $gnulib_modules`" " in + *" git-version-gen "*) + func_fatal_error "\ +cannot \$require_package_version in bootstrap.conf before +func_gnulib_tool has installed the 'git-version-gen' script." + ;; + *) + func_check_configuration package_version \ + "AC_INIT([name of your package], [package version number])" + ;; + esac + } + func_verbose "package_version='$package_version'" + + require_package_version=: +} + + +# require_patch +# ------------- +# Find patch, according to the PATCH environment variable, or else +# searching the user's PATH. +require_patch=func_require_patch +func_require_patch () +{ + $debug_cmd + + test -n "$PATCH" || { + # Find a patch program, preferring gpatch, which is usually better + # than the vendor patch. + func_find_tool PATCH gpatch patch + } + + test -n "$PATCH" || func_fatal_error "\ +Please install GNU Patch, or 'export PATCH=/path/to/gnu/patch'." + + func_verbose "export PATCH='$PATCH'" + + # Make sure the search result is visible to subshells + export PATCH + + require_patch=: +} + + +# require_source_base +# ------------------- +# Ensure that source_base has a sensible value, extracted from +# 'gnulib-cache.m4' if possible. +require_source_base=func_require_source_base +func_require_source_base () +{ + $debug_cmd + + $require_gnulib_cache + + test -f "$gnulib_cache" && test -z "$source_base" && { + func_extract_trace_first "gl_SOURCE_BASE" "$gnulib_cache" + + source_base=$func_extract_trace_first_result + + func_verbose "source_base='$source_base'" + } + + require_source_base=: +} + + +# require_vc_ignore_files +# ----------------------- +# Ensure that '$vc_ignore' has been processed to list VCS ignore files +# in '$vc_ignore_files' +require_vc_ignore_files=func_require_vc_ignore_files +func_require_vc_ignore_files () +{ + $debug_cmd + + test -n "$vc_ignore" || vc_ignore=auto + + if test auto = "$vc_ignore" && test -z "$vc_ignore_files"; then + vc_ignore_files= + test -d .git && vc_ignore_files=.gitignore + test -d CVS && vc_ignore_files="$vc_ignore_files .cvsignore" + else + vc_ignore_files=$vc_ignore + fi + + func_verbose "vc_ignore_files='$vc_ignore_files'" + + require_vc_ignore_files=: +} + + +## ----------------- ## +## Helper functions. ## +## ----------------- ## + +# This section contains the helper functions used by the rest of 'bootstrap'. + +# func_len STRING +# --------------- +# STRING may not start with a hyphen. +if (eval 'x=123; test x${#x} = "x3"') 2>/dev/null +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_len () + { + $debug_cmd + + func_len_result=${#1} + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_len () + { + $debug_cmd + + func_len_result=`expr "$1" : ".*" 2>/dev/null || echo 0` + } +fi + + +# func_unset VAR +# -------------- +# Portably unset VAR. +# In some shells, an 'unset VAR' statement leaves a non-zero return +# status if VAR is already unset, which might be problematic if the +# statement is used at the end of a function (thus poisoning its return +# value) or when 'set -e' is active (causing even a spurious abort of +# the script in this case). +func_unset () +{ + { eval $1=; unset $1; } +} +unset=func_unset + + +# func_cmp_s FILE1 FILE2 +# ---------------------- +# Return non-zero exit status unless FILE1 and FILE2 are identical, without +# any output at all, even error messages. +func_cmp_s () +{ + $debug_cmd + + # This function relies on non-zero exit status, which will cause the + # program to exit when running in 'set -e' mode. + $CMP "$@" >/dev/null 2>&1 +} + + +# func_grep_q EXPRESSION [FILENAME..] +# ----------------------------------- +# Check whether EXPRESSION matches any line of any listed FILENAME, +# without any output at all, even error messages. +func_grep_q () +{ + $debug_cmd + + # This function relies on non-zero exit status, which will cause the + # program to exit when running in 'set -e' mode. + $GREP "$@" >/dev/null 2>&1 +} + + +# func_ifcontains LIST MEMBER YES-CMD [NO-CMD] +# -------------------------------------------- +# If whitespace-separated LIST contains MEMBER then execute YES-CMD, +# otherwise if NO-CMD was given, execute that. +func_ifcontains () +{ + $debug_cmd + + _G_wslist=$1 + _G_member=$2 + _G_yes_cmd=$3 + _G_no_cmd=${4-":"} + + _G_found=false + for _G_item in $_G_wslist; do + test "x$_G_item" = "x$_G_member" && { + _G_found=: + break + } + done + if $_G_found; then + eval "$_G_yes_cmd" + _G_status=$? + else + eval "$_G_no_cmd" + _G_status=$? + fi + + test 0 -eq "$_G_status" || exit $_G_status +} + + +# func_strpad STR WIDTH CHAR +# -------------------------- +# Trim STR, or pad with CHAR to force a total length of WIDTH. +func_strpad () +{ + $debug_cmd + + _G_width=`expr "$2" - 1` + func_strpad_result=`$ECHO "$1" |$SED ' + :a + s|^.\{0,'"$_G_width"'\}$|&'"$3"'| + ta + '` +} + + +# func_strrpad STR WIDTH CHAR +# --------------------------- +# Trim STR, or right-justify-pad with CHAR to force a total length of +# WIDTH. +func_strrpad () +{ + $debug_cmd + + _G_width=`expr "$2" - 1` + func_strrpad_result=`$ECHO "$1" |$SED ' + :a + s|^.\{0,'"$_G_width"'\}$|'"$3"'&| + ta + '` +} + + +# func_strrow INDENT FIELD WIDTH [FIELDn WIDTHn]... +# ------------------------------------------------- +# Return a string containing each FIELD left justified to WIDTH, with +# the whole thing indented by INDENT spaces. This function is used to +# render one row of aligned columns for a table by func_strtable(). +func_strrow () +{ + $debug_cmd + + func_strrow_linelen=$1; shift + + _G_row= + while test $# -gt 0; do + func_strrow_linelen=`expr $func_strrow_linelen + $2` + func_strpad "$1" $2 " " + func_append _G_row "$func_strpad_result" + shift; shift + done + + func_strrpad "$_G_row" $func_strrow_linelen " " + func_strrow_result=$func_strrpad_result +} + + +# func_strtable INDENT WIDTH1...WIDTHn HEADER1...HEADERn FIELD1...FIELDn +# ---------------------------------------------------------------------- +# Generate a string of newline-separated rows arranged in lined-up +# columns of the given WIDTHs, with the entire table indented by INDENT +# spaces. The number of columns is determined by the number of integer +# valued WIDTH arguments following INDENT. The next set (i.e. a number +# of arguments equal to the number of WIDTH arguments) of fields are +# treated as the table's column HEADERs, and are separated from the +# remainder of the table by an indented row of '-' characters. Remaining +# arguments are each aligned below the next available header, wrapping +# to a new row as necessary. Finally another row of '-' characters is +# added to mark the end of the table. +# +# For example an unindented 3 column table with 2 rows of data would be +# generated by this call: +# +# func_strtable 3 20 10 25 \ +# Header1 Header2 Header3 \ +# Row1Col1 Row1Col2 Row1Col3 \ +# Row2Col1 Row2Col2 Row2Col3 +# +# returning the following string: +# +# " Header1 Header2 Header3 +# ------------------------------------------------------- +# Row1Col1 Row1Col2 Row1Col3 +# Row2Col1 Row2Col2 Row2Col3 +# -------------------------------------------------------" +func_strtable () +{ + $debug_cmd + + # Save the indent value, we'll need it for each row we render. + _G_indent=$1; shift + + # Collect remaining numeric args into a list for reuse between + # members of each row when we call func_strrow later. + _G_widths=$1; shift + while test 0 -lt `expr "$1" : '[1-9][0-9]*$'`; do + func_append _G_widths " $1"; shift + done + + # Extract the same number of positional parameters as there are + # width elements - we'll do the header rows separately so that + # we can insert a divider line. + _G_header=$_G_indent + for _G_width in $_G_widths; do + func_append _G_header " $1 $_G_width"; shift + done + func_strrow $_G_header + + # Strip off the indent, and make a divider with '-' chars, then + # reindent. + _G_divider=`$ECHO "$func_strrow_result" \ + |$SED 's|[^ ]|-|g + :a + s|- |--|g + ta + '` + + # Append the header and divider to the running result. + func_append func_strtable_result "\ +$func_strrow_result +$_G_divider +" + + # The remaining rows are zipped between the width values we + # unwound earlier just like the header row above. + while test $# -gt 0; do + _G_row=$_G_indent + for _G_width in $_G_widths; do + func_append _G_row " $1 $_G_width"; shift + done + func_strrow $_G_row + func_append func_strtable_result "\ +$func_strrow_result +" + done + + # Mark the end of the table with a final divider line. + func_append func_strtable_result "$_G_divider" +} + + +# func_internal_error ARG... +# -------------------------- +# Echo program name prefixed message to standard error, and exit. +func_internal_error () +{ + func_fatal_error "\ +INTERNAL: " ${1+"$@"} " + Please report this bug to 'bug-gnulib@gnu.org' + in as much detail as possible." +} + + +# func_permissions_error FILE-OR-DIRECTORY +# ---------------------------------------- +# Echo program name prefixed permissions error message to standard +# error, and exit. +func_permissions_error () +{ + $debug_cmd + + func_fatal_error "Failed to create '$1', check permissions." +} + + +# func_show_eval CMD [FAIL_EXP] +# ----------------------------- +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + $debug_cmd + + $require_term_colors + + _G_cmd=$1 + _G_fail_exp=${2-':'} + + ${opt_silent-'false'} || { + func_quote_for_eval $_G_cmd + eval func_truncate_cmd $func_quote_for_eval_result + func_echo "running: $tc_bold$func_truncate_cmd_result$tc_reset" + } + + ${opt_dry_run-'false'} || { + eval "$_G_cmd" + _G_status=$? + test 0 -eq "$_G_status" || eval "(exit $_G_status); $_G_fail_exp" + } +} + + +# func_truncate_cmd CMD [ARG]... +# ------------------------------ +# For unreasonably long commands (such as a gnulib-tool invocation with +# the full module list for import), truncate CMD after the second non- +# option ARG. +func_truncate_cmd () +{ + $debug_cmd + + _G_last_arg_opt_p=false + func_truncate_cmd_result= + + set dummy "$@"; shift + + while test $# -gt 0; do + _G_opt=$1; shift + + test -n "$func_truncate_cmd_result" \ + && func_append func_truncate_cmd_result ' ' + func_append func_truncate_cmd_result "$_G_opt" + + func_len "x$func_truncate_cmd_result" + + case $_G_opt in + -*) _G_last_arg_opt_p=: ;; + *) $_G_last_arg_opt_p \ + || test "$min_cmd_len" -gt "$func_len_result" \ + || break + _G_last_arg_opt_p=false + ;; + esac + done + + test $# -gt 0 && func_append func_truncate_cmd_result "..." +} + + +# func_gitignore_entries FILE... +# ------------------------------ +# Strip blank and comment lines to leave significant entries. +func_gitignore_entries () +{ + $debug_cmd + + $SED -e '/^#/d' -e '/^$/d' "$@" +} + + +# func_insert_if_absent STR FILE... +# --------------------------------- +# If $STR is not already on a line by itself in $FILE, insert it, at the +# start. Entries are inserted at the start of the ignore list to ensure +# existing entries starting with ! are not overridden. Such entries +# support whilelisting exceptions after a more generic blacklist pattern. +# sorting the new contents of the file and replacing $FILE with the result. +func_insert_if_absent () +{ + $debug_cmd + + str=$1 + shift + + for file + do + test -f "$file" || touch "$file" + + duplicate_entries=`func_gitignore_entries "$file" |sort |uniq -d` + test -n "$duplicate_entries" \ + && func_error "duplicate entries in $file: " $duplicate_entries + + func_grep_q "^$str\$" "$file" \ + || func_verbose "inserting '$str' into '$file'" + + linesold=`func_gitignore_entries "$file" |wc -l` + linesnew=`{ $ECHO "$str"; cat "$file"; } \ + |func_gitignore_entries |sort -u |wc -l` + test "$linesold" -eq "$linesnew" \ + || { $SED "1i\\$nl$str$nl" "$file" >"$file"T && mv "$file"T "$file"; } \ + || func_permissions_error "$file" + done +} + + +# func_get_version APP +# -------------------- +# echo the version number (if any) of APP, which is looked up along your +# PATH. +func_get_version () +{ + $debug_cmd + + _G_app=$1 + + # Rather than uncomment the sed script in-situ, strip the comments + # programatically before passing the result to $SED for evaluation. + sed_get_version=`$ECHO '# extract version within line + s|.*[v ]\{1,\}\([0-9]\{1,\}\.[.a-z0-9-]*\).*|\1| + t done + + # extract version at start of line + s|^\([0-9]\{1,\}\.[.a-z0-9-]*\).*|\1| + t done + + d + + :done + # the following essentially does s|5.005|5.5| + s|\.0*\([1-9]\)|.\1|g + p + q' \ + |$SED '/^[ ]*#.*$/d'` + + func_tool_version_output $_G_app >/dev/null + _G_status=$? + + test 0 -ne "$_G_status" \ + || $_G_app --version 2>&1 |$SED -n "$sed_get_version" + + (exit $_G_status) +} + + +# func_check_tool APP +# ------------------- +# Search PATH for an executable at APP. +func_check_tool () +{ + $debug_cmd + + func_check_tool_result= + + case $1 in + *[\\/]*) + test -x "$1" && func_check_tool_result=$1 + ;; + *) + save_IFS=$IFS + IFS=${PATH_SEPARATOR-:} + for _G_check_tool_path in $PATH; do + IFS=$save_IFS + if test -x "$_G_check_tool_path/$1"; then + func_check_tool_result=$_G_check_tool_path/$1 + break + fi + done + IFS=$save_IFS + ;; + esac +} + + +# func_check_versions APP1 VER1 URL1 ...[APPN VERN URLN] +# ------------------------------------------------------ +func_check_versions () +{ + $debug_cmd + + func_check_versions_result=: + + while test $# -gt 0; do + _G_app=$1; shift + _G_reqver=$1; shift + _G_url=$1; shift + + # Diagnose bad buildreq formatting. + case $_G_url in + [a-z]*://*) ;; # looks like a url + *) func_fatal_error "\ +'$_G_url' from the buildreq table in +'bootstrap.conf' does not look like the URL for downloading +$_G_app. Please ensure that buildreq is a strict newline +delimited list of triples; 'program min-version url'." + ;; + esac + + # Honor $APP variables ($TAR, $AUTOCONF, etc.) + _G_appvar=`echo $_G_app |tr '[a-z]' '[A-Z]'` + test TAR = "$_G_appvar" && _G_appvar=AMTAR + eval "_G_app=\${$_G_appvar-$_G_app}" + + # Fail if no version specified, but the program can't be found. + if test x- = "x$_G_reqver"; then + func_check_tool $_G_app + if test -z "$func_check_tool_result"; then + func_error "Prerequisite '$_G_app' not not found. Please install it, or +'export $_G_appvar=/path/to/$_G_app'." + func_check_versions_result=false + else + func_verbose "found '$func_check_tool_result' for $_G_appvar." + fi + else + _G_instver=`func_get_version $_G_app` + + # Fail if --version didn't work. + if test -z "$_G_instver"; then + func_error "Prerequisite '$_G_app' not found. Please install it, or +'export $_G_appvar=/path/to/$_G_app'." + func_check_versions_result=false + + # Fail if a newer version than what we have is required. + else + func_verbose "found '$_G_app' version $_G_instver." + + case $_G_reqver in + =*) + # If $buildreq version starts with '=', version must + # match the installed program exactly. + test "x$_G_reqver" = "x=$_G_instver" || { + func_error "\ + '$_G_app' version == $_G_instver is too old + 'exactly $_G_app-$_G_reqver is required" + func_check_versions_result=false + } + ;; + *) + # Otherwise, anything that is not older is a match. + func_lt_ver "$_G_reqver" "$_G_instver" || { + func_error "\ + '$_G_app' version == $_G_instver is too old + '$_G_app' version >= $_G_reqver is required" + func_check_versions_result=false + } + ;; + esac + fi + fi + done +} + + +# func_cleanup_gnulib +# ------------------- +# Recursively delete everything below the path in the global variable +# GNULIB_PATH. +func_cleanup_gnulib () +{ + $debug_cmd + + _G_status=$? + $RM -fr "$gnulib_path" + exit $_G_status +} + + +# func_download_po_files SUBDIR DOMAIN +# ------------------------------------ +func_download_po_files () +{ + $debug_cmd + + func_echo "getting translations into $1 for $2..." + _G_cmd=`printf "$po_download_command_format" "$2" "$1"` + eval "$_G_cmd" +} + + +# func_update_po_files PO_DIR DOMAIN +# ---------------------------------- +# Mirror .po files to $po_dir/.reference and copy only the new +# or modified ones into $po_dir. Also update $po_dir/LINGUAS. +# Note po files that exist locally only are left in $po_dir but will +# not be included in LINGUAS and hence will not be distributed. +func_update_po_files () +{ + $debug_cmd + + # Directory containing primary .po files. + # Overwrite them only when we're sure a .po file is new. + _G_po_dir=$1 + _G_domain=$2 + + # Mirror *.po files into this dir. + # Usually contains *.s1 checksum files. + _G_ref_po_dir=$_G_po_dir/.reference + + test -d "$_G_ref_po_dir" || mkdir $_G_ref_po_dir || return + func_download_po_files $_G_ref_po_dir $_G_domain \ + && ls "$_G_ref_po_dir"/*.po 2>/dev/null \ + |$SED -e 's|.*/||' -e 's|\.po$||' > "$_G_po_dir/LINGUAS" || return + + # Find sha1sum, named gsha1sum on MacPorts, and shasum on MacOS 10.6+. + func_find_tool SHA1SUM sha1sum gsha1sum shasum sha1 + + test -n "$SHA1SUM" || func_fatal_error "\ +Please install GNU Coreutils, or 'export SHA1SUM=/path/to/sha1sum'." + + _G_langs=`cd $_G_ref_po_dir && echo *.po|$SED 's|\.po||g'` + test '*' = "$_G_langs" && _G_langs=x + for _G_po in $_G_langs; do + case $_G_po in x) continue;; esac + _G_new_po=$_G_ref_po_dir/$_G_po.po + _G_cksum_file=$_G_ref_po_dir/$_G_po.s1 + if ! test -f "$_G_cksum_file" || + ! test -f "$_G_po_dir/$_G_po.po" || + ! $SHA1SUM -c "$_G_cksum_file" \ + < "$_G_new_po" > /dev/null; then + echo "updated $_G_po_dir/$_G_po.po..." + cp "$_G_new_po" "$_G_po_dir/$_G_po.po" \ + && $SHA1SUM < "$_G_new_po" > "$_G_cksum_file" || return + fi + done +} + + + +## --------------- ## +## Option parsing. ## +## --------------- ## + +# Hook in the functions to make sure our own options are parsed during +# the option parsing loop. + +usage='$progpath [OPTION]...' + +# Short help message in response to '-h'. Add to this in 'bootstrap.conf' +# if you accept any additional options. +usage_message="Common Bootstrap Options: + -c, --copy copy files instead of creating symbolic links. + --debug enable verbose shell tracing + -n, --dry-run print commands rather than running them + -f, --force attempt to bootstrap even if the sources seem not + to have been checked out. + --gnulib-srcdir=DIRNAME + specify a local directory where gnulib sources + reside. Use this if you already have the gnulib + sources on your machine, and don't want to waste + your bandwidth downloading them again. Defaults to + \$GNULIB_SRCDIR. + --no-warnings equivalent to '-Wnone' + --skip-git do not fetch files from remote repositories + --skip-po do not download po files. + -v, --verbose verbosely report processing + --version print version information and exit + -W, --warnings=CATEGORY + report the warnings falling in CATEGORY [all] + -h, --help print short or long help message and exit +" + +# Additional text appended to 'usage_message' in response to '--help'. +long_help_message=$long_help_message" + 'recommend' show warnings about missing recommended packages + 'settings' show warnings about missing '$progname.conf' settings + 'upgrade' show warnings about out-dated files + +If the file '$progname.conf' exists in the same directory as this +script, its contents are read as shell variables to configure the +bootstrap. + +For build prerequisites, environment variables like \$AUTOCONF and +\$AMTAR are honored. + +Running without arguments will suffice in most cases. +" + +# Warning categories used by 'bootstrap', append others if you use them +# in your 'bootstrap.conf'. +warning_categories='recommend settings upgrade' + + +# bootstrap_options_prep [ARG]... +# ------------------------------- +# Preparation for options parsed by Bootstrap. +bootstrap_options_prep () +{ + $debug_cmd + + # Option defaults: + opt_copy=${copy-'false'} + opt_dry_run=false + opt_force=false + opt_gnulib_srcdir=$GNULIB_SRCDIR + opt_skip_git=false + opt_skip_po=false + + # Pass back the list of options we consumed. + func_quote_for_eval ${1+"$@"} + bootstrap_options_prep_result=$func_quote_for_eval_result +} +func_add_hook func_options_prep bootstrap_options_prep + + +# bootstrap_parse_options [ARG]... +# -------------------------------- +# Provide handling for Bootstrap specific options. +bootstrap_parse_options () +{ + $debug_cmd + + # Perform our own loop to consume as many options as possible in + # each iteration. + while test $# -gt 0; do + _G_opt=$1 + shift + case $_G_opt in + --dry-run|--dryrun|-n) + opt_dry_run=: ;; + --copy|-c) opt_copy=: ;; + --force|-f) opt_force=: ;; + + --gnulib-srcdir) + test $# = 0 && func_missing_arg $_G_opt && break + opt_gnulib_srcdir=$1 + shift + ;; + + --skip-git|--no-git) + opt_skip_git=: + ;; + + --skip-po|--no-po) + opt_skip_po=: + ;; + + # Separate non-argument short options: + -c*|-f*|-n*) + func_split_short_opt "$_G_opt" + set dummy "$func_split_short_opt_name" \ + "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; + esac + done + + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + bootstrap_parse_options_result=$func_quote_for_eval_result +} +func_add_hook func_parse_options bootstrap_parse_options + + +# bootstrap_validate_options [ARG]... +# ----------------------------------- +# Perform any sanity checks on option settings and/or unconsumed +# arguments. +bootstrap_validate_options () +{ + $debug_cmd + + # Validate options. + test $# -gt 0 \ + && func_fatal_help "too many arguments" + + # Pass back the (empty) list of unconsumed options. + func_quote_for_eval ${1+"$@"} + bootstrap_validate_options_result=$func_quote_for_eval_result +} +func_add_hook func_validate_options bootstrap_validate_options + + +## -------------------------------------------------- ## +## Source package customisations in 'bootstrap.conf'. ## +## -------------------------------------------------- ## + +# Override the default configuration, if necessary. +# Make sure that bootstrap.conf is sourced from the current directory +# if we were invoked as "sh bootstrap". +case $0 in + */*) test -r "$0.conf" && . "$0.conf" ;; + *) test -r "$0.conf" && . ./"$0.conf" ;; +esac + + +## ------------------------------- ## +## Actually perform the bootstrap. ## +## ------------------------------- ## + +func_bootstrap ${1+"$@"} + +# The End. +exit ${exit_status-$EXIT_SUCCESS} + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "500/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: diff --git a/bootstrap.conf b/bootstrap.conf new file mode 100644 index 0000000..ae8a642 --- /dev/null +++ b/bootstrap.conf @@ -0,0 +1,72 @@ +# bootstrap.conf (Stdlib) version 2015-01-03 +# +# Copyright (C) 2013-2015 Gary V. Vaughan +# Written by Gary V. Vaughan, 2013 + +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html. + + +## -------------- ## +## Configuration. ## +## -------------- ## + +# List of programs, minimum versions, and software urls required to +# bootstrap, maintain and release this project. + +buildreq=' + git - http://git-scm.com + ldoc 1.4.2 http://rocks.moonscript.org/manifests/steved/ldoc-1.4.2-1.rockspec + specl 14.1.0 http://rocks.moonscript.org/manifests/gvvaughan/specl-14.1.0-1.rockspec +' + +# List of slingshot files to link into stdlib tree before autotooling. +slingshot_files=' + .autom4te.cfg + GNUmakefile + Makefile.am + build-aux/do-release-commit-and-tag + build-aux/gitlog-to-changelog + build-aux/mkrockspecs + build-aux/release.mk + build-aux/rockspecs.mk + build-aux/sanity.mk + build-aux/specl.mk + build-aux/update-copyright + m4/ax_lua.m4 + travis.yml.in +' + +# Prequisite rocks that need to be installed for travis builds to work. +travis_extra_rocks=' + ansicolors + ldoc + specl +' + +# No need to do any gnulib-tooling here. +gnulib_tool=true + + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "# bootstrap.conf (Stdlib) version " +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "$" +# End: diff --git a/build-aux/config.ld.in b/build-aux/config.ld.in new file mode 100644 index 0000000..7eff64d --- /dev/null +++ b/build-aux/config.ld.in @@ -0,0 +1,34 @@ +-- -*- lua -*- +title = "@PACKAGE_STRING@ Reference" +project = "@PACKAGE_STRING@" +description = "Standard Lua Libraries" +dir = "." + +file = { + -- Modules + "../lib/std.lua", + "../lib/std/debug.lua", + "../lib/std/functional.lua", + "../lib/std/io.lua", + "../lib/std/math.lua", + "../lib/std/operator.lua", + "../lib/std/package.lua", + "../lib/std/strict.lua", + "../lib/std/string.lua", + "../lib/std/table.lua", + "../lib/std/tree.lua", + + -- Classes + "../lib/std/container.lua", + "../lib/std/object.lua", + "../lib/std/list.lua", + "../lib/std/optparse.lua", + "../lib/std/set.lua", + "../lib/std/strbuf.lua", +} + +new_type ("object", "Objects", false, "Fields") + +format = "markdown" +backtick_references = false +sort = true diff --git a/build-aux/gitlog-to-changelog b/build-aux/gitlog-to-changelog new file mode 100755 index 0000000..e02d34c --- /dev/null +++ b/build-aux/gitlog-to-changelog @@ -0,0 +1,432 @@ +eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}' + & eval 'exec perl -wS "$0" $argv:q' + if 0; +# Convert git log output to ChangeLog format. + +my $VERSION = '2012-07-29 06:11'; # UTC +# The definition above must lie within the first 8 lines in order +# for the Emacs time-stamp write hook (at end) to update it. +# If you change this file with Emacs, please let the write hook +# do its job. Otherwise, update this string manually. + +# Copyright (C) 2008-2013 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Written by Jim Meyering + +use strict; +use warnings; +use Getopt::Long; +use POSIX qw(strftime); + +(my $ME = $0) =~ s|.*/||; + +# use File::Coda; # http://meyering.net/code/Coda/ +END { + defined fileno STDOUT or return; + close STDOUT and return; + warn "$ME: failed to close standard output: $!\n"; + $? ||= 1; +} + +sub usage ($) +{ + my ($exit_code) = @_; + my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR); + if ($exit_code != 0) + { + print $STREAM "Try '$ME --help' for more information.\n"; + } + else + { + print $STREAM < ChangeLog + $ME -- -n 5 foo > last-5-commits-to-branch-foo + +SPECIAL SYNTAX: + +The following types of strings are interpreted specially when they appear +at the beginning of a log message line. They are not copied to the output. + + Copyright-paperwork-exempt: Yes + Append the "(tiny change)" notation to the usual "date name email" + ChangeLog header to mark a change that does not require a copyright + assignment. + Co-authored-by: Joe User + List the specified name and email address on a second + ChangeLog header, denoting a co-author. + Signed-off-by: Joe User + These lines are simply elided. + +In a FILE specified via --amend, comment lines (starting with "#") are ignored. +FILE must consist of pairs where SHA is a 40-byte SHA1 (alone on +a line) referring to a commit in the current project, and CODE refers to one +or more consecutive lines of Perl code. Pairs must be separated by one or +more blank line. + +Here is sample input for use with --amend=FILE, from coreutils: + +3a169f4c5d9159283548178668d2fae6fced3030 +# fix typo in title: +s/all tile types/all file types/ + +1379ed974f1fa39b12e2ffab18b3f7a607082202 +# Due to a bug in vc-dwim, I mis-attributed a patch by Paul to myself. +# Change the author to be Paul. Note the escaped "@": +s,Jim .*>,Paul Eggert , + +EOF + } + exit $exit_code; +} + +# If the string $S is a well-behaved file name, simply return it. +# If it contains white space, quotes, etc., quote it, and return the new string. +sub shell_quote($) +{ + my ($s) = @_; + if ($s =~ m![^\w+/.,-]!) + { + # Convert each single quote to '\'' + $s =~ s/\'/\'\\\'\'/g; + # Then single quote the string. + $s = "'$s'"; + } + return $s; +} + +sub quoted_cmd(@) +{ + return join (' ', map {shell_quote $_} @_); +} + +# Parse file F. +# Comment lines (starting with "#") are ignored. +# F must consist of pairs where SHA is a 40-byte SHA1 +# (alone on a line) referring to a commit in the current project, and +# CODE refers to one or more consecutive lines of Perl code. +# Pairs must be separated by one or more blank line. +sub parse_amend_file($) +{ + my ($f) = @_; + + open F, '<', $f + or die "$ME: $f: failed to open for reading: $!\n"; + + my $fail; + my $h = {}; + my $in_code = 0; + my $sha; + while (defined (my $line = )) + { + $line =~ /^\#/ + and next; + chomp $line; + $line eq '' + and $in_code = 0, next; + + if (!$in_code) + { + $line =~ /^([0-9a-fA-F]{40})$/ + or (warn "$ME: $f:$.: invalid line; expected an SHA1\n"), + $fail = 1, next; + $sha = lc $1; + $in_code = 1; + exists $h->{$sha} + and (warn "$ME: $f:$.: duplicate SHA1\n"), + $fail = 1, next; + } + else + { + $h->{$sha} ||= ''; + $h->{$sha} .= "$line\n"; + } + } + close F; + + $fail + and exit 1; + + return $h; +} + +# git_dir_option $SRCDIR +# +# From $SRCDIR, the --git-dir option to pass to git (none if $SRCDIR +# is undef). Return as a list (0 or 1 element). +sub git_dir_option($) +{ + my ($srcdir) = @_; + my @res = (); + if (defined $srcdir) + { + my $qdir = shell_quote $srcdir; + my $cmd = "cd $qdir && git rev-parse --show-toplevel"; + my $qcmd = shell_quote $cmd; + my $git_dir = qx($cmd); + defined $git_dir + or die "$ME: cannot run $qcmd: $!\n"; + $? == 0 + or die "$ME: $qcmd had unexpected exit code or signal ($?)\n"; + chomp $git_dir; + push @res, "--git-dir=$git_dir/.git"; + } + @res; +} + +{ + my $since_date; + my $format_string = '%s%n%b%n'; + my $amend_file; + my $append_dot = 0; + my $cluster = 1; + my $strip_tab = 0; + my $strip_cherry_pick = 0; + my $srcdir; + GetOptions + ( + help => sub { usage 0 }, + version => sub { print "$ME version $VERSION\n"; exit }, + 'since=s' => \$since_date, + 'format=s' => \$format_string, + 'amend=s' => \$amend_file, + 'append-dot' => \$append_dot, + 'cluster!' => \$cluster, + 'strip-tab' => \$strip_tab, + 'strip-cherry-pick' => \$strip_cherry_pick, + 'srcdir=s' => \$srcdir, + ) or usage 1; + + defined $since_date + and unshift @ARGV, "--since=$since_date"; + + # This is a hash that maps an SHA1 to perl code (i.e., s/old/new/) + # that makes a correction in the log or attribution of that commit. + my $amend_code = defined $amend_file ? parse_amend_file $amend_file : {}; + + my @cmd = ('git', + git_dir_option $srcdir, + qw(log --log-size), + '--pretty=format:%H:%ct %an <%ae>%n%n'.$format_string, @ARGV); + open PIPE, '-|', @cmd + or die ("$ME: failed to run '". quoted_cmd (@cmd) ."': $!\n" + . "(Is your Git too old? Version 1.5.1 or later is required.)\n"); + + my $prev_multi_paragraph; + my $prev_date_line = ''; + my @prev_coauthors = (); + while (1) + { + defined (my $in = ) + or last; + $in =~ /^log size (\d+)$/ + or die "$ME:$.: Invalid line (expected log size):\n$in"; + my $log_nbytes = $1; + + my $log; + my $n_read = read PIPE, $log, $log_nbytes; + $n_read == $log_nbytes + or die "$ME:$.: unexpected EOF\n"; + + # Extract leading hash. + my ($sha, $rest) = split ':', $log, 2; + defined $sha + or die "$ME:$.: malformed log entry\n"; + $sha =~ /^[0-9a-fA-F]{40}$/ + or die "$ME:$.: invalid SHA1: $sha\n"; + + # If this commit's log requires any transformation, do it now. + my $code = $amend_code->{$sha}; + if (defined $code) + { + eval 'use Safe'; + my $s = new Safe; + # Put the unpreprocessed entry into "$_". + $_ = $rest; + + # Let $code operate on it, safely. + my $r = $s->reval("$code") + or die "$ME:$.:$sha: failed to eval \"$code\":\n$@\n"; + + # Note that we've used this entry. + delete $amend_code->{$sha}; + + # Update $rest upon success. + $rest = $_; + } + + # Remove lines inserted by "git cherry-pick". + if ($strip_cherry_pick) + { + $rest =~ s/^\s*Conflicts:\n.*//sm; + $rest =~ s/^\s*\(cherry picked from commit [\da-f]+\)\n//m; + } + + my @line = split "\n", $rest; + my $author_line = shift @line; + defined $author_line + or die "$ME:$.: unexpected EOF\n"; + $author_line =~ /^(\d+) (.*>)$/ + or die "$ME:$.: Invalid line " + . "(expected date/author/email):\n$author_line\n"; + + # Format 'Copyright-paperwork-exempt: Yes' as a standard ChangeLog + # `(tiny change)' annotation. + my $tiny = (grep (/^Copyright-paperwork-exempt:\s+[Yy]es$/, @line) + ? ' (tiny change)' : ''); + + my $date_line = sprintf "%s %s$tiny\n", + strftime ("%F", localtime ($1)), $2; + + my @coauthors = grep /^Co-authored-by:.*$/, @line; + # Omit meta-data lines we've already interpreted. + @line = grep !/^(?:Signed-off-by:[ ].*>$ + |Co-authored-by:[ ] + |Copyright-paperwork-exempt:[ ] + )/x, @line; + + # Remove leading and trailing blank lines. + if (@line) + { + while ($line[0] =~ /^\s*$/) { shift @line; } + while ($line[$#line] =~ /^\s*$/) { pop @line; } + } + + # Record whether there are two or more paragraphs. + my $multi_paragraph = grep /^\s*$/, @line; + + # Format 'Co-authored-by: A U Thor ' lines in + # standard multi-author ChangeLog format. + for (@coauthors) + { + s/^Co-authored-by:\s*/\t /; + s/\s*/ + or warn "$ME: warning: missing email address for " + . substr ($_, 5) . "\n"; + } + + # If clustering of commit messages has been disabled, if this header + # would be different from the previous date/name/email/coauthors header, + # or if this or the previous entry consists of two or more paragraphs, + # then print the header. + if ( ! $cluster + || $date_line ne $prev_date_line + || "@coauthors" ne "@prev_coauthors" + || $multi_paragraph + || $prev_multi_paragraph) + { + $prev_date_line eq '' + or print "\n"; + print $date_line; + @coauthors + and print join ("\n", @coauthors), "\n"; + } + $prev_date_line = $date_line; + @prev_coauthors = @coauthors; + $prev_multi_paragraph = $multi_paragraph; + + # If there were any lines + if (@line == 0) + { + warn "$ME: warning: empty commit message:\n $date_line\n"; + } + else + { + if ($append_dot) + { + # If the first line of the message has enough room, then + if (length $line[0] < 72) + { + # append a dot if there is no other punctuation or blank + # at the end. + $line[0] =~ /[[:punct:]\s]$/ + or $line[0] .= '.'; + } + } + + # Remove one additional leading TAB from each line. + $strip_tab + and map { s/^\t// } @line; + + # Prefix each non-empty line with a TAB. + @line = map { length $_ ? "\t$_" : '' } @line; + + print "\n", join ("\n", @line), "\n"; + } + + defined ($in = ) + or last; + $in ne "\n" + and die "$ME:$.: unexpected line:\n$in"; + } + + close PIPE + or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n"; + # FIXME-someday: include $PROCESS_STATUS in the diagnostic + + # Complain about any unused entry in the --amend=F specified file. + my $fail = 0; + foreach my $sha (keys %$amend_code) + { + warn "$ME:$amend_file: unused entry: $sha\n"; + $fail = 1; + } + + exit $fail; +} + +# Local Variables: +# mode: perl +# indent-tabs-mode: nil +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "my $VERSION = '" +# time-stamp-format: "%:y-%02m-%02d %02H:%02M" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "'; # UTC" +# End: diff --git a/build-aux/install-sh b/build-aux/install-sh new file mode 100755 index 0000000..0b0fdcb --- /dev/null +++ b/build-aux/install-sh @@ -0,0 +1,501 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2013-12-25.23; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# 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 NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +tab=' ' +nl=' +' +IFS=" $tab$nl" + +# Set DOITPROG to "echo" to test this script. + +doit=${DOITPROG-} +doit_exec=${doit:-exec} + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +is_target_a_directory=possibly + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) is_target_a_directory=never;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + dstdir=`dirname "$dst"` + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + oIFS=$IFS + IFS=/ + set -f + set fnord $dstdir + shift + set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + set +f && + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/build-aux/missing b/build-aux/missing new file mode 100755 index 0000000..f62bbae --- /dev/null +++ b/build-aux/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2013-10-28.13; # UTC + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=http://www.perl.org/ +flex_URL=http://flex.sourceforge.net/ +gnu_software_URL=http://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/build-aux/mkrockspecs b/build-aux/mkrockspecs new file mode 100755 index 0000000..622d328 --- /dev/null +++ b/build-aux/mkrockspecs @@ -0,0 +1,463 @@ +#!/bin/sh +SH=--[[ # -*- mode: lua; -*- +## Slingshot rockspec generator. +## +## This file is distributed with Slingshot, and licensed under the +## terms of the MIT license reproduced below. + +## ==================================================================== +## Copyright (C) 2013-2015 Gary V. Vaughan +## +## 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. +## ==================================================================== + + +_lua_version_re='"Lua 5."[123]*' +_lua_binaries='lua lua5.3 lua53 lua5.2 lua52 luajit lua5.1 lua51' + +export LUA +export LUA_INIT +export LUA_INIT_5_2 +export LUA_INIT_5_3 +export LUA_PATH +export LUA_CPATH + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi + +# If LUA is not set, search PATH for something suitable. +test -n "$LUA" || { + # Check that the supplied binary is executable and returns a compatible + # Lua version number. + func_vercheck () + { + test -x "$1" && { + eval 'case `'$1' -e "print (_VERSION)" 2>/dev/null` in + '"$_lua_version_re"') LUA='$1' ;; + esac' + } + } + + progname=`echo "$0" |${SED-sed} 's|.*/||'` + + save_IFS="$IFS" + LUA= + for x in $_lua_binaries; do + IFS=: + for dir in $PATH; do + IFS="$save_IFS" + func_vercheck "$dir/$x" + test -n "$LUA" && break + done + IFS="$save_IFS" + test -n "$LUA" && break + e="${e+$e\n}$progname: command not found on PATH: $x" + done +} + +test -n "$LUA" || { + printf "${e+$e\n}$progname: retry after 'export LUA=/path/to/lua'\n" >&2 + exit 1 +} + +LUA_INIT= +LUA_INIT_5_2= +LUA_INIT_5_3= + +# Reexecute using the interpreter suppiled in LUA, or found above. +exec "$LUA" "$0" "$@" +]]SH + + +--[[ ============== ]]-- +--[[ Parse options. ]]-- +--[[ ============== ]]-- + +local usage = 'Usage: mkrockspecs [OPTIONS] PACKAGE VERSION [REVISION] [FILE]\n' + +prog = { + name = arg[0] and arg[0]:gsub (".*/", "") or "mkrockspecs", + + opts = {}, +} + +-- Print an argument processing error message, and return non-zero exit +-- status. +local function opterr (msg) + io.stderr:write (usage) + io.stderr:write (prog.name .. ": error: " .. msg .. ".\n") + io.stderr:write (prog.name .. ": Try '" .. prog.name .. " --help' for help,\n") + os.exit (2) +end + +local function setopt (optname, arglist, i) + local opt = arglist[i] + if i + 1 > #arglist then + opterr ("option '" .. opt .. "' requires an argument") + end + prog.opts[optname] = arglist[i + 1] + return i + 1 +end + +local function die (msg) + msg:gsub ("([^\n]+)\n?", + function () + io.stderr:write (prog.name .. ": error: " .. msg.. "\n") + end) + os.exit (1) +end + +prog["--help"] = function () + print (usage .. [[ + +Convert a YAML configuration file into a full rockspec. + +If FILE is provided, load it as the base configuration, otherwise if +there is a 'rockspec.conf' in the current directory use that, or else +wait for input on stdin. If FILE is '-', force reading base config- +uration from stdin. + +PACKAGE and VERSION are the package name and version number as defined +by 'configure.ac' or similar. REVISION is only required for a revised +rockspec if the default "-1" revision was released with errors. + + -b, --branch=BRANCH make git rockspec use BRANCH + -m, --module-dir=ROOT directory of lua-files for builtin build type + -r, --repository=REPO set the repository name (default=PACKAGE) + --help print this help, then exit + --version print version number, then exit + +Report bugs to http://github.com/gvvaughan/slingshot/issues.]]) + os.exit (0) +end + +prog["--version"] = function () + print [[mkrockspecs (slingshot) 8.0.0 +Written by Gary V. Vaughan , 2013 + +Copyright (C) 2013, Gary V. Vaughan +Slingshot comes with ABSOLUTELY NO WARRANTY. +See source files for individual license conditions.]] + os.exit (0) +end + +prog["-b"] = function (argl, i) return setopt ("branch", argl, i) end +prog["--branch"] = prog["-b"] + +prog["-m"] = function (argl, i) return setopt ("module_root", argl, i) end +prog["--module-dir"] = prog["-m"] + +prog["-r"] = function (argl, i) return setopt ("repository", argl, i) end +prog["--repository"] = prog["-r"] + +local nonopts +local i = 0 +while i < #arg do + i = i + 1 + local opt = arg[i] + + -- Collect remaining arguments not nonopts to save back into _G.arg later. + if type (nonopts) == "table" then + table.insert (nonopts, opt) + + -- Run prog.option handler. + elseif opt:sub (1,1) == "-" and type (prog[opt]) == "function" then + i = prog[opt] (arg, i) + + -- End of option arguments. + elseif opt == "--" then + nonopts = {} + + -- Diagnose unknown command line options. + elseif opt:sub (1, 1) == "-" then + opterr ("unrecognized option '" .. opt .. "'") + + -- First non-option argument marks the end of options. + else + nonopts = { opt } + end +end + +-- put non-option args back into global arg table. +nonopts = nonopts or {} +nonopts[0] = arg[0] +_G.arg = nonopts + +if select ("#", ...) < 2 then + opterr ("only " .. select ("#", ...) .. " arguments provided") +end + +local package = arg[1] +local version = arg[2] +local revision = arg[3] or "1" +local conf = arg[4] or "rockspec.conf" + +-- Unless set explicity, assume the repo is named after the package. +if prog.opts.repository == nil then + prog.opts.repository = package +end + + +--[[ ================= ]]-- +--[[ Helper functions. ]]-- +--[[ ================= ]]-- + +local ok, posix = pcall (require, "posix") + +files = {} + +if ok then + -- faster version if luaposix is available + function tree (root) + for f in posix.files (root) do + local path = root .. "/" .. f + if f:match ("%.lua$") then + table.insert (files, path) + elseif f == "." or f == ".." then + -- don't go into a loop + elseif posix.stat (path, "type") == "directory" then + tree (path) + end + end + end +else + -- fallback version that executes ls in subshells + function tree (root) + local p = io.popen ("ls -1 " .. root .. " 2>/dev/null") + + if p ~= nil then + local f = p:read "*l" + while f ~= nil do + if f:match ("%.lua$") then + table.insert (files, root .. "/" .. f) + else + tree (root .. "/" .. f) + end + f = p:read "*l" + end + end + end +end + +local function escape_pattern (s) + return (string.gsub (s, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0")) +end + +local function loadmap (root) + local map = {} + tree (root) + for _, f in ipairs (files) do + local m = f:match ("^" .. escape_pattern (root) .. "/(.*)%.lua") + map [m:gsub ("/", "."):gsub ("%.init$", "")] = f:gsub ("^%./", "") + end + return map +end + + +--[[ =================== ]]-- +--[[ Load configuration. ]]-- +--[[ =================== ]]-- + +local yaml = require "lyaml" + +-- Slurp io.input (). +local function slurp () + h = io.input () + if h then + local s = h:read "*a" + h:close () + return s + end +end + +if conf == "-" then + io.input (io.stdin) +else + local h = io.open (conf) + if h then + io.input (conf) + h:close () + else + io.input (io.stdin) + end +end + +local spec = yaml.load (slurp ()) +local default = { source = {} } + +-- url needn't be given if it is identical to homepage. +local url +if spec.source ~= nil then + url = spec.source.url +elseif spec.description ~= nil then + url = spec.description.homepage +else + die (conf .. ": could not find source.url or description.homepage") +end +url = url:gsub ("^[a-z]*://", ""):gsub ("%.git$", "") + +-- Interpolate default values. +default.package = package +default.version = version .. "-" .. revision + +configure_flags = "" +if type (spec.external_dependencies) == "table" then + CPPFLAGS, LDFLAGS = "", "" + for name, vars in pairs (spec.external_dependencies) do + if vars.library then + CPPFLAGS = CPPFLAGS .. " -I$(" .. name .. "_INCDIR)" + LDFLAGS = LDFLAGS .. " -L$(" .. name .. "_LIBDIR)" + end + end + + if string.len (CPPFLAGS) > 0 then + configure_flags = configure_flags .. + "CPPFLAGS='" .. CPPFLAGS:gsub ("^%s", "") .. "'" .. + " LDFLAGS='" .. LDFLAGS:gsub ("^%s", "") .. "'" .. + " " + end +end + +-- If we have a module root, use the luarocks "builtin" type. +if version ~= "scm" and version ~= "git" then + if prog.opts.module_root ~= nil then + default.build = { + type = "builtin", + modules = loadmap (prog.opts.module_root), + } + elseif spec.build ~= nil and spec.build.modules ~= nil then + default.build = { type = "builtin" } + end +end + +default.build = default.build or { + type = "command", + build_command = "./configure " .. + "LUA='$(LUA)' LUA_INCLUDE='-I$(LUA_INCDIR)' " .. configure_flags .. + "--prefix='$(PREFIX)' --libdir='$(LIBDIR)' --datadir='$(LUADIR)' " .. + "--datarootdir='$(PREFIX)' && make clean all", + install_command = "make install luadir='$(LUADIR)' luaexecdir='$(LIBDIR)'", + copy_directories = {}, +} + +-- Additional spec-type dependent values. +spec.source = spec.source or {} +spec.build = spec.build or {} +if version ~= "scm" and version ~= "git" then + spec.source.url = "http://" .. url .. "/archive/release-v" .. version .. ".zip" + spec.source.dir = prog.opts.repository .. "-release-v" .. version +else + spec.source.url = "git://" .. url .. ".git" + spec.source.branch = prog.opts.branch + spec.build.modules = nil + default.build.build_command = "LUA='$(LUA)' ./bootstrap && " .. default.build.build_command +end + + +-- Recursive merge, settings from spec take precedence. Elements of src +-- overwrite equivalent keys in dest. +local function merge (dest, src) + for k, v in pairs (src) do + if type (v) == "table" then + dest[k] = merge (dest[k] or {}, src[k]) + else + dest[k] = src[k] + end + end + return dest +end + +spec = merge (default, spec) + + +--[[ ======= ]]-- +--[[ Output. ]]-- +--[[ ======= ]]-- + +-- Recursively format X, with pretty printing. +local function format (x, indent) + indent = indent or "" + if type (x) == "table" then + if next (x) == nil then + return "{}" + else + local s = "{\n" + + -- Collect and sort non-numeric keys first. + keys = {} + for k in pairs (x) do + if type (k) ~= "number" then table.insert (keys, k) end + end + table.sort (keys, function (a, b) return tostring (a) < tostring (b) end) + + -- Display non-numeric key pairs in sort order. + for _, k in ipairs (keys) do + s = s .. indent + if k:match ("[^_%w]") then + -- wrap keys with non-%w chars in square brackets + s = s .. '["' .. k .. '"]' + else + s = s .. k + end + s = s .. " = " .. format (x[k], indent .. " ") .. ",\n" + end + + -- And numeric key pairs last. + for i, v in ipairs (x) do + s = s .. indent .. format (v, indent .. " ") .. ",\n" + end + return s..indent:sub (1, -3).."}" + end + elseif type (x) == "string" then + return string.format ("%q", x) + else + return tostring (x) + end +end + +-- Use the standard order for known keys. +for _, k in ipairs { + "package", + "version", + "description", + "source", + "dependencies", + "external_dependencies", + "build", +} do + print (k .. " = " .. format (spec[k], " ")) + spec[k] = nil +end + +-- Output anything left in the table at the end. +for i, v in pairs (spec) do + print (i .. " = " .. format (v, " ")) +end + +os.exit (0) diff --git a/build-aux/release.mk b/build-aux/release.mk new file mode 100644 index 0000000..13dce9a --- /dev/null +++ b/build-aux/release.mk @@ -0,0 +1,393 @@ +# Slingshot release rules for GNU Make. + +# ====================================================================== +# Copyright (C) 2001-2015 Free Software Foundation, Inc. +# Originally by Jim Meyering, Simon Josefsson, Eric Blake, +# Akim Demaille, Gary V. Vaughan, and others. +# This version by Gary V. Vaughan, 2013. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# ====================================================================== + +NOTHING_ELSE ?= + + +## --------------- ## +## GNU Make magic. ## +## --------------- ## + +# This file uses GNU Make extensions. Include it from GNUmakefile with: +# +# include build-aux/release.mk + +# Make tar archive easier to reproduce. +export TAR_OPTIONS = --owner=0 --group=0 --numeric-owner + +# Helper variables. +_empty = +_sp = $(_empty) $(_empty) + +# member-check,VARIABLE,VALID-VALUES +# ---------------------------------- +# Check that $(VARIABLE) is in the space-separated list of VALID-VALUES, and +# return it. Die otherwise. +member-check = \ + $(strip \ + $(if $($(1)), \ + $(if $(findstring $(_sp),$($(1))), \ + $(error invalid $(1): '$($(1))', expected $(2)), \ + $(or $(findstring $(_sp)$($(1))$(_sp),$(_sp)$(2)$(_sp)), \ + $(error invalid $(1): '$($(1))', expected $(2)))), \ + $(error $(1) undefined))) + +include Makefile + +## --------- ## +## Defaults. ## +## --------- ## + +GIT ?= git +LUA ?= lua +LUAROCKS ?= luarocks +TAR ?= tar + +# Override this in cfg.mk if you are using a different format in your +# NEWS file. +today ?= $(shell date +%Y-%m-%d) + +# Old releases are stored here. +release_archive_dir ?= ../release + +# Override this in cfg.mk if you follow different procedures. +release-prep-hook ?= release-prep + +_build-aux ?= build-aux +my_distdir ?= $(PACKAGE)-$(VERSION) +prev_version_file ?= $(srcdir)/.prev-version +old_NEWS_hash-file ?= $(srcdir)/local.mk +gl_noteworthy_news_ = \#\# Noteworthy changes in release ?.? (????-??-??) [?] + +PREV_VERSION = $(shell cat $(prev_version_file) 2>/dev/null) +VERSION_REGEXP = $(subst .,\.,$(VERSION)) +PREV_VERSION_REGEXP = $(subst .,\.,$(PREV_VERSION)) + + +## ------------- ## +## Distribution. ## +## ------------- ## + +gitlog_to_changelog = $(srcdir)/build-aux/gitlog-to-changelog + +dist-hook: ChangeLog +.PHONY: ChangeLog +ChangeLog: + $(AM_V_GEN)if test -d '$(srcdir)/.git'; then \ + $(gitlog_to_changelog) $(gitlog_args) > '$@T'; \ + rm -f '$@'; mv '$@T' '$@'; \ + fi + +# Override this in GNUmakefile if you don't want to automatically +# redistribute all the maintainer support files (take care that +# Travis CI is finicky about this, and will likely need tweaking +# to cope with missing any of these if you decide to omit them). + +_travis_yml ?= .travis.yml travis.yml.in + +release_extra_dist ?= \ + .autom4te.cfg \ + GNUmakefile \ + bootstrap \ + bootstrap.conf \ + local.mk \ + $(_travis_yml) \ + $(NOTHING_ELSE) + +EXTRA_DIST += \ + $(_build-aux)/release.mk \ + $(gitlog_to_changelog) \ + $(release_extra_dist) \ + $(NOTHING_ELSE) + +all-am: $(_travis_yml) + + +## -------- ## +## Release. ## +## -------- ## + +# The vast majority of what follows is preparation -in the form +# of early bail-out if something is not right yet- for the final +# check-in-release-branch rule that makes the tip of the release +# branch match the contents of a 'make distcheck' tarball. + +# Validate and return $(RELEASE_TYPE), or die. +RELEASE_TYPES = alpha beta stable +release-type = $(call member-check,RELEASE_TYPE,$(RELEASE_TYPES)) + +# This will actually make the release, including sending release +# announcements, and pushing changes back to the origin. +# Use it like this, eg: +# make RELEASE_TYPE=beta +.PHONY: release +release: + $(AM_V_GEN)$(MAKE) $(release-type) + $(AM_V_GEN)$(MAKE) push + $(AM_V_GEN)$(MAKE) upload + $(AM_V_GEN)$(MAKE) mail + +submodule-checks ?= no-submodule-changes public-submodule-commit + +.PHONY: no-submodule-changes +no-submodule-changes: + $(AM_V_GEN)if test -d $(srcdir)/.git \ + && git --version >/dev/null 2>&1; then \ + diff=$$(cd $(srcdir) && git submodule -q foreach \ + git diff-index --name-only HEAD); \ + case $$diff in '') ;; \ + *) echo '$(ME): submodule files are locally modified:'; \ + echo "$$diff"; exit 1;; esac; \ + else \ + : ; \ + fi + +# Ensure that each sub-module commit we're using is public. +# Without this, it is too easy to tag and release code that +# cannot be built from a fresh clone. +.PHONY: public-submodule-commit +public-submodule-commit: + $(AM_V_GEN)if test -d $(srcdir)/.git \ + && git --version >/dev/null 2>&1; then \ + cd $(srcdir) && \ + git submodule --quiet foreach \ + 'test "$$(git rev-parse "$$sha1")" \ + = "$$(git merge-base origin "$$sha1")"' \ + || { echo '$(ME): found non-public submodule commit' >&2; \ + exit 1; }; \ + else \ + : ; \ + fi +# This rule has a high enough utility/cost ratio that it should be a +# dependent of "check" by default. However, some of us do occasionally +# commit a temporary change that deliberately points to a non-public +# submodule commit, and want to be able to use rules like "make check". +# In that case, run e.g., "make check gl_public_submodule_commit=" +# to disable this test. +gl_public_submodule_commit ?= public-submodule-commit +check: $(gl_public_submodule_commit) + +# These targets do all the file shuffling necessary for a release, but +# purely locally, so you can rewind and redo before pushing anything +# to origin or sending release announcements. Use it like this, eg: +# +# make beta +.PHONY: alpha beta stable +alpha beta stable: $(submodule-checks) + $(AM_V_GEN)test $@ = stable && \ + { echo $(VERSION) |$(EGREP) '^[0-9]+(\.[0-9]+)*$$' >/dev/null \ + || { echo "invalid version string: $(VERSION)" 1>&2; exit 1;};}\ + || : + $(AM_V_at)$(MAKE) prev-version-check + $(AM_V_at)$(MAKE) vc-diff-check + $(AM_V_at)$(MAKE) release-commit RELEASE_TYPE=$@ + $(AM_V_at)$(MAKE) news-check + $(AM_V_at)$(MAKE) distcheck + $(AM_V_at)$(MAKE) check + $(AM_V_at)$(MAKE) $(release-prep-hook) RELEASE_TYPE=$@ + $(AM_V_at)$(MAKE) check-in-release-branch + +prev-version-check: + $(AM_V_at)if test -z "`$(GIT) ls-files $(prev_version_file)`"; \ + then \ + echo "error: checked in $(prev_version_file) required." >&2; \ + exit 1; \ + fi + +# Abort the release if there are unchecked in changes remaining. +vc-diff-check: + $(AM_V_at)if ! $(GIT) diff --exit-code; then \ + $(GIT) diff >/dev/null; \ + echo "error: Some files are locally modified" >&2; \ + exit 1; \ + fi + +# Select which lines of NEWS are searched for $(news-check-regexp). +# This is a sed line number spec. The default says that we search +# only line 3 of NEWS for $(news-check-regexp), to match the behaviour +# of '$(_build-aux)/do-release-commit-and-tag'. +# If you want to search only lines 1-10, use "1,10". +news-check-lines-spec ?= 3 +news-check-regexp ?= '^\#\#.* $(VERSION_REGEXP) \($(today)\)' + +Makefile.in: NEWS + +NEWS: + $(AM_V_GEN)if test -f NEWS.md; then ln -s NEWS.md NEWS; \ + elif test -f NEWS.rst; then ln -s NEWS.rst NEWS; \ + elif test -f NEWS.txt; then ln -s NEWS.txt NEWS; \ + fi + +news-check: NEWS + $(AM_V_GEN)if $(SED) -n $(news-check-lines-spec)p $< \ + | $(EGREP) $(news-check-regexp) >/dev/null; then \ + :; \ + else \ + echo 'NEWS: $$(news-check-regexp) failed to match' 1>&2; \ + exit 1; \ + fi + +.PHONY: release-commit +release-commit: NEWS + $(AM_V_GEN)cd $(srcdir) \ + && $(_build-aux)/do-release-commit-and-tag \ + -C $(abs_builddir) $(VERSION) $(RELEASE_TYPE) + +define emit-commit-log + printf '%s\n' 'maint: post-release administrivia.' '' \ + '* NEWS: Add header line for next release.' \ + '* .prev-version: Record previous version.' \ + '* $(old_NEWS_hash-file) (old_NEWS_hash): Auto-update.' +endef + +.PHONY: release-prep +release-prep: $(scm_rockspec) + $(AM_V_GEN)$(MAKE) --no-print-directory -s announcement \ + > ~/announce-$(my_distdir) + $(AM_V_at)if test -d $(release_archive_dir); then \ + ln $(rel-files) $(release_archive_dir); \ + chmod a-w $(rel-files); \ + fi + $(AM_V_at)echo $(VERSION) > $(prev_version_file) + $(AM_V_at)$(MAKE) update-old-NEWS-hash + $(AM_V_at)perl -pi \ + -e '$$. == 3 and print "$(gl_noteworthy_news_)\n\n\n"' \ + `readlink $(srcdir)/NEWS 2>/dev/null || echo $(srcdir)/NEWS` + $(AM_V_at)msg=$$($(emit-commit-log)) || exit 1; \ + cd $(srcdir) && $(GIT) commit -s -m "$$msg" -a + @echo '**** Release announcement in ~/announce-$(my_distdir)' + +# Strip out copyright messages with years, so that changing those (e.g. +# with 'make update-copyight') doesn't change the old_NEWS_hash. +NEWS_hash = \ + $$(sed -n '/^\*.* $(PREV_VERSION_REGEXP) ([0-9-]*)/,$$p' \ + $(srcdir)/NEWS \ + | perl -0777 -pe 's/^Copyright.+?[12][0-9]{3}.+?\n//ms' \ + | md5sum - \ + | sed 's/ .*//') + +# Update the hash stored above. Do this after each release and +# for any corrections to old entries. + +old-NEWS-regexp = '^old_NEWS_hash[ \t]+?=[ \t]+' +update-old-NEWS-hash: NEWS + $(AM_V_GEN)if $(EGREP) $(old-NEWS-regexp) $(old_NEWS_hash-file); then \ + perl -pi -e 's/^(old_NEWS_hash[ \t]+:?=[ \t]+).*/$${1}'"$(NEWS_hash)/" \ + $(old_NEWS_hash-file); \ + else \ + printf '%s\n' '' "old_NEWS_hash = $(NEWS_hash)" \ + >> $(old_NEWS_hash-file); \ + fi + +ANNOUNCE_ENV = LUA_INIT= LUA_PATH='$(abs_srcdir)/?-git-1.rockspec' +ANNOUNCE_PRINT = $(ANNOUNCE_ENV) $(LUA) -l$(PACKAGE) -e + +_PRE = " https://raw.githubusercontent" +_POST = "/release-v$(VERSION)/$(PACKAGE)-$(VERSION)-$(rockspec_revision).rockspec" +GITHUB_ROCKSPEC = (source.url:gsub ("^git://github", $(_PRE)):gsub ("%.git$$", $(_POST))) + +announcement: NEWS +# Not $(AM_V_GEN) since the output of this command serves as +# announcement message: else, it would start with " GEN announcement". + $(AM_V_at)printf '%s\n' \ + '# [ANN] $(PACKAGE_NAME) $(VERSION) released' \ + '' + $(AM_V_at)$(ANNOUNCE_PRINT) 'print (description.detailed)' + $(AM_V_at)printf '%s\n' '' \ + 'I am happy to announce release $(VERSION) of $(PACKAGE_NAME).' \ + '' + $(AM_V_at)$(ANNOUNCE_PRINT) \ + 'print ("$(PACKAGE_NAME)'\''s home page is at " .. description.homepage)' + $(AM_V_at)printf '\n' + $(AM_V_at)$(SED) -n \ + -e '/^\#\# Noteworthy changes in release $(PREV_VERSION)/q' \ + -e p NEWS |$(SED) -e 1,2d + $(AM_V_at)printf '%s\n' \ + 'Install it with LuaRocks, using:' '' \ + ' luarocks install $(PACKAGE) $(VERSION)' + $(AM_V_at)$(ANNOUNCE_PRINT) 'print ($(GITHUB_ROCKSPEC))' + + +branch = $(shell $(GIT) branch |$(SED) -ne '/^\* /{s///;p;q;}') +GCO = $(GIT) checkout +release-tarball = $(my_distdir).tar.gz + +# Anything in $(_save-files) is not removed after switching to the +# release branch, and is thus "in the release". Add addtional partial +# filenames to save in save_release_files, for example: +# save_release_files = RELEASE-NOTES- +_save-files = \ + $(release-tarball) \ + $(save_release_files) \ + $(NOTHING_ELSE) + + +list-to-rexp = $(SED) -e 's|^|(|' -e 's/|$$/)/' +git-clean-files = `printf -- '-e %s ' $(_save-files)` +grep-clean-files = `printf -- '%s|' $(_save-files) |$(list-to-rexp)` + +# Switch to (or create) 'release' branch, remove all files, except the +# newly generated dist tarball, then unpack the dist tarball and check +# in all the files it creates, and tag that as the next release. +# Github creates automatic zipballs of tagged git revisions, so we can +# safely use this tag in the rockspecs we distribute. +submodule-regexp ?= '^\[submodule "' +submodule-extract-spec ?= 's|^.*"\([^"]*\)".*$$|\1|' + +.PHONY: check-in-release-branch +check-in-release-branch: + $(AM_V_GEN)$(GCO) -b release v$(VERSION) 2>/dev/null || $(GCO) release + $(AM_V_at)$(GIT) pull origin release 2>/dev/null || true + $(AM_V_at)if $(EGREP) $(submodule-regexp) .gitmodules >/dev/null 2>&1; then \ + $(EGREP) $(submodule-regexp) .gitmodules \ + | $(SED) $(submodule-extract-spec) | xargs rm -rf; \ + fi + $(AM_V_at)$(GIT) clean -dfx $(git-clean-files) + $(AM_V_at)remove_re=$(grep-clean-files); \ + $(GIT) rm -f `$(GIT) ls-files |$(EGREP) -v "$$remove_re"` + $(AM_V_at)ln -s . '$(my_distdir)' + $(AM_V_at)$(TAR) zxf '$(release-tarball)' + $(AM_V_at)rm -f '$(my_distdir)' '$(release-tarball)' + $(AM_V_at)$(GIT) add . + $(AM_V_at)$(GIT) commit -s -a -m 'Release v$(VERSION).' + $(AM_V_at)$(GIT) tag -s -a -m 'Full source release v$(VERSION)' release-v$(VERSION) + $(AM_V_at)$(GCO) $(branch) + +.PHONY: push +push: + $(AM_V_at)$(GIT) push origin master + $(AM_V_at)$(GIT) push origin release + $(AM_V_at)$(GIT) push origin v$(VERSION) + $(AM_V_at)$(GIT) push origin release-v$(VERSION) + +.PHONY: upload +upload: rockspecs + $(AM_V_at)$(LUAROCKS) upload $${API_KEY+--api-key=$$API_KEY} \ + '$(PACKAGE)-$(VERSION)-$(rockspec_revision).rockspec' + +announce_emails ?= lua-l@lists.lua.org + +.PHONY: mail +mail: rockspecs + $(AM_V_at)cat ~/announce-$(my_distdir) \ + | mail -s '[ANN] $(PACKAGE) $(VERSION) released' -- \ + $(announce_emails) diff --git a/build-aux/rockspecs.mk b/build-aux/rockspecs.mk new file mode 100644 index 0000000..e64cab1 --- /dev/null +++ b/build-aux/rockspecs.mk @@ -0,0 +1,116 @@ +# Slingshot rockspec rules for make. + +# This file is distributed with Slingshot, and licensed under the +# terms of the MIT license reproduced below. + +# ==================================================================== # +# Copyright (C) 2013-2015 Reuben Thomas and Gary V. Vaughan # +# # +# 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. # +# ==================================================================== # + + +## --------- ## +## LuaRocks. ## +## --------- ## + +# This file is suitable for use from a portable Makefile, you might +# include it into the top-level Makefile.am with: +# +# include build-aux/rockspecs.mk + +luarocks_config = build-aux/luarocks-config.lua +rockspec_conf = $(srcdir)/rockspec.conf +mkrockspecs = $(srcdir)/build-aux/mkrockspecs +package_rockspec = $(srcdir)/$(PACKAGE)-$(VERSION)-$(rockspec_revision).rockspec +scm_rockspec = $(PACKAGE)-git-$(rockspec_revision).rockspec + +# If you need a different rockspec revision, override this on the make +# command line: +# +# make rockspecs rockspec_revision=2 +rockspec_revision = 1 + +LUAROCKS = luarocks +MKROCKSPECS = $(MKROCKSPECS_ENV) $(LUA) $(mkrockspecs) + +ROCKSPECS_DEPS = \ + $(luarocks_config) \ + $(mkrockspecs) \ + $(rockspec_conf) \ + $(NOTHING_ELSE) + +set_LUA_BINDIR = LUA_BINDIR=`which $(LUA) |$(SED) 's|/[^/]*$$||'` +LUA_INCDIR = `cd $$LUA_BINDIR/../include && pwd` +LUA_LIBDIR = `cd $$LUA_BINDIR/../lib && pwd` + +$(luarocks_config): Makefile.am + @test -d build-aux || mkdir build-aux + $(AM_V_GEN){ \ + $(set_LUA_BINDIR); \ + echo 'rocks_trees = { "$(abs_srcdir)/luarocks" }'; \ + echo 'variables = {'; \ + echo ' LUA = "$(LUA)",'; \ + echo ' LUA_BINDIR = "'$$LUA_BINDIR'",'; \ + echo ' LUA_INCDIR = "'$(LUA_INCDIR)'",'; \ + echo ' LUA_LIBDIR = "'$(LUA_LIBDIR)'",'; \ + echo '}'; \ + } > '$@' + +$(package_rockspec): $(ROCKSPECS_DEPS) + $(AM_V_at)rm -f '$@' 2>/dev/null || : + $(AM_V_GEN)test -f '$@' || \ + $(MKROCKSPECS) $(mkrockspecs_args) \ + $(PACKAGE) $(VERSION) $(rockspec_revision) > '$@' + $(AM_V_at)$(LUAROCKS) lint '$@' + +$(scm_rockspec): $(ROCKSPECS_DEPS) + $(AM_V_at)rm '$@' 2>/dev/null || : + $(AM_V_GEN)test -f '$@' || \ + $(MKROCKSPECS) $(mkrockspecs_args) \ + $(PACKAGE) git 1 > '$@' + $(AM_V_at)$(LUAROCKS) lint '$@' + +.PHONY: rockspecs +rockspecs: + $(AM_V_at)rm -f *.rockspec + $(AM_V_at)$(MAKE) $(package_rockspec) $(scm_rockspec) + + +## ------------- ## +## Distribution. ## +## ------------- ## + +EXTRA_DIST += \ + $(mkrockspecs) \ + $(package_rockspec) \ + $(rockspec_conf) \ + $(NOTHING_ELSE) + +save_release_files += $(scm_rockspec) + + +## ------------ ## +## Maintenance. ## +## ------------ ## + +DISTCLEANFILES += \ + $(luarocks_config) \ + $(NOTHING_ELSE) diff --git a/build-aux/sanity-cfg.mk b/build-aux/sanity-cfg.mk new file mode 100644 index 0000000..6bbf697 --- /dev/null +++ b/build-aux/sanity-cfg.mk @@ -0,0 +1,3 @@ +exclude_file_name_regexp--sc_error_message_uppercase = ^lib/std/(list|optparse).lua$$ + +EXTRA_DIST += build-aux/sanity-cfg.mk diff --git a/build-aux/sanity.mk b/build-aux/sanity.mk new file mode 100644 index 0000000..78bb125 --- /dev/null +++ b/build-aux/sanity.mk @@ -0,0 +1,1114 @@ +# Slingshot sanity checking rules for GNU Make. + +# ====================================================================== +# Copyright (C) 2001-2013 Free Software Foundation, Inc. +# Originally by Jim Meyering, Simon Josefsson, Eric Blake, +# Akim Demaille, Gary V. Vaughan, and others. +# This version by Gary V. Vaughan, 2013. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# ====================================================================== + +VC_LIST = $(GIT) ls-files + +# You can override this variable in cfg.mk to set your own regexp +# matching files to ignore. +VC_LIST_ALWAYS_EXCLUDE_REGEX ?= ^$$ + +# This is to preprocess robustly the output of $(VC_LIST), so that even +# when $(srcdir) is a pathological name like "....", the leading sed command +# removes only the intended prefix. +_dot_escaped_srcdir = $(subst .,\.,$(srcdir)) + +# Post-process $(VC_LIST) output, prepending $(srcdir)/, but only +# when $(srcdir) is not ".". +ifeq ($(srcdir),.) + _prepend_srcdir_prefix = +else + _prepend_srcdir_prefix = | sed 's|^|$(srcdir)/|' +endif + +# In order to be able to consistently filter "."-relative names, +# (i.e., with no $(srcdir) prefix), this definition is careful to +# remove any $(srcdir) prefix, and to restore what it removes. +_sc_excl = \ + $(or $(exclude_file_name_regexp--$@),^build-aux/sanity.mk$$|gnulib$$|^slingshot$$) +VC_LIST_EXCEPT = \ + $(VC_LIST) | sed 's|^$(_dot_escaped_srcdir)/||' \ + | if test -f $(srcdir)/.x-$@; then grep -vEf $(srcdir)/.x-$@; \ + else grep -Ev -e "$${VC_LIST_EXCEPT_DEFAULT-ChangeLog}"; fi \ + | grep -Ev -e '($(VC_LIST_ALWAYS_EXCLUDE_REGEX)|$(_sc_excl))' \ + $(_prepend_srcdir_prefix) + + +## --------------- ## +## Sanity checks. ## +## --------------- ## + +-include $(srcdir)/$(_build-aux)/sanity-cfg.mk + +_cfg_mk := $(wildcard $(srcdir)/cfg.mk) + +# Collect the names of rules starting with 'sc_'. +syntax-check-rules := $(sort $(shell sed -n 's/^\(sc_[a-zA-Z0-9_-]*\):.*/\1/p' \ + $(srcdir)/$(ME) $(_cfg_mk) $(srcdir)/$(_build-aux)/*.mk)) +.PHONY: $(syntax-check-rules) + +ifeq ($(shell $(VC_LIST) >/dev/null 2>&1; echo $$?),0) + local-checks-available += $(syntax-check-rules) +else + local-checks-available += no-vc-detected +no-vc-detected: + @echo "No version control files detected; skipping syntax check" +endif +.PHONY: $(local-checks-available) + +# Arrange to print the name of each syntax-checking rule just before running it. +$(syntax-check-rules): %: %.m +sc_m_rules_ = $(patsubst %, %.m, $(syntax-check-rules)) +.PHONY: $(sc_m_rules_) +$(sc_m_rules_): + @echo $(patsubst sc_%.m, %, $@) + @date +%s.%N > .sc-start-$(basename $@) + +# Compute and print the elapsed time for each syntax-check rule. +sc_z_rules_ = $(patsubst %, %.z, $(syntax-check-rules)) +.PHONY: $(sc_z_rules_) +$(sc_z_rules_): %.z: % + @end=$$(date +%s.%N); \ + start=$$(cat .sc-start-$*); \ + rm -f .sc-start-$*; \ + awk -v s=$$start -v e=$$end \ + 'END {printf "%.2f $(patsubst sc_%,%,$*)\n", e - s}' < /dev/null + +# The patsubst here is to replace each sc_% rule with its sc_%.z wrapper +# that computes and prints elapsed time. +local-check := \ + $(patsubst sc_%, sc_%.z, \ + $(filter-out $(local-checks-to-skip), $(local-checks-available))) + +syntax-check: $(local-check) + +# _sc_search_regexp +# +# This macro searches for a given construct in the selected files and +# then takes some action. +# +# Parameters (shell variables): +# +# prohibit | require +# +# Regular expression (ERE) denoting either a forbidden construct +# or a required construct. Those arguments are exclusive. +# +# exclude +# +# Regular expression (ERE) denoting lines to ignore that matched +# a prohibit construct. For example, this can be used to exclude +# comments that mention why the nearby code uses an alternative +# construct instead of the simpler prohibited construct. +# +# in_vc_files | in_files +# +# grep-E-style regexp selecting the files to check. For in_vc_files, +# the regexp is used to select matching files from the list of all +# version-controlled files; for in_files, it's from the names printed +# by "find $(srcdir)". When neither is specified, use all files that +# are under version control. +# +# containing | non_containing +# +# Select the files (non) containing strings matching this regexp. +# If both arguments are specified then CONTAINING takes +# precedence. +# +# with_grep_options +# +# Extra options for grep. +# +# ignore_case +# +# Ignore case. +# +# halt +# +# Message to display before to halting execution. +# +# Finally, you may exempt files based on an ERE matching file names. +# For example, to exempt from the sc_space_tab check all files with the +# .diff suffix, set this Make variable: +# +# exclude_file_name_regexp--sc_space_tab = \.diff$ +# +# Note that while this functionality is mostly inherited via VC_LIST_EXCEPT, +# when filtering by name via in_files, we explicitly filter out matching +# names here as well. + +# Initialize each, so that envvar settings cannot interfere. +export require = +export prohibit = +export exclude = +export in_vc_files = +export in_files = +export containing = +export non_containing = +export halt = +export with_grep_options = + +# By default, _sc_search_regexp does not ignore case. +export ignore_case = +_ignore_case = $$(test -n "$$ignore_case" && printf %s -i || :) + +define _sc_say_and_exit + dummy=; : so we do not need a semicolon before each use; \ + { printf '%s\n' "$(ME): $$msg" 1>&2; exit 1; }; +endef + +define _sc_search_regexp + dummy=; : so we do not need a semicolon before each use; \ + \ + : Check arguments; \ + test -n "$$prohibit" && test -n "$$require" \ + && { msg='Cannot specify both prohibit and require' \ + $(_sc_say_and_exit) } || :; \ + test -z "$$prohibit" && test -z "$$require" \ + && { msg='Should specify either prohibit or require' \ + $(_sc_say_and_exit) } || :; \ + test -z "$$prohibit" && test -n "$$exclude" \ + && { msg='Use of exclude requires a prohibit pattern' \ + $(_sc_say_and_exit) } || :; \ + test -n "$$in_vc_files" && test -n "$$in_files" \ + && { msg='Cannot specify both in_vc_files and in_files' \ + $(_sc_say_and_exit) } || :; \ + test "x$$halt" != x \ + || { msg='halt not defined' $(_sc_say_and_exit) }; \ + \ + : Filter by file name; \ + if test -n "$$in_files"; then \ + files=$$(find $(srcdir) | grep -E "$$in_files" \ + | grep -Ev '$(_sc_excl)'); \ + else \ + files=$$($(VC_LIST_EXCEPT)); \ + if test -n "$$in_vc_files"; then \ + files=$$(echo "$$files" | grep -E "$$in_vc_files"); \ + fi; \ + fi; \ + \ + : Filter by content; \ + test -n "$$files" && test -n "$$containing" \ + && { files=$$(grep -l "$$containing" $$files); } || :; \ + test -n "$$files" && test -n "$$non_containing" \ + && { files=$$(grep -vl "$$non_containing" $$files); } || :; \ + \ + : Check for the construct; \ + if test -n "$$files"; then \ + if test -n "$$prohibit"; then \ + grep $$with_grep_options $(_ignore_case) -nE "$$prohibit" $$files \ + | grep -vE "$${exclude:-^$$}" \ + && { msg="$$halt" $(_sc_say_and_exit) } || :; \ + else \ + grep $$with_grep_options $(_ignore_case) -LE "$$require" $$files \ + | grep . \ + && { msg="$$halt" $(_sc_say_and_exit) } || :; \ + fi \ + else :; \ + fi || :; +endef + +sc_avoid_if_before_free: + @test -f $(srcdir)/$(_build-aux)/useless-if-before-free && \ + $(srcdir)/$(_build-aux)/useless-if-before-free \ + $(useless_free_options) \ + $$($(VC_LIST_EXCEPT) | grep -v useless-if-before-free) && \ + { echo '$(ME): found useless "if" before "free" above' 1>&2; \ + exit 1; } || : + +sc_cast_of_argument_to_free: + @prohibit='\&2; \ + exit 1; } || : + +# Error messages should not start with a capital letter +sc_error_message_uppercase: + @grep -nEA2 '[^rp]error *\(' $$($(VC_LIST_EXCEPT)) \ + | grep -E '"[A-Z]' \ + | grep -vE '"FATAL|"WARNING|"Java|"C#|PRIuMAX' && \ + { echo '$(ME): found capitalized error message' 1>&2; \ + exit 1; } || : + +# Error messages should not end with a period +sc_error_message_period: + @grep -nEA2 '[^rp]error *\(' $$($(VC_LIST_EXCEPT)) \ + | grep -E '[^."]\."' && \ + { echo '$(ME): found error message ending in period' 1>&2; \ + exit 1; } || : + +sc_file_system: + @prohibit=file''system \ + ignore_case=1 \ + halt='found use of "file''system"; spell it "file system"' \ + $(_sc_search_regexp) + +sc_makefile: + @prohibit=make''flie \ + ignore_case=1 \ + halt='found misspelled "make''flie"; use "makefile" instead' \ + $(_sc_search_regexp) + +# Don't use cpp tests of this symbol. All code assumes config.h is included. +sc_prohibit_have_config_h: + @prohibit='^# *if.*HAVE''_CONFIG_H' \ + halt='found use of HAVE''_CONFIG_H; remove' \ + $(_sc_search_regexp) + +# Nearly all .c files must include . However, we also permit this +# via inclusion of a package-specific header, if cfg.mk specified one. +# config_h_header must be suitable for grep -E. +config_h_header ?= +sc_require_config_h: + @require='^# *include $(config_h_header)' \ + in_vc_files='\.c$$' \ + halt='the above files do not include ' \ + $(_sc_search_regexp) + +# You must include before including any other header file. +# This can possibly be via a package-specific header, if given by cfg.mk. +sc_require_config_h_first: + @if $(VC_LIST_EXCEPT) | grep -l '\.c$$' > /dev/null; then \ + fail=0; \ + for i in $$($(VC_LIST_EXCEPT) | grep '\.c$$'); do \ + grep '^# *include\>' $$i | sed 1q \ + | grep -E '^# *include $(config_h_header)' > /dev/null \ + || { echo $$i; fail=1; }; \ + done; \ + test $$fail = 1 && \ + { echo '$(ME): the above files include some other header' \ + 'before ' 1>&2; exit 1; } || :; \ + else :; \ + fi + +sc_prohibit_HAVE_MBRTOWC: + @prohibit='\bHAVE_MBRTOWC\b' \ + halt="do not use $$prohibit; it is always defined" \ + $(_sc_search_regexp) + +# To use this "command" macro, you must first define two shell variables: +# h: the header name, with no enclosing <> or "" +# re: a regular expression that matches IFF something provided by $h is used. +define _sc_header_without_use + dummy=; : so we do not need a semicolon before each use; \ + h_esc=`echo '[<"]'"$$h"'[">]'|sed 's/\./\\\\./g'`; \ + if $(VC_LIST_EXCEPT) | grep -l '\.c$$' > /dev/null; then \ + files=$$(grep -l '^# *include '"$$h_esc" \ + $$($(VC_LIST_EXCEPT) | grep '\.c$$')) && \ + grep -LE "$$re" $$files | grep . && \ + { echo "$(ME): the above files include $$h but don't use it" \ + 1>&2; exit 1; } || :; \ + else :; \ + fi +endef + +# Prohibit the inclusion of assert.h without an actual use of assert. +sc_prohibit_assert_without_use: + @h='assert.h' re='\new(file => "/dev/stdin")->as_string'|sed 's/\?://g' +# Note this was produced by the above: +# _xa1 = \ +#x(((2n?)?re|c(har)?|n(re|m)|z)alloc|alloc_(oversized|die)|m(alloc|emdup)|strdup) +# But we can do better, in at least two ways: +# 1) take advantage of two "dup"-suffixed strings: +# x(((2n?)?re|c(har)?|n(re|m)|[mz])alloc|alloc_(oversized|die)|(mem|str)dup) +# 2) notice that "c(har)?|[mz]" is equivalent to the shorter and more readable +# "char|[cmz]" +# x(((2n?)?re|char|n(re|m)|[cmz])alloc|alloc_(oversized|die)|(mem|str)dup) +_xa1 = x(((2n?)?re|char|n(re|m)|[cmz])alloc|alloc_(oversized|die)|(mem|str)dup) +_xa2 = X([CZ]|N?M)ALLOC +sc_prohibit_xalloc_without_use: + @h='xalloc.h' \ + re='\<($(_xa1)|$(_xa2)) *\('\ + $(_sc_header_without_use) + +# Extract function names: +# perl -lne '/^(?:extern )?(?:void|char) \*?(\w+) *\(/ and print $1' lib/hash.h +_hash_re = \ +clear|delete|free|get_(first|next)|insert|lookup|print_statistics|reset_tuning +_hash_fn = \<($(_hash_re)) *\( +_hash_struct = (struct )?\<[Hh]ash_(table|tuning)\> +sc_prohibit_hash_without_use: + @h='hash.h' \ + re='$(_hash_fn)|$(_hash_struct)'\ + $(_sc_header_without_use) + +sc_prohibit_cloexec_without_use: + @h='cloexec.h' re='\<(set_cloexec_flag|dup_cloexec) *\(' \ + $(_sc_header_without_use) + +sc_prohibit_posixver_without_use: + @h='posixver.h' re='\' \ + halt='do not use HAVE''_FCNTL_H or O'_NDELAY \ + $(_sc_search_regexp) + +# FIXME: warn about definitions of EXIT_FAILURE, EXIT_SUCCESS, STREQ + +# Each nonempty ChangeLog line must start with a year number, or a TAB. +sc_changelog: + @prohibit='^[^12 ]' \ + in_vc_files='^ChangeLog$$' \ + halt='found unexpected prefix in a ChangeLog' \ + $(_sc_search_regexp) + +# Ensure that each .c file containing a "main" function also +# calls set_program_name. +sc_program_name: + @require='set_program_name *\(m?argv\[0\]\);' \ + in_vc_files='\.c$$' \ + containing='\
/dev/null \ + && : || { die=1; echo $$i; } \ + done; \ + test $$die = 1 && \ + { echo 1>&2 '$(ME): the final line in each of the above is not:'; \ + echo 1>&2 'Exit something'; \ + exit 1; } || :; \ + fi + +sc_trailing_blank: + @prohibit='[ ]$$' \ + halt='found trailing blank(s)' \ + exclude='^Binary file .* matches$$' \ + $(_sc_search_regexp) + +# Match lines like the following, but where there is only one space +# between the options and the description: +# -D, --all-repeated[=delimit-method] print all duplicate lines\n +longopt_re = --[a-z][0-9A-Za-z-]*(\[?=[0-9A-Za-z-]*\]?)? +sc_two_space_separator_in_usage: + @prohibit='^ *(-[A-Za-z],)? $(longopt_re) [^ ].*\\$$' \ + halt='help2man requires at least two spaces between an option and its description'\ + $(_sc_search_regexp) + +# A regexp matching function names like "error_" that may be used +# to emit translatable messages. +_gl_translatable_diag_func_re ?= error_ + +# Look for diagnostics that aren't marked for translation. +# This won't find any for which error's format string is on a separate line. +sc_unmarked_diagnostics: + @prohibit='\<$(_gl_translatable_diag_func_re) *\([^"]*"[^"]*[a-z]{3}' \ + exclude='(_|ngettext ?)\(' \ + halt='found unmarked diagnostic(s)' \ + $(_sc_search_regexp) + +# Avoid useless parentheses like those in this example: +# #if defined (SYMBOL) || defined (SYM2) +sc_useless_cpp_parens: + @prohibit='^# *if .*defined *\(' \ + halt='found useless parentheses in cpp directive' \ + $(_sc_search_regexp) + +# List headers for which HAVE_HEADER_H is always true, assuming you are +# using the appropriate gnulib module. CAUTION: for each "unnecessary" +# #if HAVE_HEADER_H that you remove, be sure that your project explicitly +# requires the gnulib module that guarantees the usability of that header. +gl_assured_headers_ = \ + cd $(gnulib_dir)/lib && echo *.in.h|sed 's/\.in\.h//g' + +# Convert the list of names to upper case, and replace each space with "|". +az_ = abcdefghijklmnopqrstuvwxyz +AZ_ = ABCDEFGHIJKLMNOPQRSTUVWXYZ +gl_header_upper_case_or_ = \ + $$($(gl_assured_headers_) \ + | tr $(az_)/.- $(AZ_)___ \ + | tr -s ' ' '|' \ + ) +sc_prohibit_always_true_header_tests: + @or=$(gl_header_upper_case_or_); \ + re="HAVE_($$or)_H"; \ + prohibit='\<'"$$re"'\>' \ + halt=$$(printf '%s\n' \ + 'do not test the above HAVE_
_H symbol(s);' \ + ' with the corresponding gnulib module, they are always true') \ + $(_sc_search_regexp) + +sc_prohibit_defined_have_decl_tests: + @prohibit='#[ ]*if(n?def|.*\[ (]+HAVE_DECL_' \ + halt='HAVE_DECL macros are always defined' \ + $(_sc_search_regexp) + +# ================================================================== +gl_other_headers_ ?= \ + intprops.h \ + openat.h \ + stat-macros.h + +# Perl -lne code to extract "significant" cpp-defined symbols from a +# gnulib header file, eliminating a few common false-positives. +# The exempted names below are defined only conditionally in gnulib, +# and hence sometimes must/may be defined in application code. +gl_extract_significant_defines_ = \ + /^\# *define ([^_ (][^ (]*)(\s*\(|\s+\w+)/\ + && $$2 !~ /(?:rpl_|_used_without_)/\ + && $$1 !~ /^(?:NSIG|ENODATA)$$/\ + && $$1 !~ /^(?:SA_RESETHAND|SA_RESTART)$$/\ + and print $$1 + +# Create a list of regular expressions matching the names +# of macros that are guaranteed to be defined by parts of gnulib. +define def_sym_regex + gen_h=$(gl_generated_headers_); \ + (cd $(gnulib_dir)/lib; \ + for f in *.in.h $(gl_other_headers_); do \ + test -f $$f \ + && perl -lne '$(gl_extract_significant_defines_)' $$f; \ + done; \ + ) | sort -u \ + | sed 's/^/^ *# *(define|undef) */;s/$$/\\>/' +endef + +# Don't define macros that we already get from gnulib header files. +sc_prohibit_always-defined_macros: + @if test -d $(gnulib_dir); then \ + case $$(echo all: | grep -l -f - Makefile) in Makefile);; *) \ + echo '$(ME): skipping $@: you lack GNU grep' 1>&2; exit 0;; \ + esac; \ + $(def_sym_regex) | grep -E -f - $$($(VC_LIST_EXCEPT)) \ + && { echo '$(ME): define the above via some gnulib .h file' \ + 1>&2; exit 1; } || :; \ + fi +# ================================================================== + +# Prohibit checked in backup files. +sc_prohibit_backup_files: + @$(VC_LIST) | grep '~$$' && \ + { echo '$(ME): found version controlled backup file' 1>&2; \ + exit 1; } || : + +# Require the latest GPL. +sc_GPL_version: + @prohibit='either ''version [^3]' \ + halt='GPL vN, N!=3' \ + $(_sc_search_regexp) + +# Require the latest GFDL. Two regexp, since some .texi files end up +# line wrapping between 'Free Documentation License,' and 'Version'. +_GFDL_regexp = (Free ''Documentation.*Version 1\.[^3]|Version 1\.[^3] or any) +sc_GFDL_version: + @prohibit='$(_GFDL_regexp)' \ + halt='GFDL vN, N!=3' \ + $(_sc_search_regexp) + +# Don't use Texinfo's @acronym{}. +# http://lists.gnu.org/archive/html/bug-gnulib/2010-03/msg00321.html +texinfo_suffix_re_ ?= \.(txi|texi(nfo)?)$$ +sc_texinfo_acronym: + @prohibit='@acronym\{' \ + in_vc_files='$(texinfo_suffix_re_)' \ + halt='found use of Texinfo @acronym{}' \ + $(_sc_search_regexp) + +cvs_keywords = \ + Author|Date|Header|Id|Name|Locker|Log|RCSfile|Revision|Source|State + +sc_prohibit_cvs_keyword: + @prohibit='\$$($(cvs_keywords))\$$' \ + halt='do not use CVS keyword expansion' \ + $(_sc_search_regexp) + +# This Perl code is slightly obfuscated. Not only is each "$" doubled +# because it's in a Makefile, but the $$c's are comments; we cannot +# use "#" due to the way the script ends up concatenated onto one line. +# It would be much more concise, and would produce better output (including +# counts) if written as: +# perl -ln -0777 -e '/\n(\n+)$/ and print "$ARGV: ".length $1' ... +# but that would be far less efficient, reading the entire contents +# of each file, rather than just the last two bytes of each. +# In addition, while the code below detects both blank lines and a missing +# newline at EOF, the above detects only the former. +# +# This is a perl script that is expected to be the single-quoted argument +# to a command-line "-le". The remaining arguments are file names. +# Print the name of each file that does not end in exactly one newline byte. +# I.e., warn if there are blank lines (2 or more newlines), or if the +# last byte is not a newline. However, currently we don't complain +# about any file that contains exactly one byte. +# Exit nonzero if at least one such file is found, otherwise, exit 0. +# Warn about, but otherwise ignore open failure. Ignore seek/read failure. +# +# Use this if you want to remove trailing empty lines from selected files: +# perl -pi -0777 -e 's/\n\n+$/\n/' files... +# +require_exactly_one_NL_at_EOF_ = \ + foreach my $$f (@ARGV) \ + { \ + open F, "<", $$f or (warn "failed to open $$f: $$!\n"), next; \ + my $$p = sysseek (F, -2, 2); \ + my $$c = "seek failure probably means file has < 2 bytes; ignore"; \ + my $$last_two_bytes; \ + defined $$p and $$p = sysread F, $$last_two_bytes, 2; \ + close F; \ + $$c = "ignore read failure"; \ + $$p && ($$last_two_bytes eq "\n\n" \ + || substr ($$last_two_bytes,1) ne "\n") \ + and (print $$f), $$fail=1; \ + } \ + END { exit defined $$fail } +sc_prohibit_empty_lines_at_EOF: + @perl -le '$(require_exactly_one_NL_at_EOF_)' $$($(VC_LIST_EXCEPT)) \ + || { echo '$(ME): empty line(s) or no newline at EOF' \ + 1>&2; exit 1; } || : + +# Make sure we don't use st_blocks. Use ST_NBLOCKS instead. +# This is a bit of a kludge, since it prevents use of the string +# even in comments, but for now it does the job with no false positives. +sc_prohibit_stat_st_blocks: + @prohibit='[.>]st_blocks' \ + halt='do not use st_blocks; use ST_NBLOCKS' \ + $(_sc_search_regexp) + +# Make sure we don't define any S_IS* macros in src/*.c files. +# They're already defined via gnulib's sys/stat.h replacement. +sc_prohibit_S_IS_definition: + @prohibit='^ *# *define *S_IS' \ + halt='do not define S_IS* macros; include ' \ + $(_sc_search_regexp) + +# Perl block to convert a match to FILE_NAME:LINENO:TEST, +# that is shared by two definitions below. +perl_filename_lineno_text_ = \ + -e ' {' \ + -e ' $$n = ($$` =~ tr/\n/\n/ + 1);' \ + -e ' ($$v = $$&) =~ s/\n/\\n/g;' \ + -e ' print "$$ARGV:$$n:$$v\n";' \ + -e ' }' + +prohibit_doubled_word_RE_ ?= \ + /\b(then?|[iao]n|i[fst]|but|f?or|at|and|[dt]o)\s+\1\b/gims +prohibit_doubled_word_ = \ + -e 'while ($(prohibit_doubled_word_RE_))' \ + $(perl_filename_lineno_text_) + +# Define this to a regular expression that matches +# any filename:dd:match lines you want to ignore. +# The default is to ignore no matches. +ignore_doubled_word_match_RE_ ?= ^$$ + +sc_prohibit_doubled_word: + @perl -n -0777 $(prohibit_doubled_word_) $$($(VC_LIST_EXCEPT)) \ + | grep -vE '$(ignore_doubled_word_match_RE_)' \ + | grep . && { echo '$(ME): doubled words' 1>&2; exit 1; } || : + +# A regular expression matching undesirable combinations of words like +# "can not"; this matches them even when the two words appear on different +# lines, but not when there is an intervening delimiter like "#" or "*". +# Similarly undesirable, "See @xref{...}", since an @xref should start +# a sentence. Explicitly prohibit any prefix of "see" or "also". +# Also prohibit a prefix matching "\w+ +". +# @pxref gets the same see/also treatment and should be parenthesized; +# presume it must *not* start a sentence. +bad_xref_re_ ?= (?:[\w,:;] +|(?:see|also)\s+)\@xref\{ +bad_pxref_re_ ?= (?:[.!?]|(?:see|also))\s+\@pxref\{ +prohibit_undesirable_word_seq_RE_ ?= \ + /(?:\bcan\s+not\b|$(bad_xref_re_)|$(bad_pxref_re_))/gims +prohibit_undesirable_word_seq_ = \ + -e 'while ($(prohibit_undesirable_word_seq_RE_))' \ + $(perl_filename_lineno_text_) +# Define this to a regular expression that matches +# any filename:dd:match lines you want to ignore. +# The default is to ignore no matches. +ignore_undesirable_word_sequence_RE_ ?= ^$$ + +sc_prohibit_undesirable_word_seq: + @perl -n -0777 $(prohibit_undesirable_word_seq_) \ + $$($(VC_LIST_EXCEPT)) \ + | grep -vE '$(ignore_undesirable_word_sequence_RE_)' | grep . \ + && { echo '$(ME): undesirable word sequence' >&2; exit 1; } || : + +_ptm1 = use "test C1 && test C2", not "test C1 -''a C2" +_ptm2 = use "test C1 || test C2", not "test C1 -''o C2" +# Using test's -a and -o operators is not portable. +# We prefer test over [, since the latter is spelled [[ in configure.ac. +sc_prohibit_test_minus_ao: + @prohibit='(\ /dev/null \ + || { fail=1; echo 1>&2 "$(ME): $$p uses proper_name_utf8"; }; \ + done; \ + test $$fail = 1 && \ + { echo 1>&2 '$(ME): the above do not link with any ICONV library'; \ + exit 1; } || :; \ + fi + +# Warn about "c0nst struct Foo const foo[]", +# but not about "char const *const foo" or "#define const const". +sc_redundant_const: + @prohibit='\bconst\b[[:space:][:alnum:]]{2,}\bconst\b' \ + halt='redundant "const" in declarations' \ + $(_sc_search_regexp) + +sc_const_long_option: + @prohibit='^ *static.*struct option ' \ + exclude='const struct option|struct option const' \ + halt='add "const" to the above declarations' \ + $(_sc_search_regexp) + +# Ensure that we don't accidentally insert an entry into an old NEWS block. +sc_immutable_NEWS: + @if test -f $(srcdir)/NEWS; then \ + test "$(NEWS_hash)" = '$(old_NEWS_hash)' && : || \ + { echo '$(ME): you have modified old NEWS' 1>&2; exit 1; }; \ + fi + +# Ensure that we use only the standard $(VAR) notation, +# not @...@ in Makefile.am, now that we can rely on automake +# to emit a definition for each substituted variable. +# However, there is still one case in which @VAR@ use is not just +# legitimate, but actually required: when augmenting an automake-defined +# variable with a prefix. For example, gettext uses this: +# MAKEINFO = env LANG= LC_MESSAGES= LC_ALL= LANGUAGE= @MAKEINFO@ +# otherwise, makeinfo would put German or French (current locale) +# navigation hints in the otherwise-English documentation. +# +# Allow the package to add exceptions via a hook in cfg.mk; +# for example, @PRAGMA_SYSTEM_HEADER@ can be permitted by +# setting this to ' && !/PRAGMA_SYSTEM_HEADER/'. +_makefile_at_at_check_exceptions ?= +sc_makefile_at_at_check: + @perl -ne '/\@\w+\@/' \ + -e ' && !/(\w+)\s+=.*\@\1\@$$/' \ + -e ''$(_makefile_at_at_check_exceptions) \ + -e 'and (print "$$ARGV:$$.: $$_"), $$m=1; END {exit !$$m}' \ + $$($(VC_LIST_EXCEPT) | grep -E '(^|/)(Makefile\.am|[^/]+\.mk)$$') \ + && { echo '$(ME): use $$(...), not @...@' 1>&2; exit 1; } || : + +sc_makefile_TAB_only_indentation: + @prohibit='^ [ ]{8}' \ + in_vc_files='akefile|\.mk$$' \ + halt='found TAB-8-space indentation' \ + $(_sc_search_regexp) + +sc_m4_quote_check: + @prohibit='(AC_DEFINE(_UNQUOTED)?|AC_DEFUN)\([^[]' \ + in_vc_files='(^configure\.ac|\.m4)$$' \ + halt='quote the first arg to AC_DEF*' \ + $(_sc_search_regexp) + +fix_po_file_diag = \ +'you have changed the set of files with translatable diagnostics;\n\ +apply the above patch\n' + +# Verify that all source files using _() (more specifically, files that +# match $(_gl_translatable_string_re)) are listed in po/POTFILES.in. +po_file ?= $(srcdir)/po/POTFILES.in +generated_files ?= $(srcdir)/lib/*.[ch] +_gl_translatable_string_re ?= \b(N?_|gettext *)\([^)"]*("|$$) +sc_po_check: + @if test -f $(po_file); then \ + grep -E -v '^(#|$$)' $(po_file) \ + | grep -v '^src/false\.c$$' | sort > $@-1; \ + files=; \ + for file in $$($(VC_LIST_EXCEPT)) $(generated_files); do \ + test -r $$file || continue; \ + case $$file in \ + *.m4|*.mk) continue ;; \ + *.?|*.??) ;; \ + *) continue;; \ + esac; \ + case $$file in \ + *.[ch]) \ + base=`expr " $$file" : ' \(.*\)\..'`; \ + { test -f $$base.l || test -f $$base.y; } && continue;; \ + esac; \ + files="$$files $$file"; \ + done; \ + grep -E -l '$(_gl_translatable_string_re)' $$files \ + | sed 's|^$(_dot_escaped_srcdir)/||' | sort -u > $@-2; \ + diff -u -L $(po_file) -L $(po_file) $@-1 $@-2 \ + || { printf '$(ME): '$(fix_po_file_diag) 1>&2; exit 1; }; \ + rm -f $@-1 $@-2; \ + fi + +# Sometimes it is useful to change the PATH environment variable +# in Makefiles. When doing so, it's better not to use the Unix-centric +# path separator of ':', but rather the automake-provided '$(PATH_SEPARATOR)'. +msg = 'Do not use ":" above; use $$(PATH_SEPARATOR) instead' +sc_makefile_path_separator_check: + @prohibit='PATH[=].*:' \ + in_vc_files='akefile|\.mk$$' \ + halt=$(msg) \ + $(_sc_search_regexp) + +v_etc_file = $(gnulib_dir)/lib/version-etc.c +sample-test = tests/sample-test +texi = doc/$(PACKAGE).texi +# Make sure that the copyright date in $(v_etc_file) is up to date. +# Do the same for the $(sample-test) and the main doc/.texi file. +sc_copyright_check: + @require='enum { COPYRIGHT_YEAR = '$$(date +%Y)' };' \ + in_files=$(v_etc_file) \ + halt='out of date copyright in $(v_etc_file); update it' \ + $(_sc_search_regexp) + @require='# Copyright \(C\) '$$(date +%Y)' Free' \ + in_vc_files=$(sample-test) \ + halt='out of date copyright in $(sample-test); update it' \ + $(_sc_search_regexp) + @require='Copyright @copyright\{\} .*'$$(date +%Y)' Free' \ + in_vc_files=$(texi) \ + halt='out of date copyright in $(texi); update it' \ + $(_sc_search_regexp) + +# #if HAVE_... will evaluate to false for any non numeric string. +# That would be flagged by using -Wundef, however gnulib currently +# tests many undefined macros, and so we can't enable that option. +# So at least preclude common boolean strings as macro values. +sc_Wundef_boolean: + @prohibit='^#define.*(yes|no|true|false)$$' \ + in_files='$(CONFIG_INCLUDE)' \ + halt='Use 0 or 1 for macro values' \ + $(_sc_search_regexp) + +# Even if you use pathmax.h to guarantee that PATH_MAX is defined, it might +# not be constant, or might overflow a stack. In general, use PATH_MAX as +# a limit, not an array or alloca size. +sc_prohibit_path_max_allocation: + @prohibit='(\balloca *\([^)]*|\[[^]]*)\bPATH_MAX' \ + halt='Avoid stack allocations of size PATH_MAX' \ + $(_sc_search_regexp) + +sc_vulnerable_makefile_CVE-2009-4029: + @prohibit='perm -777 -exec chmod a\+rwx|chmod 777 \$$\(distdir\)' \ + in_files='(^|/)Makefile\.in$$' \ + halt=$$(printf '%s\n' \ + 'the above files are vulnerable; beware of running' \ + ' "make dist*" rules, and upgrade to fixed automake' \ + ' see http://bugzilla.redhat.com/542609 for details') \ + $(_sc_search_regexp) + +sc_vulnerable_makefile_CVE-2012-3386: + @prohibit='chmod a\+w \$$\(distdir\)' \ + in_files='(^|/)Makefile\.in$$' \ + halt=$$(printf '%s\n' \ + 'the above files are vulnerable; beware of running' \ + ' "make distcheck", and upgrade to fixed automake' \ + ' see http://bugzilla.redhat.com/CVE-2012-3386 for details') \ + $(_sc_search_regexp) + + +## ------------- ## +## Distribution. ## +## ------------- ## + +EXTRA_DIST += \ + $(_build-aux)/sanity.mk \ + $(NOTHING_ELSE) diff --git a/build-aux/specl.mk b/build-aux/specl.mk new file mode 100644 index 0000000..530d961 --- /dev/null +++ b/build-aux/specl.mk @@ -0,0 +1,71 @@ +# Slingshot specl rules for make. + +# This file is distributed with Slingshot, and licensed under the +# terms of the MIT license reproduced below. + +# ==================================================================== # +# Copyright (C) 2013-2015 Gary V. Vaughan # +# # +# 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. # +# ==================================================================== # + +# To use this file create a list of your spec files in specl_SPECS +# and then include this make fragment. + +## ------ ## +## Specl. ## +## ------ ## + +CHECK_ENV += \ + LUA='$(LUA)' \ + PACKAGE_STRING='$(PACKAGE_STRING)' \ + abs_top_builddir='$(abs_top_builddir)' \ + abs_top_srcdir='$(abs_top_srcdir)' \ + top_builddir='$(top_builddir)' \ + top_srcdir='$(top_srcdir)' \ + $(NOTHING_ELSE) + +check_local += specl-check-local +specl-check-local: $(specl_SPECS) + $(CHECK_ENV) $(SPECL_ENV) $(SPECL) $(SPECL_OPTS) $(specl_SPECS) + + +## ------------- ## +## Installation. ## +## ------------- ## + +INSTALLCHECK_ENV += \ + LUA='$(LUA)' \ + PACKAGE_STRING='$(PACKAGE_STRING)' \ + installcheck='true' \ + $(NOTHING_ELSE) + +installcheck_local += specl-installcheck-local +specl-installcheck-local: $(specl_SPECS) + $(INSTALLCHECK_ENV) $(SPECL_ENV) $(SPECL) $(SPECL_OPTS) $(specl_SPECS) + + +## ------------- ## +## Distribution. ## +## ------------- ## + +EXTRA_DIST += \ + $(specl_SPECS) \ + $(NOTHING_ELSE) diff --git a/configure b/configure new file mode 100755 index 0000000..3cb1dc0 --- /dev/null +++ b/configure @@ -0,0 +1,4242 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for stdlib 41.2.2. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: http://github.com/lua-stdlib/lua-stdlib/issues about +$0: your system, including any error possibly output before +$0: this message. Then install a modern shell, or manually +$0: run the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='stdlib' +PACKAGE_TARNAME='stdlib' +PACKAGE_VERSION='41.2.2' +PACKAGE_STRING='stdlib 41.2.2' +PACKAGE_BUGREPORT='http://github.com/lua-stdlib/lua-stdlib/issues' +PACKAGE_URL='' + +ac_subst_vars='LTLIBOBJS +LIBOBJS +EGREP +SPECL +LDOC +pkgluaexecdir +luaexecdir +pkgluadir +luadir +LUA_EXEC_PREFIX +LUA_PREFIX +LUA_PLATFORM +LUA_SHORT_VERSION +LUA_VERSION +LUA +SED +GREP +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_silent_rules +' + ac_precious_vars='build_alias +host_alias +target_alias +LUA' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures stdlib 41.2.2 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/stdlib] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of stdlib 41.2.2:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + +Some influential environment variables: + LUA The Lua interpreter, e.g. /usr/bin/lua5.1 + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +stdlib configure 41.2.2 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by stdlib $as_me 41.2.2, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_aux_dir= +for ac_dir in build-aux "$srcdir"/build-aux; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in build-aux \"$srcdir\"/build-aux" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + + +$as_echo "## ------------------------- ## +## Configuring stdlib 41.2.2 ## +## ------------------------- ##" +echo + +am__api_version='1.15' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='stdlib' + VERSION='41.2.2' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=0;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + + + + + + + + + + if test "x$LUA" != 'x'; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $LUA is a Lua interpreter" >&5 +$as_echo_n "checking if $LUA is a Lua interpreter... " >&6; } + + _ax_lua_factorial=`$LUA 2>/dev/null -e ' + -- a simple factorial + function fact (n) + if n == 0 then + return 1 + else + return n * fact(n-1) + end + end + print("fact(5) is " .. fact(5))'` + if test "$_ax_lua_factorial" = 'fact(5) is 120'; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "not a Lua interpreter" "$LINENO" 5 + +fi + + _ax_check_text="whether $LUA version >= 5.1, < 5.5" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking $_ax_check_text" >&5 +$as_echo_n "checking $_ax_check_text... " >&6; } + + _ax_lua_good_version=`$LUA -e ' + -- a script to compare versions + function verstr2num(verstr) + local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)") + if majorver and minorver then + return tonumber(majorver) * 100 + tonumber(minorver) + end + end + local minver = verstr2num("5.1") + local _, _, trimver = string.find(_VERSION, "^Lua (.*)") + local ver = verstr2num(trimver) + local maxver = verstr2num("5.5") or 1e9 + if minver <= ver and ver < maxver then + print("yes") + else + print("no") + end'` + if test "x$_ax_lua_good_version" = "xyes"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + as_fn_error $? "version is out of range for specified LUA" "$LINENO" 5 +fi + + ax_display_LUA=$LUA + +else + _ax_check_text="for a Lua interpreter with version >= 5.1, < 5.5" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking $_ax_check_text" >&5 +$as_echo_n "checking $_ax_check_text... " >&6; } +if ${ax_cv_pathless_LUA+:} false; then : + $as_echo_n "(cached) " >&6 +else + for ax_cv_pathless_LUA in lua lua5.2 lua52 lua5.1 lua51 lua50 none; do + test "x$ax_cv_pathless_LUA" = 'xnone' && break + + _ax_lua_factorial=`$ax_cv_pathless_LUA 2>/dev/null -e ' + -- a simple factorial + function fact (n) + if n == 0 then + return 1 + else + return n * fact(n-1) + end + end + print("fact(5) is " .. fact(5))'` + if test "$_ax_lua_factorial" = 'fact(5) is 120'; then : + +else + continue +fi + + + _ax_lua_good_version=`$ax_cv_pathless_LUA -e ' + -- a script to compare versions + function verstr2num(verstr) + local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)") + if majorver and minorver then + return tonumber(majorver) * 100 + tonumber(minorver) + end + end + local minver = verstr2num("5.1") + local _, _, trimver = string.find(_VERSION, "^Lua (.*)") + local ver = verstr2num(trimver) + local maxver = verstr2num("5.5") or 1e9 + if minver <= ver and ver < maxver then + print("yes") + else + print("no") + end'` + if test "x$_ax_lua_good_version" = "xyes"; then : + break +fi + + done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_pathless_LUA" >&5 +$as_echo "$ax_cv_pathless_LUA" >&6; } + if test "x$ax_cv_pathless_LUA" = 'xnone'; then : + LUA=':' +else + # Extract the first word of "$ax_cv_pathless_LUA", so it can be a program name with args. +set dummy $ax_cv_pathless_LUA; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_LUA+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $LUA in + [\\/]* | ?:[\\/]*) + ac_cv_path_LUA="$LUA" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_LUA="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +LUA=$ac_cv_path_LUA +if test -n "$LUA"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LUA" >&5 +$as_echo "$LUA" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi + ax_display_LUA=$ax_cv_pathless_LUA + +fi + + + if test "x$LUA" = 'x:'; then : + as_fn_error $? "cannot find suitable Lua interpreter" "$LINENO" 5 + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ax_display_LUA version" >&5 +$as_echo_n "checking for $ax_display_LUA version... " >&6; } +if ${ax_cv_lua_version+:} false; then : + $as_echo_n "(cached) " >&6 +else + ax_cv_lua_version=`$LUA -e ' + -- return a version number in X.Y format + local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)") + print(ver)'` + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_lua_version" >&5 +$as_echo "$ax_cv_lua_version" >&6; } + if test "x$ax_cv_lua_version" = 'x'; then : + as_fn_error $? "invalid Lua version number" "$LINENO" 5 +fi + LUA_VERSION=$ax_cv_lua_version + + LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'` + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ax_display_LUA platform" >&5 +$as_echo_n "checking for $ax_display_LUA platform... " >&6; } +if ${ax_cv_lua_platform+:} false; then : + $as_echo_n "(cached) " >&6 +else + ax_cv_lua_platform=`$LUA -e 'print("unknown")'` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_lua_platform" >&5 +$as_echo "$ax_cv_lua_platform" >&6; } + LUA_PLATFORM=$ax_cv_lua_platform + + + LUA_PREFIX='${prefix}' + + LUA_EXEC_PREFIX='${exec_prefix}' + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ax_display_LUA script directory" >&5 +$as_echo_n "checking for $ax_display_LUA script directory... " >&6; } +if ${ax_cv_lua_luadir+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$prefix" = 'xNONE'; then : + ax_lua_prefix=$ac_default_prefix +else + ax_lua_prefix=$prefix +fi + + ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION" + + + + ax_lua_prefixed_path=`$LUA -e ' + -- get the path based on search type + local searchtype = "script" + local paths = "" + if searchtype == "script" then + paths = (package and package.path) or LUA_PATH + elseif searchtype == "module" then + paths = (package and package.cpath) or LUA_CPATH + end + -- search for the prefix + local prefix = "'$ax_lua_prefix'" + local minpath = "" + local mindepth = 1e9 + string.gsub(paths, "([^;]+)", + function (path) + path = string.gsub(path, "%?.*$", "") + path = string.gsub(path, "/[^/]*$", "") + if string.find(path, prefix) then + local depth = string.len(string.gsub(path, "[^/]", "")) + if depth < mindepth then + minpath = path + mindepth = depth + end + end + end) + print(minpath)'` + + if test "x$ax_lua_prefixed_path" != 'x'; then : + _ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'` + ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \ + $SED "s|^$_ax_strip_prefix|$LUA_PREFIX|"` + +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_lua_luadir" >&5 +$as_echo "$ax_cv_lua_luadir" >&6; } + luadir=$ax_cv_lua_luadir + + pkgluadir=\${luadir}/$PACKAGE + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ax_display_LUA module directory" >&5 +$as_echo_n "checking for $ax_display_LUA module directory... " >&6; } +if ${ax_cv_lua_luaexecdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$exec_prefix" = 'xNONE'; then : + ax_lua_exec_prefix=$ax_lua_prefix +else + ax_lua_exec_prefix=$exec_prefix +fi + + ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION" + + + + ax_lua_prefixed_path=`$LUA -e ' + -- get the path based on search type + local searchtype = "module" + local paths = "" + if searchtype == "script" then + paths = (package and package.path) or LUA_PATH + elseif searchtype == "module" then + paths = (package and package.cpath) or LUA_CPATH + end + -- search for the prefix + local prefix = "'$ax_lua_exec_prefix'" + local minpath = "" + local mindepth = 1e9 + string.gsub(paths, "([^;]+)", + function (path) + path = string.gsub(path, "%?.*$", "") + path = string.gsub(path, "/[^/]*$", "") + if string.find(path, prefix) then + local depth = string.len(string.gsub(path, "[^/]", "")) + if depth < mindepth then + minpath = path + mindepth = depth + end + end + end) + print(minpath)'` + + if test "x$ax_lua_prefixed_path" != 'x'; then : + _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'` + ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \ + $SED "s|^$_ax_strip_prefix|$LUA_EXEC_PREFIX|"` + +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_lua_luaexecdir" >&5 +$as_echo "$ax_cv_lua_luaexecdir" >&6; } + luaexecdir=$ax_cv_lua_luaexecdir + + pkgluaexecdir=\${luaexecdir}/$PACKAGE + + + + +fi + +# Extract the first word of "ldoc", so it can be a program name with args. +set dummy ldoc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_LDOC+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $LDOC in + [\\/]* | ?:[\\/]*) + ac_cv_path_LDOC="$LDOC" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_LDOC="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_LDOC" && ac_cv_path_LDOC=":" + ;; +esac +fi +LDOC=$ac_cv_path_LDOC +if test -n "$LDOC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LDOC" >&5 +$as_echo "$LDOC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +# Extract the first word of "specl", so it can be a program name with args. +set dummy specl; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_SPECL+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $SPECL in + [\\/]* | ?:[\\/]*) + ac_cv_path_SPECL="$SPECL" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_SPECL="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_SPECL" && ac_cv_path_SPECL=":" + ;; +esac +fi +SPECL=$ac_cv_path_SPECL +if test -n "$SPECL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPECL" >&5 +$as_echo "$SPECL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + + +ac_config_files="$ac_config_files Makefile build-aux/config.ld" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +ac_script=' +:mline +/\\$/{ + N + s,\\\n,, + b mline +} +t clear +:clear +s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g +t quote +s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g +t quote +b any +:quote +s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g +s/\[/\\&/g +s/\]/\\&/g +s/\$/$$/g +H +:any +${ + g + s/^\n// + s/\n/ /g + p +} +' +DEFS=`sed -n "$ac_script" confdefs.h` + + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by stdlib $as_me 41.2.2, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +stdlib config.status 41.2.2 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "build-aux/config.ld") CONFIG_FILES="$CONFIG_FILES build-aux/config.ld" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + + +eval set X " :F $CONFIG_FILES " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/configure.ac b/configure.ac index d6daabf..9511e38 100644 --- a/configure.ac +++ b/configure.ac @@ -1,18 +1,40 @@ -dnl Process this file with autoconf to produce a configure script +dnl configure.ac +dnl +dnl Copyright (C) 2012-2015 Gary V. Vaughan +dnl Written by Gary V. Vaughan, 2012 +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published +dnl by the Free Software Foundation; either version 3, or (at your +dnl option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program. If not, see . + dnl Initialise autoconf and automake -AC_INIT(stdlib, 30, rrt@sc3d.org) +AC_INIT([stdlib], [41.2.2], [http://github.com/lua-stdlib/lua-stdlib/issues]) AC_CONFIG_AUX_DIR([build-aux]) -AM_INIT_AUTOMAKE([foreign]) -AM_SILENT_RULES([yes]) +AC_CONFIG_MACRO_DIR([m4]) + +AS_BOX([Configuring AC_PACKAGE_TARNAME AC_PACKAGE_VERSION]) +echo -dnl Lua -AC_SUBST([LUA_MIN_VERSION], [5.1]) -AX_PROG_LUA([$LUA_MIN_VERSION], [5.3]) +AM_INIT_AUTOMAKE([-Wall]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -AX_WITH_PROG([LUADOC], [luadoc], [:]) +dnl Check for programs +AX_PROG_LUA([5.1], [5.5]) +AC_PATH_PROG([LDOC], [ldoc], [:]) +AC_PATH_PROG([SPECL], [specl], [:]) +AC_PROG_EGREP +AC_PROG_SED dnl Generate output files -AC_CONFIG_MACRO_DIR(m4) -AC_CONFIG_FILES([Makefile luarocks-config.lua]) +AC_CONFIG_FILES([Makefile build-aux/config.ld]) AC_OUTPUT diff --git a/doc/classes/std.container.html b/doc/classes/std.container.html new file mode 100644 index 0000000..acdfc83 --- /dev/null +++ b/doc/classes/std.container.html @@ -0,0 +1,179 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Class std.container

+

Container prototype.

+

+ + +

A container is a std.object with no methods. It's functionality is + instead defined by its metamethods.

+ +

Where an Object uses the __index metatable entry to hold object + methods, a Container stores its contents using __index, preventing + it from having methods in there too.

+ +

Although there are no actual methods, Containers are free to use + metamethods (__index, __sub, etc) and, like Objects, can supply + module functions by listing them in _functions. Also, since a + std.container is a std.object, it can be passed to the + std.object module functions, or anywhere else a std.object is + expected.

+ +

When making your own prototypes, derive from std.container if you want + to access the contents of your objects with the [] operator, or from + std.object if you want to access the functionality of your objects with + named object methods.

+ +

Prototype Chain

+ + +
+table
+ `-> Object
+      `-> Container
+
+ +

+ + +

Objects

+ + + + + +
std.container.ContainerContainer prototype.
+ +
+
+ + +

Objects

+ +
+
+ + std.container.Container +
+
+ Container prototype.

+ +

Container also inherits all the fields and methods from + std.object.Object. + + +

Fields:

+
    +
  • _type + string + object name + (default "Container") +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    + local std = require "std"
    + local Container = std.container {}
    +
    + local Graph = Container {
    +   _type = "Graph",
    +   _functions = {
    +     nodes = function (graph)
    +       local n = 0
    +       for _ in std.pairs (graph) do n = n + 1 end
    +       return n
    +     end,
    +   },
    + }
    + local g = Graph { "node1", "node2" }
    + --> 2
    + print (Graph.nodes (g))
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/classes/std.list.html b/doc/classes/std.list.html new file mode 100644 index 0000000..c29ce85 --- /dev/null +++ b/doc/classes/std.list.html @@ -0,0 +1,572 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Class std.list

+

Tables as lists.

+

+ + +

Prototype Chain

+ + +
+table
+ `-> Object
+      `-> List
+
+ +

+ + +

Objects

+ + + + + +
std.list.ListAn Object derived List.
+

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
std.list.append (l, x)Append an item to a list.
std.list.compare (l, m)Compare two lists element-by-element, from left-to-right.
std.list.concat (l, ...)Concatenate the elements from any number of lists.
std.list.cons (l, x)Prepend an item to a list.
std.list.rep (l, n)Repeat a list.
std.list.sub (l[, from=1[, to=#l]])Return a sub-range of a list.
std.list.tail (l)Return a list with its first element removed.
+

Metamethods

+ + + + + + + + + + + + + + + + + +
std.list:__add (l, e)Append element to list.
std.list:__concat (l, m)Concatenate lists.
std.list:__le (l, m)List equality or order operator.
std.list:__lt (l, m)List order operator.
+ +
+
+ + +

Objects

+ +
+
+ + std.list.List +
+
+ An Object derived List. + + + + + + + +
+
+

Functions

+ Methods +
+
+ + std.list.append (l, x) +
+
+ Append an item to a list. + + +

Parameters:

+
    +
  • l + List + a list +
  • +
  • x + item +
  • +
+ +

Returns:

+
    + + List + new list with x appended +
+ + + +

Usage:

+
    +
    longer = append (short, "last")
    +
+ +
+
+ + std.list.compare (l, m) +
+
+ Compare two lists element-by-element, from left-to-right. + + +

Parameters:

+
    +
  • l + List + a list +
  • +
  • m + List or table + another list, or table +
  • +
+ +

Returns:

+
    + + -1 if l is less than m, 0 if they are the same, and 1 + if l is greater than m +
+ + + +

Usage:

+
    +
    if a_list:compare (another_list) == 0 then print "same" end
    +
+ +
+
+ + std.list.concat (l, ...) +
+
+ Concatenate the elements from any number of lists. + + +

Parameters:

+
    +
  • l + List + a list +
  • +
  • ... + tuple of lists +
  • +
+ +

Returns:

+
    + + List + new list with elements from arguments +
+ + + +

Usage:

+
    +
    + --> {1, 2, 3, {4, 5}, 6, 7}
    + list.concat ({1, 2, 3}, {{4, 5}, 6, 7})
    +
+ +
+
+ + std.list.cons (l, x) +
+
+ Prepend an item to a list. + + +

Parameters:

+
    +
  • l + List + a list +
  • +
  • x + item +
  • +
+ +

Returns:

+
    + + List + new list with x followed by elements of l +
+ + + +

Usage:

+
    +
    + --> {"x", 1, 2, 3}
    + list.cons ({1, 2, 3}, "x")
    +
+ +
+
+ + std.list.rep (l, n) +
+
+ Repeat a list. + + +

Parameters:

+
    +
  • l + List + a list +
  • +
  • n + int + number of times to repeat +
  • +
+ +

Returns:

+
    + + List + n copies of l appended together +
+ + + +

Usage:

+
    +
    + --> {1, 2, 3, 1, 2, 3, 1, 2, 3}
    + list.rep ({1, 2, 3}, 3)
    +
+ +
+
+ + std.list.sub (l[, from=1[, to=#l]]) +
+
+ Return a sub-range of a list. + (The equivalent of ??? on strings; negative list indices + count from the end of the list.) + + +

Parameters:

+
    +
  • l + List + a list +
  • +
  • from + int + start of range + (default 1) +
  • +
  • to + int + end of range + (default #l) +
  • +
+ +

Returns:

+
    + + List + new list containing elements between from and to + inclusive +
+ + + +

Usage:

+
    +
    + --> {3, 4, 5}
    + list.sub ({1, 2, 3, 4, 5, 6}, 3, 5)
    +
+ +
+
+ + std.list.tail (l) +
+
+ Return a list with its first element removed. + + +

Parameters:

+
    +
  • l + List + a list +
  • +
+ +

Returns:

+
    + + List + new list with all but the first element of l +
+ + + +

Usage:

+
    +
    + --> {3, {4, 5}, 6, 7}
    + list.tail {{1, 2}, 3, {4, 5}, 6, 7}
    +
+ +
+
+

Metamethods

+ +
+
+ + std.list:__add (l, e) +
+
+ Append element to list. + + +

Parameters:

+
    +
  • l + List + a list +
  • +
  • e + element to append +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    list = list + "element"
    +
+ +
+
+ + std.list:__concat (l, m) +
+
+ Concatenate lists. + + +

Parameters:

+
    +
  • l + List + a list +
  • +
  • m + List or table + another list, or table (hash part is ignored) +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    new = alist .. {"append", "these", "elements"}
    +
+ +
+
+ + std.list:__le (l, m) +
+
+ List equality or order operator. + + +

Parameters:

+
    +
  • l + List + a list +
  • +
  • m + List + another list +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    min = list1 <= list2 and list1 or list2
    +
+ +
+
+ + std.list:__lt (l, m) +
+
+ List order operator. + + +

Parameters:

+
    +
  • l + List + a list +
  • +
  • m + List + another list +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    max = list1 > list2 and list1 or list2
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/classes/std.object.html b/doc/classes/std.object.html new file mode 100644 index 0000000..65dc357 --- /dev/null +++ b/doc/classes/std.object.html @@ -0,0 +1,511 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Class std.object

+

Prototype-based objects.

+

+ + +

This module creates the root prototype object from which every other + object is descended. There are no classes as such, rather new objects + are created by cloning an existing object, and then changing or adding + to the clone. Further objects can then be made by cloning the changed + object, and so on.

+ +

Objects are cloned by simply calling an existing object, which then + serves as a prototype from which the new object is copied.

+ +

Note that Object methods are stored in the __index field of their + metatable, and so cannot also use __index to lookup references with + square brackets. See std.container objects if you want to do that.

+ +

Prototype Chain

+ + +
+table
+ `-> Object
+
+ +

+ + +

Objects

+ + + + + +
std.object.ObjectRoot object.
+

Functions

+ + + + + + + + + + + + + +
std.object.clone (obj, ...)Clone an Object.
std.object.mapfields (obj, src[, map={}])Return obj with references to the fields of src merged in.
std.object.prototype (x)Type of an object, or primitive.
+

Metamethods

+ + + + + + + + + + + + + +
std.object:__call (...)Return a clone of this object, and its metatable.
std.object:__pairs ()Return an in-order iterator over public object fields.
std.object:__tostring ()Return a string representation of this object.
+ +
+
+ + +

Objects

+ +
+
+ + std.object.Object +
+
+ Root object.

+ +

Changing the values of these fields in a new object will change the + corresponding behaviour. + + +

Fields:

+
    +
  • _init + table or function + object initialisation + (default {}) +
  • +
  • _functions + table + module functions omitted when cloned +
  • +
  • _type + string + object name + (default "Object") +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
  • + -- `_init` can be a list of keys; then the unnamed `init_1` through
    + -- `init_m` values from the argument table are assigned to the
    + -- corresponding keys in `new_object`.
    + local Process = Object {
    +   _type = "Process",
    +   _init = { "status", "out", "err" },
    + }
    + local process = Process {
    +   procs[pid].status, procs[pid].out, procs[pid].err, -- auto assigned
    +   command = pipeline[pid],                           -- manual assignment
    + }
  • +
  • + -- Or it can be a function, in which the arguments passed to the
    + -- prototype during cloning are simply handed to the `_init` function.
    + local Bag = Object {
    +   _type = "Bag",
    +   _init = function (obj, ...)
    +     for e in std.elems {...} do
    +       obj[#obj + 1] = e
    +     end
    +     return obj
    +   end,
    + }
    + local bag = Bag ("function", "arguments", "sent", "to", "_init")
  • +
+ +
+
+

Functions

+ Methods +
+
+ + std.object.clone (obj, ...) +
+
+ Clone an Object.

+ +

Objects are essentially tables of field_n = value_n pairs.

+ +

Normally new_object automatically shares a metatable with + proto_object. However, field names beginning with "_" are private, + and moved into the object metatable during cloning. So, adding new + private fields to an object during cloning will result in a new + metatable for new_object that also happens to contain a copy of all + the entries from the proto_object metatable.

+ +

While clones of Object inherit all properties of their prototype, + it's idiomatic to always keep separate tables for the module table and + the root object itself: That way you can't mistakenly engage the slower + clone-from-module-table process unnecessarily. + + +

Parameters:

+
    +
  • obj + Object + an object +
  • +
  • ... + a list of arguments if obj._init is a function, or a + single table if obj._init is a table. +
  • +
+ +

Returns:

+
    + + Object + a clone of obj +
+ + +

See also:

+ + +

Usage:

+
    +
    + local object = require "std.object"  -- module table
    + local Object = object {}             -- root object
    + local o = Object {
    +   field_1 = "value_1",
    +   method_1 = function (self) return self.field_1 end,
    + }
    + print (o.field_1)                    --> value_1
    + o.field_2 = 2
    + function o:method_2 (n) return self.field_2 + n end
    + print (o:method_2 (2))               --> 4
    + os.exit (0)
    +
+ +
+
+ + std.object.mapfields (obj, src[, map={}]) +
+
+ Return obj with references to the fields of src merged in.

+ +

More importantly, split the fields in src between obj and its + metatable. If any field names begin with "_", attach a metatable + to obj by cloning the metatable from src, and then copy the + "private" _ prefixed fields there.

+ +

You might want to use this function to instantiate your derived + object clones when the src._init is a function -- when + src._init is a table, the default (inherited unless you overwrite + it) clone method calls mapfields automatically. When you're + using a function _init setting, clone doesn't know what to + copy into a new object from the _init function's arguments... + so you're on your own. Except that calling mapfields inside + _init is safer than manually splitting src into obj and + its metatable, because you'll pick up any fixes and changes when + you upgrade stdlib. + + +

Parameters:

+
    +
  • obj + table + destination object +
  • +
  • src + table + fields to copy int clone +
  • +
  • map + table + key renames as {old_key=new_key, ...} + (default {}) +
  • +
+ +

Returns:

+
    + + table + obj with non-private fields from src merged, + and a metatable with private fields (if any) merged, both sets + of keys renamed according to map +
+ + + +

Usage:

+
    +
    + myobject.mapfields = function (obj, src, map)
    +   object.mapfields (obj, src, map)
    +   ...
    + end
    +
+ +
+
+ + std.object.prototype (x) +
+
+ Type of an object, or primitive.

+ +

It's conventional to organise similar objects according to a + string valued _type field, which can then be queried using this + function.

+ +

Additionally, this function returns the results of ??? for + file objects, or type otherwise. + + +

Parameters:

+
    +
  • x + anything +
  • +
+ +

Returns:

+
    + + string + type of x +
+ + + +

Usage:

+
    +
    + local Stack = Object {
    +   _type = "Stack",
    +
    +   __tostring = function (self) ... end,
    +
    +   __index = {
    +     push = function (self) ... end,
    +     pop  = function (self) ... end,
    +   },
    + }
    + local stack = Stack {}
    + assert (stack:prototype () == getmetatable (stack)._type)
    +
    + local prototype = Object.prototype
    + assert (prototype (stack) == getmetatable (stack)._type)
    +
    + local h = io.open (os.tmpname (), "w")
    + assert (prototype (h) == io.type (h))
    +
    + assert (prototype {} == type {})
    +
+ +
+
+

Metamethods

+ +
+
+ + std.object:__call (...) +
+
+ Return a clone of this object, and its metatable.

+ +

Private fields are stored in the metatable. + + +

Parameters:

+
    +
  • ... + arguments for prototype's _init +
  • +
+ +

Returns:

+
    + + Object + a clone of the this object. +
+ + +

See also:

+ + +

Usage:

+
    +
    + local Object = require "std.object" {} -- not a typo!
    + new = Object {"initialisation", "elements"}
    +
+ +
+
+ + std.object:__pairs () +
+
+ Return an in-order iterator over public object fields. + + + +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + Object + self
  4. +
+ + + +

Usage:

+
    +
    for k, v in std.pairs (anobject) do process (k, v) end
    +
+ +
+
+ + std.object:__tostring () +
+
+ Return a string representation of this object.

+ +

First the object type, and then between { and } a list of the + array part of the object table (without numeric keys) followed + by the remaining key-value pairs.

+ +

This function doesn't recurse explicity, but relies upon suitable + __tostring metamethods in field values. + + + +

Returns:

+
    + + string + stringified object representation +
+ + +

See also:

+ + +

Usage:

+
    +
    print (anobject)
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/classes/std.optparse.html b/doc/classes/std.optparse.html new file mode 100644 index 0000000..757d8dc --- /dev/null +++ b/doc/classes/std.optparse.html @@ -0,0 +1,895 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Class std.optparse

+

Parse and process command line options.

+

+ + +

Prototype Chain

+ + +
+table
+ `-> Object
+      `-> OptionParser
+
+ +

+ + +

Objects

+ + + + + +
std.optparse.OptionParserOptionParser prototype object.
+

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
std.optparse.OptionParser_Init (spec)Signature for initialising a custom OptionParser.
std.optparse.boolean (opt[, optarg="1"])Return a Lua boolean equivalent of various optarg strings.
std.optparse.file (opt, optarg)Report an option parse error unless optarg names an + existing file.
std.optparse.finished (arglist, i)Finish option processing

+ +

This is the handler automatically assigned to the option written as + -- in the OptionParser spec argument.

std.optparse.flag (arglist, i[, value])Option at arglist[i] is a boolean switch.
std.optparse.help ()Option should display help text, then exit.
std.optparse.opterr (msg)Report an option parse error, then exit with status 2.
std.optparse.optional (arglist, i[, value=true])Option at arglist[i] can take an argument.
std.optparse.required (arglist, i[, value])Option at arglist[i} requires an argument.
std.optparse.version ()Option should display version text, then exit.
+

Tables

+ + + + + + + + + +
std.optparse.boolvalsMap various option strings to equivalent Lua boolean values.
std.optparse.optsParsed options table, with a key for each encountered option, each + with value set by that option's on_handler.
+

Methods

+ + + + + + + + + + + + + +
std.optparse:on (name, handler, value)Add an option handler.
std.optparse:on_handler (arglist, i[, value=nil])Function signature of an option handler for on.
std.optparse:parse (arglist[, defaults])Parse an argument list.
+ +
+
+ + +

Objects

+ +
+
+ + std.optparse.OptionParser +
+
+ OptionParser prototype object.

+ +

Most often, after instantiating an OptionParser, everything else + is handled automatically.

+ +

Then, calling parser:parse as shown below saves unparsed arguments + into _G.arg (usually filenames or similar), and _G.opts will be a + table of successfully parsed option values. The keys into this table + are the long-options with leading hyphens stripped, and non-word + characters turned to _. For example if --another-long had been + found in the initial _G.arg, then _G.opts will have a key named + another_long, with an appropriate value. If there is no long + option name, then the short option is used, i.e. _G.opts.b will be + set.

+ +

The values saved against those keys are controlled by the option + handler, usually just true or the option argument string as + appropriate. + + +

Fields:

+
    +
  • _init + OptionParser_Init + initialisation function +
  • +
  • program + string + the first word following "Usage:" from spec +
  • +
  • version + string + the last white-space delimited word on the first line + of text from spec +
  • +
  • versiontext + string + everything preceding "Usage:" from spec, and + which will be displayed by the version on_handler +
  • +
  • helptext + string + everything including and following "Usage:" from + spec string and which will be displayed by the help + on_handler +
  • +
+ + + + +

Usage:

+
    +
    + local std = require "std"
    +
    + local optparser = std.optparse [[
    + any text VERSION
    + Additional lines of text to show when the --version
    + option is passed.
    +
    + Several lines or paragraphs are permitted.
    +
    + Usage: PROGNAME
    +
    + Banner text.
    +
    + Optional long description text to show when the --help
    + option is passed.
    +
    + Several lines or paragraphs of long description are permitted.
    +
    + Options:
    +
    +   -b                       a short option with no long option
    +       --long               a long option with no short option
    +       --another-long       a long option with internal hypen
    +   -v, --verbose            a combined short and long option
    +   -n, --dryrun, --dry-run  several spellings of the same option
    +   -u, --name=USER          require an argument
    +   -o, --output=[FILE]      accept an optional argument
    +       --version            display version information, then exit
    +       --help               display this help, then exit
    +
    + Footer text.  Several lines or paragraphs are permitted.
    +
    + Please report bugs at bug-list@yourhost.com
    + ]]
    +
    + -- Note that std.io.die and std.io.warn will only prefix messages
    + -- with `parser.program` if the parser options are assigned back to
    + -- `_G.opts`:
    + _G.arg, _G.opts = optparser:parse (_G.arg)
    +
+ +
+
+

Functions

+ Methods +
+
+ + std.optparse.OptionParser_Init (spec) +
+
+ Signature for initialising a custom OptionParser.

+ +

Read the documented options from spec and return custom parser that + can be used for parsing the options described in spec from a run-time + argument list. Options in spec are recognised as lines that begin + with at least two spaces, followed by a hyphen. + + +

Parameters:

+
    +
  • spec + string + option parsing specification +
  • +
+ +

Returns:

+
    + + OptionParser + a parser for options described by spec +
+ + + +

Usage:

+
    +
    customparser = std.optparse (optparse_spec)
    +
+ +
+
+ + std.optparse.boolean (opt[, optarg="1"]) +
+
+ Return a Lua boolean equivalent of various optarg strings. + Report an option parse error if optarg is not recognised.

+ +

Pass this as the value function to on when you want various + "truthy" or "falsey" option arguments to be coerced to a Lua true + or false respectively in the options table. + + +

Parameters:

+
    +
  • opt + string + option name +
  • +
  • optarg + string + option argument, must be a key in boolvals + (default "1") +
  • +
+ +

Returns:

+
    + + bool + true or false +
+ + + +

Usage:

+
    +
    parser:on ("--enable-nls", parser.optional, parser.boolean)
    +
+ +
+
+ + std.optparse.file (opt, optarg) +
+
+ Report an option parse error unless optarg names an + existing file.

+ +

Pass this as the value function to on when you want to accept + only option arguments that name an existing file. + + +

Parameters:

+
    +
  • opt + string + option name +
  • +
  • optarg + string + option argument, must be an existing file +
  • +
+ +

Returns:

+
    + + string + optarg +
+ + + +

Usage:

+
    +
    parser:on ("--config-file", parser.required, parser.file)
    +
+ +
+
+ + std.optparse.finished (arglist, i) +
+
+ Finish option processing

+ +

This is the handler automatically assigned to the option written as + -- in the OptionParser spec argument. You can also pass it as + the handler argument to on if you want to manually add an end + of options marker without writing it in the OptionParser spec.

+ +

This handler tells the parser to stop processing arguments, so that + anything after it will be an argument even if it otherwise looks + like an option. + + +

Parameters:

+
    +
  • arglist + table + list of arguments +
  • +
  • i + int + index of last processed element of arglist +
  • +
+ +

Returns:

+
    + + int + index of next element of arglist to process +
+ + + +

Usage:

+
    +
    parser:on ("--", parser.finished)
    +
+ +
+
+ + std.optparse.flag (arglist, i[, value]) +
+
+ Option at arglist[i] is a boolean switch.

+ +

This is the handler automatically assigned to options that have + --long-opt or -x style specifications in the OptionParser spec + argument. You can also pass it as the handler argument to on for + options you want to add manually without putting them in the + OptionParser spec.

+ +

Beware that, unlike required, this handler will store multiple + occurrences of a command-line option as a table only when given a + value function. Automatically assigned handlers do not do this, so + the option will simply be true if the option was given one or more + times on the command-line. + + +

Parameters:

+
    +
  • arglist + table + list of arguments +
  • +
  • i + int + index of last processed element of arglist +
  • +
  • value + either a function to process the option argument, + or a value to store when this flag is encountered + (optional) +
  • +
+ +

Returns:

+
    + + int + index of next element of arglist to process +
+ + + +

Usage:

+
    +
    parser:on ({"--long-opt", "-x"}, parser.flag)
    +
+ +
+
+ + std.optparse.help () +
+
+ Option should display help text, then exit.

+ +

This is the handler automatically assigned tooptions that have + --help in the specification, e.g. -h, -?, --help. + + + + + + +

Usage:

+
    +
    parser:on ("-?", parser.version)
    +
+ +
+
+ + std.optparse.opterr (msg) +
+
+ Report an option parse error, then exit with status 2.

+ +

Use this in your custom option handlers for consistency with the + error output from built-in std.optparse error messages. + + +

Parameters:

+
    +
  • msg + string + error message +
  • +
+ + + + + +
+
+ + std.optparse.optional (arglist, i[, value=true]) +
+
+ Option at arglist[i] can take an argument. + Argument is accepted only if there is a following entry that does not + begin with a '-'.

+ +

This is the handler automatically assigned to options that have + --opt=[ARG] style specifications in the OptionParser spec + argument. You can also pass it as the handler argument to on for + options you want to add manually without putting them in the + OptionParser spec.

+ +

Like required, this handler will store multiple occurrences of a + command-line option. + + +

Parameters:

+
    +
  • arglist + table + list of arguments +
  • +
  • i + int + index of last processed element of arglist +
  • +
  • value + either a function to process the option + argument, or a default value if encountered without an optarg + (default true) +
  • +
+ +

Returns:

+
    + + int + index of next element of arglist to process +
+ + + +

Usage:

+
    +
    parser:on ("--enable-nls", parser.option, parser.boolean)
    +
+ +
+
+ + std.optparse.required (arglist, i[, value]) +
+
+ +

Option at arglist[i} requires an argument.

+ +

This is the handler automatically assigned to options that have + --opt=ARG style specifications in the OptionParser spec argument. + You can also pass it as the handler argument to on for options + you want to add manually without putting them in the OptionParser + spec.

+ +

Normally the value stored in the opt table by this handler will be + the string given as the argument to that option on the command line. + However, if the option is given on the command-line multiple times, + opt["name"] will end up with all those arguments stored in the + array part of a table:

+ +
 $ cat ./prog
+ ...
+ parser:on ({"-e", "-exec"}, required)
+ _G.arg, _G.opt = parser:parse (_G.arg)
+ print std.string.tostring (_G.opt.exec)
+ ...
+ $ ./prog -e '(foo bar)' -e '(foo baz)' -- qux
+ {1=(foo bar),2=(foo baz)}
+
+ + + +

Parameters:

+
    +
  • arglist + table + list of arguments +
  • +
  • i + int + index of last processed element of arglist +
  • +
  • value + either a function to process the option argument, + or a forced value to replace the user's option argument. + (optional) +
  • +
+ +

Returns:

+
    + + int + index of next element of arglist to process +
+ + + +

Usage:

+
    +
    parser:on ({"-o", "--output"}, parser.required)
    +
+ +
+
+ + std.optparse.version () +
+
+ Option should display version text, then exit.

+ +

This is the handler automatically assigned tooptions that have + --version in the specification, e.g. -V, --version. + + + + + + +

Usage:

+
    +
    parser:on ("-V", parser.version)
    +
+ +
+
+

Tables

+ +
+
+ + std.optparse.boolvals +
+
+ Map various option strings to equivalent Lua boolean values. + + +

Fields:

+
    +
  • false + false +
  • +
  • 0 + false +
  • +
  • no + false +
  • +
  • n + false +
  • +
  • true + true +
  • +
  • 1 + true +
  • +
  • yes + true +
  • +
  • y + true +
  • +
+ + + + + +
+
+ + std.optparse.opts +
+
+ Parsed options table, with a key for each encountered option, each + with value set by that option's on_handler. Where an option + has one or more long-options specified, the key will be the first + one of those with leading hyphens stripped and non-alphanumeric + characters replaced with underscores. For options that can only be + specified by a short option, the key will be the letter of the first + of the specified short options:

+ +
 {"-e", "--eval-file"} => opts.eval_file
+ {"-n", "--dryrun", "--dry-run"} => opts.dryrun
+ {"-t", "-T"} => opts.t
+
+ +

Generally there will be one key for each previously specified + option (either automatically assigned by OptionParser or + added manually with on) containing the value(s) assigned by the + associated on_handler. For automatically assigned handlers, + that means true for straight-forward flags and + optional-argument options for which no argument was given; or else + the string value of the argument passed with an option given only + once; or a table of string values of the same for arguments given + multiple times.

+ +
 ./prog -x -n -x => opts = { x = true, dryrun = true }
+ ./prog -e '(foo bar)' -e '(foo baz)'
+     => opts = {eval_file = {"(foo bar)", "(foo baz)"} }
+
+ +

If you write your own handlers, or otherwise specify custom + handling of options with on, then whatever value those handlers + return will be assigned to the respective keys in opts. + + + + + + + +

+
+

Methods

+ +
+
+ + std.optparse:on (name, handler, value) +
+
+ Add an option handler.

+ +

When the automatically assigned option handlers don't do everything + you require, or when you don't want to put an option into the + OptionParser spec argument, use this function to specify custom + behaviour. If you write the option into the spec argument anyway, + calling this function will replace the automatically assigned handler + with your own.

+ +

When writing your own handlers for std.optparse:on, you only need + to deal with normalised arguments, because combined short arguments + (-xyz), equals separators to long options (--long=ARG) are fully + expanded before any handler is called. + + +

Parameters:

+
    +
  • name + opts + of the option, or list of option names +
  • +
  • handler + on_handler + function to call when any of opts is + encountered +
  • +
  • value + additional value passed to on_handler +
  • +
+ + + + +

Usage:

+
    +
    + -- Don't process any arguments after `--`
    + parser:on ('--', parser.finished)
    +
+ +
+
+ + std.optparse:on_handler (arglist, i[, value=nil]) +
+
+ Function signature of an option handler for on. + + +

Parameters:

+
    +
  • arglist + table + list of arguments +
  • +
  • i + int + index of last processed element of arglist +
  • +
  • value + additional value registered with on + (default nil) +
  • +
+ +

Returns:

+
    + + int + index of next element of arglist to process +
+ + + + +
+
+ + std.optparse:parse (arglist[, defaults]) +
+
+ Parse an argument list. + + +

Parameters:

+
    +
  • arglist + table + list of arguments +
  • +
  • defaults + table + table of default option values + (optional) +
  • +
+ +

Returns:

+
    +
  1. + table + a list of unrecognised arglist elements
  2. +
  3. + opts + parsing results
  4. +
+ + + + +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/classes/std.set.html b/doc/classes/std.set.html new file mode 100644 index 0000000..0344303 --- /dev/null +++ b/doc/classes/std.set.html @@ -0,0 +1,862 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Class std.set

+

Set container prototype.

+

+ + +

Note that Functions listed below are only available from the Set + prototype returned by requiring this module, because Container + objects cannot have object methods.

+ +

Prototype Chain

+ + +
+table
+ `-> Object
+      `-> Container
+           `-> Set
+
+ +

+ + +

Objects

+ + + + + +
std.set.SetSet prototype object.
+

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
std.set.delete (set, e)Delete an element from a set.
std.set.difference (set1, set2)Find the difference of two sets.
std.set.difference (set, e)Say whether an element is in a set.
std.set.elems (set)Iterator for sets.
std.set.equal (set1, set2)Find whether two sets are equal.
std.set.insert (set, e)Insert an element into a set.
std.set.intersection (set1, set2)Find the intersection of two sets.
std.set.proper_subset (set1, set2)Find whether one set is a proper subset of another.
std.set.subset (set1, set2)Find whether one set is a subset of another.
std.set.symmetric_difference (set1, set2)Find the symmetric difference of two sets.
std.set.union (set1, set2)Find the union of two sets.
+

Metamethods

+ + + + + + + + + + + + + + + + + + + + + + + + + +
std.set.__add (set1, set2)Union operator.
std.set.__div (set1, set2)Symmetric difference operator.
std.set.__le (set1, set2)Subset operator.
std.set.__lt (set1, set2)Proper subset operator.
std.set.__mul (set1, set2)Intersection operator.
std.set.__sub (set1, set2)Difference operator.
+ +
+
+ + +

Objects

+ +
+
+ + std.set.Set +
+
+ Set prototype object.

+ +

Set also inherits all the fields and methods from + std.container.Container. + + +

Fields:

+
    +
  • _type + string + object name + (default "Set") +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    + local std = require "std"
    + std.prototype (std.set) --> "Set"
    + os.exit (0)
    +
+ +
+
+

Functions

+ Methods +
+
+ + std.set.delete (set, e) +
+
+ Delete an element from a set. + + +

Parameters:

+
    +
  • set + Set + a set +
  • +
  • e + element +
  • +
+ +

Returns:

+
    + + Set + the modified set +
+ + + +

Usage:

+
    +
    set.delete (available, found)
    +
+ +
+
+ + std.set.difference (set1, set2) +
+
+ Find the difference of two sets. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + Set + a copy of set1 with elements of set2 removed +
+ + + +

Usage:

+
    +
    all = set.difference (all, {32, 49, 56})
    +
+ +
+
+ + std.set.difference (set, e) +
+
+ Say whether an element is in a set. + + +

Parameters:

+
    +
  • set + Set + a set +
  • +
  • e + element +
  • +
+ +

Returns:

+
    + + true if e is in set, otherwise false + otherwise +
+ + + +

Usage:

+
    +
    if not set.member (keyset, pressed) then return nil end
    +
+ +
+
+ + std.set.elems (set) +
+
+ Iterator for sets. + + +

Parameters:

+
    +
  • set + Set + a set +
  • +
+ +

Returns:

+
    + + *set* + iterator +
+ + + +

Usage:

+
    +
    for code in set.elems (isprintable) do print (code) end
    +
+ +
+
+ + std.set.equal (set1, set2) +
+
+ Find whether two sets are equal. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + boolean + true if set1 and set2 each contain identical + elements, false otherwise +
+ + + +

Usage:

+
    +
    if set.equal (keys, {META, CTRL, "x"}) then process (keys) end
    +
+ +
+
+ + std.set.insert (set, e) +
+
+ Insert an element into a set. + + +

Parameters:

+
    +
  • set + Set + a set +
  • +
  • e + element +
  • +
+ +

Returns:

+
    + + Set + the modified set +
+ + + +

Usage:

+
    +
    + for byte = 32,126 do
    +   set.insert (isprintable, string.char (byte))
    + end
    +
+ +
+
+ + std.set.intersection (set1, set2) +
+
+ Find the intersection of two sets. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + Set + a new set with elements in both set1 and set2 +
+ + + +

Usage:

+
    +
    common = set.intersection (a, b)
    +
+ +
+
+ + std.set.proper_subset (set1, set2) +
+
+ Find whether one set is a proper subset of another. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + boolean + true if set2 contains all elements in set1 + but not only those elements, false otherwise +
+ + + +

Usage:

+
    +
    + if set.proper_subset (a, b) then
    +   for e in set.elems (set.difference (b, a)) do
    +     set.delete (b, e)
    +   end
    + end
    + assert (set.equal (a, b))
    +
+ +
+
+ + std.set.subset (set1, set2) +
+
+ Find whether one set is a subset of another. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + boolean + true if all elements in set1 are also in set2, + false otherwise +
+ + + +

Usage:

+
    +
    if set.subset (a, b) then a = b end
    +
+ +
+
+ + std.set.symmetric_difference (set1, set2) +
+
+ Find the symmetric difference of two sets. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + Set + a new set with elements that are in set1 or set2 + but not both +
+ + + +

Usage:

+
    +
    unique = set.symmetric_difference (a, b)
    +
+ +
+
+ + std.set.union (set1, set2) +
+
+ Find the union of two sets. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + Set + a copy of set1 with elements in set2 merged in +
+ + + +

Usage:

+
    +
    all = set.union (a, b)
    +
+ +
+
+

Metamethods

+ +
+
+ + std.set.__add (set1, set2) +
+
+ Union operator. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + Set + everything from set1 plus everything from set2 +
+ + +

See also:

+ + +

Usage:

+
    +
    union = set1 + set2
    +
+ +
+
+ + std.set.__div (set1, set2) +
+
+ Symmetric difference operator. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + Set + everything from set1 or set2 but not both +
+ + +

See also:

+ + +

Usage:

+
    +
    symmetric_difference = set1 / set2
    +
+ +
+
+ + std.set.__le (set1, set2) +
+
+ Subset operator. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + boolean + true if everything in set1 is also in set2 +
+ + +

See also:

+ + +

Usage:

+
    +
    issubset = set1 <= set2
    +
+ +
+
+ + std.set.__lt (set1, set2) +
+
+ Proper subset operator. + + +

Parameters:

+
    +
  • set1 + Set + set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + boolean + true if set2 is not equal to set1, but does + contain everything from set1 +
+ + +

See also:

+ + +

Usage:

+
    +
    ispropersubset = set1 < set2
    +
+ +
+
+ + std.set.__mul (set1, set2) +
+
+ Intersection operator. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + Set + anything this is in both set1 and set2 +
+ + +

See also:

+ + +

Usage:

+
    +
    intersection = set1 * set2
    +
+ +
+
+ + std.set.__sub (set1, set2) +
+
+ Difference operator. + + +

Parameters:

+
    +
  • set1 + Set + a set +
  • +
  • set2 + Set + another set +
  • +
+ +

Returns:

+
    + + Set + everything from set1 that is not also in set2 +
+ + +

See also:

+ + +

Usage:

+
    +
    difference = set1 - set2
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/classes/std.strbuf.html b/doc/classes/std.strbuf.html new file mode 100644 index 0000000..22c5c94 --- /dev/null +++ b/doc/classes/std.strbuf.html @@ -0,0 +1,298 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Class std.strbuf

+

String buffers.

+

+ + +

Buffers are mutable by default, but being based on objects, they can + also be used in a functional style:

+ + +
+local StrBuf = require "std.strbuf" {}
+local a = StrBuf {"a"}
+local b = a:concat "b"    -- mutate *a*
+print (a, b)              --> ab   ab
+local c = a {} .. "c"     -- copy and append
+print (a, c)              --> ab   abc
+
+
+ +

Prototype Chain

+ + +
+table
+ `-> Object
+      `-> StrBuf
+
+ +

+ + +

Objects

+ + + + + +
std.strbuf.StrBufStrBuf prototype object.
+

Functions

+ + + + + +
std.strbuf.concat (x)Add a object to a buffer.
+

Metamethods

+ + + + + + + + + +
std.strbuf:__concat (buffer, x)Support concatenation to StrBuf objects.
std.strbuf:__tostring (buffer)Support fast conversion to Lua string.
+ +
+
+ + +

Objects

+ +
+
+ + std.strbuf.StrBuf +
+
+ StrBuf prototype object.

+ +

Set also inherits all the fields and methods from + std.object.Object. + + +

Fields:

+
    +
  • _type + string + object name + (default "StrBuf") +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    + local std = require "std"
    + local StrBuf = std.strbuf {}
    + local a = {1, 2, 3}
    + local b = {a, "five", "six"}
    + a = a .. 4
    + b = b:concat "seven"
    + print (a, b) --> 1234   1234fivesixseven
    + os.exit (0)
    +
+ +
+
+

Functions

+ Methods +
+
+ + std.strbuf.concat (x) +
+
+ Add a object to a buffer. + Elements are stringified lazily, so if add a table and then change + its contents, the contents of the buffer will be affected too. + + +

Parameters:

+
    +
  • x + object to add to buffer +
  • +
+ +

Returns:

+
    + + StrBuf + modified buffer +
+ + + +

Usage:

+
    +
    buf = buf:concat "append this" {" and", " this"}
    +
+ +
+
+

Metamethods

+ +
+
+ + std.strbuf:__concat (buffer, x) +
+
+ Support concatenation to StrBuf objects. + + +

Parameters:

+
    +
  • buffer + StrBuf + object +
  • +
  • x + a string, or object that can be coerced to a string +
  • +
+ +

Returns:

+
    + + StrBuf + modified buf +
+ + +

See also:

+ + +

Usage:

+
    +
    buf = buf .. x
    +
+ +
+
+ + std.strbuf:__tostring (buffer) +
+
+ Support fast conversion to Lua string. + + +

Parameters:

+
    +
  • buffer + StrBuf + object +
  • +
+ +

Returns:

+
    + + string + concatenation of buffer contents +
+ + +

See also:

+ + +

Usage:

+
    +
    str = tostring (buf)
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/classes/std.tree.html b/doc/classes/std.tree.html new file mode 100644 index 0000000..0a64c23 --- /dev/null +++ b/doc/classes/std.tree.html @@ -0,0 +1,545 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Class std.tree

+

Tree container prototype.

+

+ + +

Note that Functions listed below are only available from the Tree + prototype returned by requiring this module, because Container objects + cannot have object methods.

+ +

Prototype Chain

+ + +
+table
+ `-> Object
+      `-> Container
+           `-> Tree
+
+ +

+ + +

Objects

+ + + + + +
std.tree.TreeTree prototype object.
+

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + +
std.tree.clone (t, nometa)Make a deep copy of a tree, including any metatables.
std.tree.ileaves (tr)Tree iterator which returns just numbered leaves, in order.
std.tree.inodes (tr)Tree iterator over numbered nodes, in order.
std.tree.leaves (t)Tree iterator which returns just leaves.
std.tree.merge (t, u)Destructively deep-merge one tree into another.
std.tree.nodes (tr)Tree iterator over all nodes.
+

Metamethods

+ + + + + + + + + +
std.tree.__index (tr, i)Deep retrieval.
std.tree.__newindex (tr, i[, v])Deep insertion.
+ +
+
+ + +

Objects

+ +
+
+ + std.tree.Tree +
+
+ Tree prototype object. + + +

Fields:

+
    +
  • _type + string + object name + (default "Tree") +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    + local std = require "std"
    + local Tree = std.tree {}
    + local tr = Tree {}
    + tr[{"branch1", 1}] = "leaf1"
    + tr[{"branch1", 2}] = "leaf2"
    + tr[{"branch2", 1}] = "leaf3"
    + print (tr[{"branch1"}])      --> Tree {leaf1, leaf2}
    + print (tr[{"branch1", 2}])   --> leaf2
    + print (tr[{"branch1", 3}])   --> nil
    + --> leaf1	leaf2	leaf3
    + for leaf in std.tree.leaves (tr) do
    +   io.write (leaf .. "\t")
    + end
    +
+ +
+
+

Functions

+ Methods +
+
+ + std.tree.clone (t, nometa) +
+
+ Make a deep copy of a tree, including any metatables. + + +

Parameters:

+
    +
  • t + table + tree or tree-like table +
  • +
  • nometa + boolean + if non-nil don't copy metatables +
  • +
+ +

Returns:

+
    + + Tree or table + a deep copy of tr +
+ + +

See also:

+ + +

Usage:

+
    +
    + tr = {"one", {two=2}, {{"three"}, four=4}}
    + copy = clone (tr)
    + copy[2].two=5
    + assert (tr[2].two == 2)
    +
+ +
+
+ + std.tree.ileaves (tr) +
+
+ Tree iterator which returns just numbered leaves, in order. + + +

Parameters:

+
    +
  • tr + Tree or table + tree or tree-like table +
  • +
+ +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + Tree or table + the tree tr
  4. +
+ + +

See also:

+ + +

Usage:

+
    +
    + --> t = {"one", "three", "five"}
    + for leaf in ileaves {"one", {two=2}, {{"three"}, four=4}}, foo="bar", "five"}
    + do
    +   t[#t + 1] = leaf
    + end
    +
+ +
+
+ + std.tree.inodes (tr) +
+
+ Tree iterator over numbered nodes, in order.

+ +

The iterator function behaves like nodes, but only traverses the + array part of the nodes of tr, ignoring any others. + + +

Parameters:

+
    +
  • tr + Tree or table + tree or tree-like table to iterate over +
  • +
+ +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + tree or table + the tree, tr
  4. +
+ + +

See also:

+ + + +
+
+ + std.tree.leaves (t) +
+
+ Tree iterator which returns just leaves. + + +

Parameters:

+
    +
  • t + table + tree or tree-like table +
  • +
+ +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + table + t
  4. +
+ + +

See also:

+ + +

Usage:

+
    +
    + for leaf in leaves {"one", {two=2}, {{"three"}, four=4}}, foo="bar", "five"}
    + do
    +   t[#t + 1] = leaf
    + end
    + --> t = {2, 4, "five", "foo", "one", "three"}
    + table.sort (t, lambda "=tostring(_1) < tostring(_2)")
    +
+ +
+
+ + std.tree.merge (t, u) +
+
+ Destructively deep-merge one tree into another. + + +

Parameters:

+
    +
  • t + table + destination tree +
  • +
  • u + table + table with nodes to merge +
  • +
+ +

Returns:

+
    + + table + t with nodes from u merged in +
+ + +

See also:

+ + +

Usage:

+
    +
    merge (dest, {{exists=1}, {{not = {present = { inside = "dest" }}}}})
    +
+ +
+
+ + std.tree.nodes (tr) +
+
+ Tree iterator over all nodes.

+ +

The returned iterator function performs a depth-first traversal of + tr, and at each node it returns {node-type, tree-path, tree-node} + where node-type is branch, join or leaf; tree-path is a + list of keys used to reach this node, and tree-node is the current + node.

+ +

Note that the tree-path reuses the same table on each iteration, so + you must table.clone a copy if you want to take a snap-shot of the + current state of the tree-path list before the next iteration + changes it. + + +

Parameters:

+
    +
  • tr + Tree or table + tree or tree-like table to iterate over +
  • +
+ +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + Tree or table + the tree, tr
  4. +
+ + +

See also:

+ + +

Usage:

+
    +
    + -- tree = +-- node1
    + --        |    +-- leaf1
    + --        |    '-- leaf2
    + --        '-- leaf 3
    + tree = Tree { Tree { "leaf1", "leaf2"}, "leaf3" }
    + for node_type, path, node in nodes (tree) do
    +   print (node_type, path, node)
    + end
    + --> "branch"   {}      {{"leaf1", "leaf2"}, "leaf3"}
    + --> "branch"   {1}     {"leaf1", "leaf"2")
    + --> "leaf"     {1,1}   "leaf1"
    + --> "leaf"     {1,2}   "leaf2"
    + --> "join"     {1}     {"leaf1", "leaf2"}
    + --> "leaf"     {2}     "leaf3"
    + --> "join"     {}      {{"leaf1", "leaf2"}, "leaf3"}
    + os.exit (0)
    +
+ +
+
+

Metamethods

+ +
+
+ + std.tree.__index (tr, i) +
+
+ Deep retrieval. + + +

Parameters:

+
    +
  • tr + Tree + a tree +
  • +
  • i + non-table, or list of keys {i1, ...i_n} +
  • +
+ +

Returns:

+
    + + tr[i1]...[i_n] if i is a key list, tr[i] otherwise +
+ + + +

Usage:

+
    +
    del_other_window = keymap[{"C-x", "4", KEY_DELETE}]
    +
+ +
+
+ + std.tree.__newindex (tr, i[, v]) +
+
+ Deep insertion. + + +

Parameters:

+
    +
  • tr + Tree + a tree +
  • +
  • i + non-table, or list of keys {i1, ...i_n} +
  • +
  • v + value + (optional) +
  • +
+ + + + +

Usage:

+
    +
    function bindkey (keylist, fn) keymap[keylist] = fn end
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..9a1556f --- /dev/null +++ b/doc/index.html @@ -0,0 +1,146 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + +

Standard Lua Libraries

+ +

Modules

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
stdLua Standard Libraries.
std.debugAdditions to the core debug module.
std.functionalFunctional programming.
std.ioAdditions to the core io module.
std.mathAdditions to the core math module.
std.operatorFunctional forms of Lua operators.
std.packageAdditions to the core package module.
std.strictChecks uses of undeclared global variables.
std.stringAdditions to the core string module.
std.tableExtensions to the core table module.
+

Classes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
std.treeTree container prototype.
std.containerContainer prototype.
std.objectPrototype-based objects.
std.listTables as lists.
std.optparseParse and process command line options.
std.setSet container prototype.
std.strbufString buffers.
+ +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/ldoc.css b/doc/ldoc.css new file mode 100644 index 0000000..7d74ca2 --- /dev/null +++ b/doc/ldoc.css @@ -0,0 +1,307 @@ +/* BEGIN RESET + +Copyright (c) 2010, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.html +version: 2.8.2r1 +*/ +html { + color: #000; + background: #FFF; +} +body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td { + margin: 0; + padding: 0; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +fieldset,img { + border: 0; +} +address,caption,cite,code,dfn,em,strong,th,var,optgroup { + font-style: inherit; + font-weight: inherit; +} +del,ins { + text-decoration: none; +} +li { + list-style: disc; + margin-left: 20px; +} +caption,th { + text-align: left; +} +h1,h2,h3,h4,h5,h6 { + font-size: 100%; + font-weight: bold; +} +q:before,q:after { + content: ''; +} +abbr,acronym { + border: 0; + font-variant: normal; +} +sup { + vertical-align: baseline; +} +sub { + vertical-align: baseline; +} +legend { + color: #000; +} +input,button,textarea,select,optgroup,option { + font-family: inherit; + font-size: inherit; + font-style: inherit; + font-weight: inherit; +} +input,button,textarea,select {*font-size:100%; +} +/* END RESET */ + +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color: #ffffff; margin: 0px; +} + +code, tt { font-family: monospace; font-size: 1.1em; } +span.parameter { font-family:monospace; } +span.parameter:after { content:":"; } +span.types:before { content:"("; } +span.types:after { content:")"; } +.type { font-weight: bold; font-style:italic } + +body, p, td, th { font-size: .95em; line-height: 1.2em;} + +p, ul { margin: 10px 0 0 0px;} + +strong { font-weight: bold;} + +em { font-style: italic;} + +h1 { + font-size: 1.5em; + margin: 0 0 20px 0; +} +h2, h3, h4 { margin: 15px 0 10px 0; } +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; } +h4 { font-size: 1.06em; } + +a:link { font-weight: bold; color: #004080; text-decoration: none; } +a:visited { font-weight: bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration: underline; } + +hr { + color:#cccccc; + background: #00007f; + height: 1px; +} + +blockquote { margin-left: 3em; } + +ul { list-style-type: disc; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; +} + +pre.example { + background-color: rgb(245, 245, 245); + border: 1px solid silver; + padding: 10px; + margin: 10px 0 10px 0; + font-family: "Andale Mono", monospace; + font-size: .85em; +} + +pre { + background-color: rgb(245, 245, 245); + border: 1px solid silver; + padding: 10px; + margin: 10px 0 10px 0; + overflow: auto; + font-family: "Andale Mono", monospace; +} + + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #f0f0f0; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#main { + background-color: #f0f0f0; + border-left: 2px solid #cccccc; +} + +#navigation { + float: left; + width: 14em; + vertical-align: top; + background-color: #f0f0f0; + overflow: visible; +} + +#navigation h2 { + background-color:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align: left; + padding:0.2em; + border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul +{ + font-size:1em; + list-style-type: none; + margin: 1px 1px 10px 1px; +} + +#navigation li { + text-indent: -1em; + display: block; + margin: 3px 0px 0px 22px; +} + +#navigation li li a { + margin: 0px 3px 0px -1em; +} + +#content { + margin-left: 14em; + padding: 1em; + width: 700px; + border-left: 2px solid #cccccc; + border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 12pt "Times New Roman", "TimeNR", Times, serif; + } + a { font-weight: bold; color: #004080; text-decoration: underline; } + + #main { + background-color: #ffffff; + border-left: 0px; + } + + #container { + margin-left: 2%; + margin-right: 2%; + background-color: #ffffff; + } + + #content { + padding: 1em; + background-color: #ffffff; + } + + #navigation { + display: none; + } + pre.example { + font-family: "Andale Mono", monospace; + font-size: 10pt; + page-break-inside: avoid; + } +} + +table.module_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.module_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.module_list td.name { background-color: #f0f0f0; min-width: 200px; } +table.module_list td.summary { width: 100%; } + + +table.function_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.function_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.function_list td.name { background-color: #f0f0f0; min-width: 200px; } +table.function_list td.summary { width: 100%; } + +ul.nowrap { + overflow:auto; + white-space:nowrap; +} + +dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} +dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} +dl.table h3, dl.function h3 {font-size: .95em;} + +/* stop sublists from having initial vertical space */ +ul ul { margin-top: 0px; } +ol ul { margin-top: 0px; } +ol ol { margin-top: 0px; } +ul ol { margin-top: 0px; } + +/* make the target distinct; helps when we're navigating to a function */ +a:target + * { + background-color: #FF9; +} + +/* styles for prettification of source */ +pre .comment { color: #558817; } +pre .constant { color: #a8660d; } +pre .escape { color: #844631; } +pre .keyword { color: #aa5050; font-weight: bold; } +pre .library { color: #0e7c6b; } +pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } +pre .string { color: #8080ff; } +pre .number { color: #f8660d; } +pre .operator { color: #2239a8; font-weight: bold; } +pre .preprocessor, pre .prepro { color: #a33243; } +pre .global { color: #800080; } +pre .prompt { color: #558817; } +pre .url { color: #272fc2; text-decoration: underline; } diff --git a/doc/modules/std.debug.html b/doc/modules/std.debug.html new file mode 100644 index 0000000..052f34c --- /dev/null +++ b/doc/modules/std.debug.html @@ -0,0 +1,857 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.debug

+

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"
+
+ +

The behaviour of the functions in this module are controlled by the value + of the global _DEBUG. Not setting _DEBUG prior to requiring any of + stdlib's modules is equivalent to having _DEBUG = true.

+ +

The first line of Lua code in production quality projects that use stdlib + should be either:

+ + +
+_DEBUG = false
+
+ +

or alternatively, if you need to be careful not to damage the global + environment:

+ + +
+local init = require "std.debug_init"
+init._DEBUG = false
+
+ +

This mitigates almost all of the overhead of argument typechecking in + stdlib API functions.

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DEPRECATED (version, name[, extramsg], fn)Provide a deprecated function definition according to _DEBUG.deprecate.
DEPRECATIONMSG (version, name[, extramsg], level)Format a deprecation warning message.
argcheck (name, i, expected, actual[, level=2])Check the type of an argument against expected types.
argerror (name, i[, extramsg[, level=1]])Raise a bad argument error.
argscheck (decl, inner)Wrap a function definition with argument type and arity checking.
debug ()Equivalent to calling debug.say (1, ...)
extramsg_mismatch (expected, actual[, index])Format a type mismatch error.
parsetypes (types)Compact permutation list into a list of valid types at each argument.
say ([n=1], ...)Print a debugging message to io.stderr.
trace (event)Trace function calls.
+

Tables

+ + + + + +
_DEBUGControl std.debug function behaviour.
+

Fields

+ + + + + + + + + + + + + + + + + + + + + +
extramsg_toomanyFormat a too many things error.
getfenvExtend debug.getfenv to unwrap functables correctly.
resulterrorRaise a bad result error.
setfenvExtend debug.setfenv to unwrap functables correctly.
typesplitSplit a typespec string into a table of normalized type names.
+ +
+
+ + +

Functions

+ Methods +
+
+ + DEPRECATED (version, name[, extramsg], fn) +
+
+ Provide a deprecated function definition according to _DEBUG.deprecate. + You can check whether your covered code uses deprecated functions by + setting _DEBUG.deprecate to true before loading any stdlib modules, + or silence deprecation warnings by setting _DEBUG.deprecate = false. + + +

Parameters:

+
    +
  • version + string + first deprecation release version +
  • +
  • name + string + function name for automatic warning message +
  • +
  • extramsg + string + additional warning text + (optional) +
  • +
  • fn + func + deprecated function +
  • +
+ +

Returns:

+
    + + a function to show the warning on first call, and hand off to fn +
+ + + +

Usage:

+
    +
    M.op = DEPRECATED ("41", "'std.functional.op'", std.operator)
    +
+ +
+
+ + DEPRECATIONMSG (version, name[, extramsg], level) +
+
+ Format a deprecation warning message. + + +

Parameters:

+
    +
  • version + string + first deprecation release version +
  • +
  • name + string + function name for automatic warning message +
  • +
  • extramsg + string + additional warning text + (optional) +
  • +
  • level + int + call stack level to blame for the error +
  • +
+ +

Returns:

+
    + + string + deprecation warning message, or empty string +
+ + + +

Usage:

+
    +
    io.stderr:write (DEPRECATIONMSG ("42", "multi-argument 'module.fname'", 2))
    +
+ +
+
+ + argcheck (name, i, expected, actual[, level=2]) +
+
+ Check the type of an argument against expected types. + Equivalent to luaL_argcheck in the Lua C API.

+ +

Call argerror if there is a type mismatch.

+ +

Argument actual must match one of the types from in expected, each + of which can be the name of a primitive Lua type, a stdlib object type, + or one of the special options below:

+ +
#table    accept any non-empty table
+any       accept any non-nil argument type
+file      accept an open file object
+function  accept a function, or object with a __call metamethod
+int       accept an integer valued number
+list      accept a table where all keys are a contiguous 1-based integer range
+#list     accept any non-empty list
+object    accept any std.Object derived type
+:foo      accept only the exact string ":foo", works for any :-prefixed string
+
+ +

The :foo format allows for type-checking of self-documenting + boolean-like constant string parameters predicated on nil versus + :option instead of false versus true. Or you could support + both:

+ +
argcheck ("table.copy", 2, "boolean|:nometa|nil", nometa)
+
+ +

A very common pattern is to have a list of possible types including + "nil" when the argument is optional. Rather than writing long-hand + as above, prepend a question mark to the list of types and omit the + explicit "nil" entry:

+ +
argcheck ("table.copy", 2, "?boolean|:nometa", predicate)
+
+ +

Normally, you should not need to use the level parameter, as the + default is to blame the caller of the function using argcheck in + error messages; which is almost certainly what you want. + + +

Parameters:

+
    +
  • name + string + function to blame in error message +
  • +
  • i + int + argument number to blame in error message +
  • +
  • expected + string + specification for acceptable argument types +
  • +
  • actual + argument passed +
  • +
  • level + int + call stack level to blame for the error + (default 2) +
  • +
+ + + + +

Usage:

+
    +
    + local function case (with, branches)
    +   argcheck ("std.functional.case", 2, "#table", branches)
    +   ...
    +
+ +
+
+ + argerror (name, i[, extramsg[, level=1]]) +
+
+ Raise a bad argument error. + Equivalent to luaL_argerror in the Lua C API. This function does not + return. The level argument behaves just like the core error + function. + + +

Parameters:

+
    +
  • name + string + function to callout in error message +
  • +
  • i + int + argument number +
  • +
  • extramsg + string + additional text to append to message inside parentheses + (optional) +
  • +
  • level + int + call stack level to blame for the error + (default 1) +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    + local function slurp (file)
    +   local h, err = input_handle (file)
    +   if h == nil then argerror ("std.io.slurp", 1, err, 2) end
    +   ...
    +
+ +
+
+ + argscheck (decl, inner) +
+
+ +

Wrap a function definition with argument type and arity checking. + In addition to checking that each argument type matches the corresponding + element in the types table with argcheck, if the final element of + types ends with an ellipsis, remaining unchecked arguments are checked + against that type:

+ +
 format = argscheck ("string.format (string, ?any...)", string.format)
+
+ +

A colon in the function name indicates that the argument type list does + not have a type for self:

+ +
 format = argscheck ("string:format (?any...)", string.format)
+
+ +

If an argument can be omitted entirely, then put its type specification + in square brackets:

+ +
 insert = argscheck ("table.insert (table, [int], ?any)", table.insert)
+
+ +

Similarly return types can be checked with the same list syntax as + arguments:

+ +
 len = argscheck ("string.len (string) => int", string.len)
+
+ +

Additionally, variant return type lists can be listed like this:

+ +
 open = argscheck ("io.open (string, ?string) => file or nil, string",
+                   io.open)
+
+ + + + +

Parameters:

+
    +
  • decl + string + function type declaration string +
  • +
  • inner + func + function to wrap with argument checking +
  • +
+ + + + +

Usage:

+
    +
    + local case = argscheck ("std.functional.case (?any, #table) => [any...]",
    +   function (with, branches)
    +     ...
    + end)
    +
+ +
+
+ + debug () +
+
+ Equivalent to calling debug.say (1, ...) + + + + + +

See also:

+ + +

Usage:

+
    +
    + local debug = require "std.debug"
    + debug "oh noes!"
    +
+ +
+
+ + extramsg_mismatch (expected, actual[, index]) +
+
+ Format a type mismatch error. + + +

Parameters:

+
    +
  • expected + string + a pipe delimited list of matchable types +
  • +
  • actual + the actual argument to match with +
  • +
  • index + number + erroring container element index + (optional) +
  • +
+ +

Returns:

+
    + + string + formatted extramsg for this mismatch for argerror +
+ + +

See also:

+ + +

Usage:

+
    +
    +   if fmt ~= nil and type (fmt) ~= "string" then
    +     argerror ("format", 1, extramsg_mismatch ("?string", fmt))
    +   end
    +
+ +
+
+ + parsetypes (types) +
+
+ Compact permutation list into a list of valid types at each argument. + Eliminate bracketed types by combining all valid types at each position + for all permutations of typelist. + + +

Parameters:

+
    +
  • types + list + a normalized list of type names +
  • +
+ +

Returns:

+
    + + list + valid types for each positional parameter +
+ + + + +
+
+ + say ([n=1], ...) +
+
+ Print a debugging message to io.stderr. + Display arguments passed through std.tostring and separated by tab + characters when _DEBUG is true and n is 1 or less; or _DEBUG.level + is a number greater than or equal to n. If _DEBUG is false or + nil, nothing is written. + + +

Parameters:

+
    +
  • n + int + debugging level, smaller is higher priority + (default 1) +
  • +
  • ... + objects to print (as for print) +
  • +
+ + + + +

Usage:

+
    +
    + local _DEBUG = require "std.debug_init"._DEBUG
    + _DEBUG.level = 3
    + say (2, "_DEBUG table contents:", _DEBUG)
    +
+ +
+
+ + trace (event) +
+
+ Trace function calls. + Use as debug.sethook (trace, "cr"), which is done automatically + when _DEBUG.call is set. + Based on test/trace-calls.lua from the Lua distribution. + + +

Parameters:

+
    +
  • event + string + event causing the call +
  • +
+ + + + +

Usage:

+
    +
    + _DEBUG = { call = true }
    + local debug = require "std.debug"
    +
+ +
+
+

Tables

+ +
+
+ + _DEBUG +
+
+ Control std.debug function behaviour. + To declare debugging state, set _DEBUG either to false to disable all + runtime debugging; to any "truthy" value (equivalent to enabling everything + except call, or as documented below. + + +

Fields:

+
    +
  • argcheck + boolean + honor argcheck and argscheck calls + (default true) +
  • +
  • call + boolean + do call trace debugging + (default false) +
  • +
  • deprecate + if false, deprecated APIs are defined, + and do not issue deprecation warnings when used; if nil issue a + deprecation warning each time a deprecated api is used; any other + value causes deprecated APIs not to be defined at all + (default nil) +
  • +
  • level + int + debugging level + (default 1) +
  • +
+ + + + +

Usage:

+
    +
    _DEBUG = { argcheck = false, level = 9 }
    +
+ +
+
+

Fields

+ +
+
+ + extramsg_toomany +
+
+ Format a too many things error. + + +
    +
  • bad + string + the thing there are too many of +
  • +
  • expected + int + maximum number of bad things expected +
  • +
  • actual + int + actual number of bad things that triggered the error +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    +   if maxn (argt) > 7 then
    +     argerror ("sevenses", 8, extramsg_toomany ("argument", 7, maxn (argt)))
    +   end
    +
+ +
+
+ + getfenv +
+
+ Extend debug.getfenv to unwrap functables correctly. + + +
    +
  • fn + int, function or functable + target function, or stack level +
  • +
+ + + + + +
+
+ + resulterror +
+
+ Raise a bad result error. + Like argerror for bad results. This function does not + return. The level argument behaves just like the core error + function. + + +
    +
  • name + string + function to callout in error message +
  • +
  • i + int + argument number +
  • +
  • extramsg + string + additional text to append to message inside parentheses + (optional) +
  • +
  • level + int + call stack level to blame for the error + (default 1) +
  • +
+ + + + +

Usage:

+
    +
    + local function slurp (file)
    +   local h, err = input_handle (file)
    +   if h == nil then argerror ("std.io.slurp", 1, err, 2) end
    +   ...
    +
+ +
+
+ + setfenv +
+
+ Extend debug.setfenv to unwrap functables correctly. + + +
    +
  • fn + function or functable + target function +
  • +
  • env + table + new function environment +
  • +
+ + + + + +
+
+ + typesplit +
+
+ Split a typespec string into a table of normalized type names. + + +
    +
  • either + string or table + "?bool|:nometa" or {"boolean", ":nometa"} +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/modules/std.functional.html b/doc/modules/std.functional.html new file mode 100644 index 0000000..f5eeda1 --- /dev/null +++ b/doc/modules/std.functional.html @@ -0,0 +1,1030 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.functional

+

Functional programming.

+

A selection of higher-order functions to enable a functional style of + programming in Lua.

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
bind (fn, argt)Partially apply a function.
callable (x)Identify callable types.
case (with, branches)A rudimentary case statement.
collect ([ifn=std.npairs], ...)Collect the results of an iterator.
compose (...)Compose functions.
cond (expr, branch, ...)A rudimentary condition-case statement.
curry (fn, n)Curry a function.
filter (pfn[, ifn=std.pairs], ...)Filter an iterator with a predicate.
foldl (fn[, d=t[1]], t)Fold a binary function left associatively.
foldr (fn[, d=t[1]], t)Fold a binary function right associatively.
id (...)Identity function.
lambda (s)Compile a lambda string into a Lua function.
map (fn[, ifn=std.pairs], ...)Map a function over an iterator.
map_with (fn, tt)Map a function over a table of argument lists.
memoize (fn[, normfn=std.tostring])Memoize a function, by wrapping it in a functable.
nop ()No operation.
reduce (fn, d[, ifn=std.pairs], ...)Fold a binary function into an iterator.
zip (tt)Zip a table of tables.
zip_with (fn, tt)Zip a list of tables together with a function.
+

Types

+ + + + + + + + + +
normalize (...)Signature of a memoize argument normalization callback function.
predicate (...)Signature of a filter predicate callback function.
+ +
+
+ + +

Functions

+ Methods +
+
+ + bind (fn, argt) +
+
+ Partially apply a function. + + +

Parameters:

+
    +
  • fn + func + function to apply partially +
  • +
  • argt + table + table of fn arguments to bind +
  • +
+ +

Returns:

+
    + + function with argt arguments already bound +
+ + + +

Usage:

+
    +
    cube = bind (std.operator.pow, {[2] = 3})
    +
+ +
+
+ + callable (x) +
+
+ Identify callable types. + + +

Parameters:

+
    +
  • x + an object or primitive +
  • +
+ +

Returns:

+
    + + true if x can be called, otherwise false +
+ + + +

Usage:

+
    +
    if callable (functable) then functable (args) end
    +
+ +
+
+ + case (with, branches) +
+
+ A rudimentary case statement. + Match with against keys in branches table. + + +

Parameters:

+
    +
  • with + expression to match +
  • +
  • branches + table + map possible matches to functions +
  • +
+ +

Returns:

+
    + + the value associated with a matching key, or the first non-key + value if no key matches. Function or functable valued matches are + called using with as the sole argument, and the result of that call + returned; otherwise the matching value associated with the matching + key is returned directly; or else nil if there is no match and no + default. +
+ + +

See also:

+ + +

Usage:

+
    +
    + return case (type (object), {
    +   table  = "table",
    +   string = function ()  return "string" end,
    +            function (s) error ("unhandled type: " .. s) end,
    + })
    +
+ +
+
+ + collect ([ifn=std.npairs], ...) +
+
+ Collect the results of an iterator. + + +

Parameters:

+
    +
  • ifn + func + iterator function + (default std.npairs) +
  • +
  • ... + ifn arguments +
  • +
+ +

Returns:

+
    + + table + of results from running ifn on args +
+ + +

See also:

+ + +

Usage:

+
    +
    + --> {"a", "b", "c"}
    + collect {"a", "b", "c", x=1, y=2, z=5}
    +
+ +
+
+ + compose (...) +
+
+ Compose functions. + + +

Parameters:

+
    +
  • ... + func + functions to compose +
  • +
+ +

Returns:

+
    + + function + composition of fnN .. fn1: note that this is the + reverse of what you might expect, but means that code like:

    + +
     functional.compose (function (x) return f (x) end,
    +                     function (x) return g (x) end))
    +
    + +

    can be read from top to bottom. +

+ + + +

Usage:

+
    +
    + vpairs = compose (table.invert, ipairs)
    + for v, i in vpairs {"a", "b", "c"} do process (v, i) end
    +
+ +
+
+ + cond (expr, branch, ...) +
+
+ A rudimentary condition-case statement. + If expr is "truthy" return branch if given, otherwise expr + itself. If the return value is a function or functable, then call it + with expr as the sole argument and return the result; otherwise + return it explicitly. If expr is "falsey", then recurse with the + first two arguments stripped. + + +

Parameters:

+
    +
  • expr + a Lua expression +
  • +
  • branch + a function, functable or value to use if expr is + "truthy" +
  • +
  • ... + additional arguments to retry if expr is "falsey" +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    + -- recursively calculate the nth triangular number
    + function triangle (n)
    +   return cond (
    +     n <= 0, 0,
    +     n == 1, 1,
    +             function () return n + triangle (n - 1) end)
    + end
    +
+ +
+
+ + curry (fn, n) +
+
+ Curry a function. + + +

Parameters:

+
    +
  • fn + func + function to curry +
  • +
  • n + int + number of arguments +
  • +
+ +

Returns:

+
    + + function + curried version of fn +
+ + + +

Usage:

+
    +
    + add = curry (function (x, y) return x + y end, 2)
    + incr, decr = add (1), add (-1)
    +
+ +
+
+ + filter (pfn[, ifn=std.pairs], ...) +
+
+ Filter an iterator with a predicate. + + +

Parameters:

+
    +
  • pfn + predicate + predicate function +
  • +
  • ifn + func + iterator function + (default std.pairs) +
  • +
  • ... + iterator arguments +
  • +
+ +

Returns:

+
    + + table + elements e for which pfn (e) is not "falsey". +
+ + +

See also:

+ + +

Usage:

+
    +
    + --> {2, 4}
    + filter (lambda '|e|e%2==0', std.elems, {1, 2, 3, 4})
    +
+ +
+
+ + foldl (fn[, d=t[1]], t) +
+
+ Fold a binary function left associatively. + If parameter d is omitted, the first element of t is used, + and t treated as if it had been passed without that element. + + +

Parameters:

+
    +
  • fn + func + binary function +
  • +
  • d + initial left-most argument + (default t[1]) +
  • +
  • t + table + a table +
  • +
+ +

Returns:

+
    + + result +
+ + +

See also:

+ + +

Usage:

+
    +
    foldl (std.operator.quot, {10000, 100, 10}) == (10000 / 100) / 10
    +
+ +
+
+ + foldr (fn[, d=t[1]], t) +
+
+ Fold a binary function right associatively. + If parameter d is omitted, the last element of t is used, + and t treated as if it had been passed without that element. + + +

Parameters:

+
    +
  • fn + func + binary function +
  • +
  • d + initial right-most argument + (default t[1]) +
  • +
  • t + table + a table +
  • +
+ +

Returns:

+
    + + result +
+ + +

See also:

+ + +

Usage:

+
    +
    foldr (std.operator.quot, {10000, 100, 10}) == 10000 / (100 / 10)
    +
+ +
+
+ + id (...) +
+
+ Identity function. + + +

Parameters:

+
    +
  • ... + arguments +
  • +
+ +

Returns:

+
    + + arguments +
+ + + + +
+
+ + lambda (s) +
+
+ Compile a lambda string into a Lua function.

+ +

A valid lambda string takes one of the following forms:

+ +
    +
  1. '=expression': equivalent to function (...) return expression end
  2. +
  3. '|args|expression': equivalent to function (args) return expression end
  4. +
+ +

The first form (starting with '=') automatically assigns the first + nine arguments to parameters '_1' through '_9' for use within the + expression body. The parameter '_1' is aliased to '_', and if the + first non-whitespace of the whole expression is '_', then the + leading '=' can be omitted.

+ +

The results are memoized, so recompiling a previously compiled + lambda string is extremely fast. + + +

Parameters:

+
    +
  • s + string + a lambda string +
  • +
+ +

Returns:

+
    + + functable + compiled lambda string, can be called like a function +
+ + + +

Usage:

+
    +
    + -- The following are equivalent:
    + lambda '= _1 < _2'
    + lambda '|a,b| a<b'
    +
+ +
+
+ + map (fn[, ifn=std.pairs], ...) +
+
+ Map a function over an iterator. + + +

Parameters:

+
    +
  • fn + func + map function +
  • +
  • ifn + func + iterator function + (default std.pairs) +
  • +
  • ... + iterator arguments +
  • +
+ +

Returns:

+
    + + table + results +
+ + +

See also:

+ + +

Usage:

+
    +
    + --> {1, 4, 9, 16}
    + map (lambda '=_1*_1', std.ielems, {1, 2, 3, 4})
    +
+ +
+
+ + map_with (fn, tt) +
+
+ Map a function over a table of argument lists. + + +

Parameters:

+
    +
  • fn + func + map function +
  • +
  • tt + table + a table of fn argument lists +
  • +
+ +

Returns:

+
    + + table + new table of fn results +
+ + +

See also:

+ + +

Usage:

+
    +
    + --> {"123", "45"}, {a="123", b="45"}
    + conc = bind (map_with, {lambda '|...|table.concat {...}'})
    + conc {{1, 2, 3}, {4, 5}}, conc {a={1, 2, 3, x="y"}, b={4, 5, z=6}}
    +
+ +
+
+ + memoize (fn[, normfn=std.tostring]) +
+
+ Memoize a function, by wrapping it in a functable.

+ +

To ensure that memoize always returns the same results for the same + arguments, it passes arguments to fn. You can specify a more + sophisticated function if memoize should handle complicated argument + equivalencies. + + +

Parameters:

+
    +
  • fn + func + pure function: a function with no side effects +
  • +
  • normfn + normalize + function to normalize arguments + (default std.tostring) +
  • +
+ +

Returns:

+
    + + functable + memoized function +
+ + + +

Usage:

+
    +
    local fast = memoize (function (...) --[[ slow code ]] end)
    +
+ +
+
+ + nop () +
+
+ No operation. + This function ignores all arguments, and returns no values. + + + + + +

See also:

+ + +

Usage:

+
    +
    if unsupported then vtable["memrmem"] = nop end
    +
+ +
+
+ + reduce (fn, d[, ifn=std.pairs], ...) +
+
+ Fold a binary function into an iterator. + + +

Parameters:

+
    +
  • fn + func + reduce function +
  • +
  • d + initial first argument +
  • +
  • ifn + func + iterator function + (default std.pairs) +
  • +
  • ... + iterator arguments +
  • +
+ +

Returns:

+
    + + result +
+ + +

See also:

+ + +

Usage:

+
    +
    + --> 2 ^ 3 ^ 4 ==> 4096
    + reduce (std.operator.pow, 2, std.ielems, {3, 4})
    +
+ +
+
+ + zip (tt) +
+
+ Zip a table of tables. + Make a new table, with lists of elements at the same index in the + original table. This function is effectively its own inverse. + + +

Parameters:

+
    +
  • tt + table + a table of tables +
  • +
+ +

Returns:

+
    + + table + new table with lists of elements of the same key + from tt +
+ + +

See also:

+ + +

Usage:

+
    +
    + --> {{1, 3, 5}, {2, 4}}, {a={x=1, y=3, z=5}, b={x=2, y=4}}
    + zip {{1, 2}, {3, 4}, {5}}, zip {x={a=1, b=2}, y={a=3, b=4}, z={a=5}}
    +
+ +
+
+ + zip_with (fn, tt) +
+
+ Zip a list of tables together with a function. + + +

Parameters:

+
    +
  • fn + function + function +
  • +
  • tt + table + table of tables +
  • +
+ +

Returns:

+
    + + table + a new table of results from calls to fn with arguments + made from all elements the same key in the original tables; effectively + the "columns" in a simple list + of lists. +
+ + +

See also:

+ + +

Usage:

+
    +
    + --> {"135", "24"}, {a="1", b="25"}
    + conc = bind (zip_with, {lambda '|...|table.concat {...}'})
    + conc {{1, 2}, {3, 4}, {5}}, conc {{a=1, b=2}, x={a=3, b=4}, {b=5}}
    +
+ +
+
+

Types

+ + +
+
+ + normalize (...) +
+
+ Signature of a memoize argument normalization callback function. + + +

Parameters:

+
    +
  • ... + arguments +
  • +
+ +

Returns:

+
    + + string + normalized arguments +
+ + + +

Usage:

+
    +
    + local normalize = function (name, value, props) return name end
    + local intern = std.functional.memoize (mksymbol, normalize)
    +
+ +
+
+ + predicate (...) +
+
+ Signature of a filter predicate callback function. + + +

Parameters:

+
    +
  • ... + arguments +
  • +
+ +

Returns:

+
    + + boolean + "truthy" if the predicate condition succeeds, + "falsey" otherwise +
+ + + +

Usage:

+
    +
    + local predicate = lambda '|k,v|type(v)=="string"'
    + local strvalues = filter (predicate, std.pairs, {name="Roberto", id=12345})
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/modules/std.html b/doc/modules/std.html new file mode 100644 index 0000000..40f7de8 --- /dev/null +++ b/doc/modules/std.html @@ -0,0 +1,850 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std

+

Lua Standard Libraries.

+

This module contains a selection of improved Lua core functions, among + others.

+ +

Also, after requiring this module, simply referencing symbols in the + submodule hierarchy will load the necessary modules on demand.

+ +

By default there are no changes to any global symbols, or monkey + patching of core module tables and metatables. However, sometimes it's + still convenient to do that: For example, when using stdlib from the + REPL, or in a prototype where you want to throw caution to the wind and + compatibility with other modules be damned. In that case, you can give + stdlib permission to scribble all over your namespaces by using the + various monkey_patch calls in the library.

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
assert (expect[, f=""[, ...]])Enhance core assert to also allow formatted arguments.
barrel ([namespace=_G])A barrel of monkey_patches.
elems (t)An iterator over all elements of a sequence.
eval (s)Evaluate a string as Lua code.
getmetamethod (x, n)Return named metamethod, if any, otherwise nil.
ielems (t)An iterator over the integer keyed elements of a sequence.
ipairs (t)An iterator over elements of a sequence, until the first nil value.
ireverse (t)Return a new table with element order reversed.
monkey_patch ([namespace=_G])Overwrite core methods and metamethods with std enhanced versions.
npairs (t)Ordered iterator for integer keyed values.
pairs (t)Enhance core pairs to respect __pairs even in Lua 5.1.
require (module[, min[, too_big[, pattern]]])Enhance core require to assert version number compatibility.
ripairs (t)An iterator like ipairs, but in reverse.
rnpairs (t)An iterator like npairs, but in reverse.
tostring (x)Enhance core tostring to render table contents as a string.
+

Tables

+ + + + + +
stdModule table.
+

Metamethods

+ + + + + +
__index (name)Lazy loading of stdlib modules.
+ +
+
+ + +

Functions

+ Methods +
+
+ + assert (expect[, f=""[, ...]]) +
+
+ Enhance core assert to also allow formatted arguments. + + +

Parameters:

+
    +
  • expect + expression, expected to be truthy +
  • +
  • f + string + format string + (default "") +
  • +
  • ... + arguments to format + (optional) +
  • +
+ +

Returns:

+
    + + value of expect, if truthy +
+ + + +

Usage:

+
    +
    + std.assert (expected ~= nil, "100% unexpected!")
    + std.assert (expected ~= nil, "%s unexpected!", expected)
    +
+ +
+
+ + barrel ([namespace=_G]) +
+
+ A barrel of monkey_patches.

+ +

Apply all monkey_patch functions. Additionally, for backwards + compatibility only, write a selection of sub-module functions into + the given namespace. + + +

Parameters:

+
    +
  • namespace + table + where to install global functions + (default _G) +
  • +
+ +

Returns:

+
    + + table + module table +
+ + + +

Usage:

+
    +
    local std = require "std".barrel ()
    +
+ +
+
+ + elems (t) +
+
+ An iterator over all elements of a sequence. + If t has a __pairs metamethod, use that to iterate. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + table + t, the table being iterated over
  4. +
  5. + key, the previous iteration key
  6. +
+ + +

See also:

+ + +

Usage:

+
    +
    for value in std.elems {a = 1, b = 2, c = 5} do process (value) end
    +
+ +
+
+ + eval (s) +
+
+ Evaluate a string as Lua code. + + +

Parameters:

+
    +
  • s + string + string of Lua code +
  • +
+ +

Returns:

+
    + + result of evaluating s +
+ + + +

Usage:

+
    +
    std.eval "math.min (2, 10)"
    +
+ +
+
+ + getmetamethod (x, n) +
+
+ Return named metamethod, if any, otherwise nil. + + +

Parameters:

+
    +
  • x + item to act on +
  • +
  • n + string + name of metamethod to lookup +
  • +
+ +

Returns:

+
    + + function or nil + metamethod function, or nil if no metamethod +
+ + + +

Usage:

+
    +
    lookup = std.getmetamethod (require "std.object", "__index")
    +
+ +
+
+ + ielems (t) +
+
+ An iterator over the integer keyed elements of a sequence. + If t has a __len metamethod, iterate up to the index it returns. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + table + t, the table being iterated over
  4. +
  5. + int + index, the previous iteration index
  6. +
+ + +

See also:

+ + +

Usage:

+
    +
    for v in std.ielems {"a", "b", "c"} do process (v) end
    +
+ +
+
+ + ipairs (t) +
+
+ An iterator over elements of a sequence, until the first nil value.

+ +

Like Lua 5.1 and 5.3, but unlike Lua 5.2 (which looks for and uses the + __ipairs metamethod), this iterator returns successive key-value + pairs with integer keys starting at 1, up to the first nil valued + pair. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + table + t, the table being iterated over
  4. +
  5. + int + index, the previous iteration index
  6. +
+ + +

See also:

+ + +

Usage:

+
    +
    + -- length of sequence
    + args = {"first", "second", nil, "last"}
    + --> 1=first
    + --> 2=second
    + for i, v in std.ipairs (args) do
    +   print (string.format ("%d=%s", i, v))
    + end
    +
+ +
+
+ + ireverse (t) +
+
+ Return a new table with element order reversed. + Apart from the order of the elments returned, this function follows + the same rules as ipairs for determining first and last elements. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    + + table + a new table with integer keyed elements in reverse + order with respect to t +
+ + +

See also:

+ + +

Usage:

+
    +
    + local rielems = std.functional.compose (std.ireverse, std.ielems)
    + for e in rielems (l) do process (e) end
    +
+ +
+
+ + monkey_patch ([namespace=_G]) +
+
+ Overwrite core methods and metamethods with std enhanced versions.

+ +

Write all functions from this module, except std.barrel and + std.monkey_patch, into the given namespace. + + +

Parameters:

+
    +
  • namespace + table + where to install global functions + (default _G) +
  • +
+ +

Returns:

+
    + + table + the module table +
+ + + +

Usage:

+
    +
    local std = require "std".monkey_patch ()
    +
+ +
+
+ + npairs (t) +
+
+ Ordered iterator for integer keyed values. + Like ipairs, but does not stop until the largest integer key. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + table + t
  4. +
+ + +

See also:

+ + +

Usage:

+
    +
    for i,v in npairs {"one", nil, "three"} do ... end
    +
+ +
+
+ + pairs (t) +
+
+ Enhance core pairs to respect __pairs even in Lua 5.1. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + table + t, the table being iterated over
  4. +
  5. + key, the previous iteration key
  6. +
+ + +

See also:

+ + +

Usage:

+
    +
    for k, v in pairs {"a", b = "c", foo = 42} do process (k, v) end
    +
+ +
+
+ + require (module[, min[, too_big[, pattern]]]) +
+
+ Enhance core require to assert version number compatibility. + By default match against the last substring of (dot-delimited) + digits in the module version string. + + +

Parameters:

+
    +
  • module + string + module to require +
  • +
  • min + string + lowest acceptable version + (optional) +
  • +
  • too_big + string + lowest version that is too big + (optional) +
  • +
  • pattern + string + to match version in module.version or + module._VERSION (default: "([%.%d]+)%D*$") + (optional) +
  • +
+ + + + +

Usage:

+
    +
    + -- posix.version == "posix library for Lua 5.2 / 32"
    + posix = require ("posix", "29")
    +
+ +
+
+ + ripairs (t) +
+
+ An iterator like ipairs, but in reverse. + Apart from the order of the elments returned, this function follows + the same rules as ipairs for determining first and last elements. + + +

Parameters:

+
    +
  • t + table + any table +
  • +
+ +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + table + t
  4. +
  5. + number + #t + 1
  6. +
+ + +

See also:

+ + +

Usage:

+
    +
    for i, v = ripairs (t) do ... end
    +
+ +
+
+ + rnpairs (t) +
+
+ An iterator like npairs, but in reverse. + Apart from the order of the elments returned, this function follows + the same rules as npairs for determining first and last elements. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + table + t
  4. +
+ + +

See also:

+ + +

Usage:

+
    +
    for i,v in rnpairs {"one", nil, "three"} do ... end
    +
+ +
+
+ + tostring (x) +
+
+ Enhance core tostring to render table contents as a string. + + +

Parameters:

+
    +
  • x + object to convert to string +
  • +
+ +

Returns:

+
    + + string + compact string rendering of x +
+ + + +

Usage:

+
    +
    + -- {1=baz,foo=bar}
    + print (std.tostring {foo="bar","baz"})
    +
+ +
+
+

Tables

+ +
+
+ + std +
+
+ Module table.

+ +

In addition to the functions documented on this page, and a version + field, references to other submodule functions will be loaded on + demand. + + +

Fields:

+
    +
  • version + release version string +
  • +
+ + + + + +
+
+

Metamethods

+ + +
+
+ + __index (name) +
+
+ 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. + + +

Parameters:

+
    +
  • name + string + submodule name +
  • +
+ +

Returns:

+
    + + table or nil + the submodule that was loaded to satisfy the missing + name, otherwise nil if nothing was found +
+ + + +

Usage:

+
    +
    + local std = require "std"
    + local prototype = std.object.prototype
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/modules/std.io.html b/doc/modules/std.io.html new file mode 100644 index 0000000..f69b83a --- /dev/null +++ b/doc/modules/std.io.html @@ -0,0 +1,618 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.io

+

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"
+
+ +

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
catdir (...)Concatenate directory names into a path.
catfile (...)Concatenate one or more directories and a filename into a path.
die (msg, ...)Die with error.
dirname (path)Remove the last dirsep delimited element from a path.
monkey_patch ([namespace=_G])Overwrite core io methods with std enhanced versions.
process_files (fn)Process files specified on the command-line.
readlines ([file=io.input()])Read a file or file handle into a list of lines.
shell (c)Perform a shell command and return its output.
slurp ([file=io.input()])Slurp a file handle.
splitdir (path)Split a directory path into components.
warn (msg, ...)Give warning with the name of program and file (if any).
writelines ([h=io.output()], ...)Write values adding a newline after each.
+

Types

+ + + + + +
fileprocessor (filename, i)Signature of process_files callback function.
+ +
+
+ + +

Functions

+ Methods +
+
+ + catdir (...) +
+
+ Concatenate directory names into a path. + + +

Parameters:

+
    +
  • ... + string + path components +
  • +
+ +

Returns:

+
    + + path without trailing separator +
+ + +

See also:

+ + +

Usage:

+
    +
    dirpath = catdir ("", "absolute", "directory")
    +
+ +
+
+ + catfile (...) +
+
+ Concatenate one or more directories and a filename into a path. + + +

Parameters:

+
    +
  • ... + string + path components +
  • +
+ +

Returns:

+
    + + string + path +
+ + +

See also:

+ + +

Usage:

+
    +
    filepath = catfile ("relative", "path", "filename")
    +
+ +
+
+ + die (msg, ...) +
+
+ Die with error. + This function uses the same rules to build a message prefix + as warn. + + +

Parameters:

+
    +
  • msg + string + format string +
  • +
  • ... + additional arguments to plug format string specifiers +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    die ("oh noes! (%s)", tostring (obj))
    +
+ +
+
+ + dirname (path) +
+
+ Remove the last dirsep delimited element from a path. + + +

Parameters:

+
    +
  • path + string + file path +
  • +
+ +

Returns:

+
    + + string + a new path with the last dirsep and following + truncated +
+ + + +

Usage:

+
    +
    dir = dirname "/base/subdir/filename"
    +
+ +
+
+ + monkey_patch ([namespace=_G]) +
+
+ Overwrite core io methods with std enhanced versions.

+ +

Also adds readlines and writelines metamethods to core file objects. + + +

Parameters:

+
    +
  • namespace + table + where to install global functions + (default _G) +
  • +
+ +

Returns:

+
    + + table + the std.io module table +
+ + + +

Usage:

+
    +
    local io = require "std.io".monkey_patch ()
    +
+ +
+
+ + process_files (fn) +
+
+ 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. + + +

Parameters:

+
    +
  • fn + fileprocessor + 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)
    +
+ +
+
+ + readlines ([file=io.input()]) +
+
+ Read a file or file handle into a list of lines. + The lines in the returned list are not \n terminated. + + +

Parameters:

+
    +
  • file + file or string + file handle or name; + if file is a file handle, that file is closed after reading + (default io.input()) +
  • +
+ +

Returns:

+
    + + list + lines +
+ + + +

Usage:

+
    +
    list = readlines "/etc/passwd"
    +
+ +
+
+ + shell (c) +
+
+ Perform a shell command and return its output. + + +

Parameters:

+ + +

Returns:

+
    + + string + output, or nil if error +
+ + +

See also:

+ + +

Usage:

+
    +
    users = shell [[cat /etc/passwd | awk -F: '{print $1;}']]
    +
+ +
+
+ + slurp ([file=io.input()]) +
+
+ Slurp a file handle. + + +

Parameters:

+
    +
  • file + file or string + file handle or name; + if file is a file handle, that file is closed after reading + (default io.input()) +
  • +
+ +

Returns:

+
    + + contents of file or handle, or nil if error +
+ + +

See also:

+ + +

Usage:

+
    +
    contents = slurp (filename)
    +
+ +
+
+ + splitdir (path) +
+
+ Split a directory path into components. + Empty components are retained: the root directory becomes {"", ""}. + + +

Parameters:

+
    +
  • path + path +
  • +
+ +

Returns:

+
    + + list of path components +
+ + +

See also:

+ + +

Usage:

+
    +
    dir_components = splitdir (filepath)
    +
+ +
+
+ + warn (msg, ...) +
+
+ 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. std.optparse:parse + returns an opts table that provides the required program + field, as long as you assign it back to _G.opts. + + +

Parameters:

+
    +
  • msg + string + format string +
  • +
  • ... + additional arguments to plug format string specifiers +
  • +
+ + + +

See also:

+ + +

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
    +
+ +
+
+ + writelines ([h=io.output()], ...) +
+
+ Write values adding a newline after each. + + +

Parameters:

+
    +
  • h + file + open writable file handle; + the file is not closed after writing + (default io.output()) +
  • +
  • ... + string or number + values to write (as for write) +
  • +
+ + + + +

Usage:

+
    +
    writelines (io.stdout, "first line", "next line")
    +
+ +
+
+

Types

+ + +
+
+ + fileprocessor (filename, i) +
+
+ Signature of process_files callback function. + + +

Parameters:

+
    +
  • filename + string + filename +
  • +
  • i + int + 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)
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/modules/std.math.html b/doc/modules/std.math.html new file mode 100644 index 0000000..8fd2b94 --- /dev/null +++ b/doc/modules/std.math.html @@ -0,0 +1,222 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.math

+

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"
+
+ +

+ + +

Functions

+ + + + + + + + + + + + + +
floor (n[, p=0])Extend math.floor to take the number of decimal places.
monkey_patch ([namespace=_G])Overwrite core math methods with std enhanced versions.
round (n[, p=0])Round a number to a given number of decimal places
+ +
+
+ + +

Functions

+ Methods +
+
+ + floor (n[, p=0]) +
+
+ Extend math.floor to take the number of decimal places. + + +

Parameters:

+
    +
  • n + number + number +
  • +
  • p + int + number of decimal places to truncate to + (default 0) +
  • +
+ +

Returns:

+
    + + number + n truncated to p decimal places +
+ + + +

Usage:

+
    +
    tenths = floor (magnitude, 1)
    +
+ +
+
+ + monkey_patch ([namespace=_G]) +
+
+ Overwrite core math methods with std enhanced versions. + + +

Parameters:

+
    +
  • namespace + table + where to install global functions + (default _G) +
  • +
+ +

Returns:

+
    + + table + the module table +
+ + + +

Usage:

+
    +
    require "std.math".monkey_patch ()
    +
+ +
+
+ + round (n[, p=0]) +
+
+ Round a number to a given number of decimal places + + +

Parameters:

+
    +
  • n + number + number +
  • +
  • p + int + number of decimal places to round to + (default 0) +
  • +
+ +

Returns:

+
    + + number + n rounded to p decimal places +
+ + + +

Usage:

+
    +
    roughly = round (exactly, 2)
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/modules/std.operator.html b/doc/modules/std.operator.html new file mode 100644 index 0000000..0ebf53f --- /dev/null +++ b/doc/modules/std.operator.html @@ -0,0 +1,755 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.operator

+

Functional forms of Lua operators.

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
concat (a, b)Stringify and concatenate arguments.
conj (a, b)Return the logical conjunction of the arguments.
diff (a, b)Return the difference of the arguments.
disj (a, b)Return the logical disjunction of the arguments.
eq (a, b)Return the equality of the arguments.
get (t, k)Dereference a table.
gt (a, b)Return whether the arguments are in descending order.
gte (a, b)Return whether the arguments are not in ascending order.
lt (a, b)Return whether the arguments are in ascending order.
lte (a, b)Return whether the arguments are not in descending order.
mod (a, b)Return the modulus of the arguments.
neg (a)Return the logical negation of the arguments.
neq (a, b)Return the inequality of the arguments.
pow (a, b)Return the exponent of the arguments.
prod (a, b)Return the product of the arguments.
quot (a, b)Return the quotient of the arguments.
set (t, k, v)Set a table element, honoring metamethods.
sum (a, b)Return the sum of the arguments.
+ +
+
+ + +

Functions

+ Methods +
+
+ + concat (a, b) +
+
+ Stringify and concatenate arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + concatenation of stringified arguments. +
+ + + +

Usage:

+
    +
    + --> "=> 1000010010"
    + functional.foldl (concat, "=> ", {10000, 100, 10})
    +
+ +
+
+ + conj (a, b) +
+
+ Return the logical conjunction of the arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + logical a and b +
+ + + +

Usage:

+
    +
    + --> true
    + functional.foldl (conj, {true, 1, "false"})
    +
+ +
+
+ + diff (a, b) +
+
+ Return the difference of the arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + the difference between a and b +
+ + + +

Usage:

+
    +
    + --> 890
    + functional.foldl (diff, {10000, 100, 10})
    +
+ +
+
+ + disj (a, b) +
+
+ Return the logical disjunction of the arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + logical a or b +
+ + + +

Usage:

+
    +
    + --> true
    + functional.foldl (disj, {true, 1, false})
    +
+ +
+
+ + eq (a, b) +
+
+ Return the equality of the arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + true if a is b, otherwise false +
+ + + + +
+
+ + get (t, k) +
+
+ Dereference a table. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
  • k + a key to lookup in t +
  • +
+ +

Returns:

+
    + + value stored at t[k] if any, otherwise nil +
+ + + +

Usage:

+
    +
    + --> 4
    + functional.foldl (get, {1, {{2, 3, 4}, 5}}, {2, 1, 3})
    +
+ +
+
+ + gt (a, b) +
+
+ Return whether the arguments are in descending order. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + true if a is greater then b, otherwise false +
+ + + + +
+
+ + gte (a, b) +
+
+ Return whether the arguments are not in ascending order. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + true if a is not greater then b, otherwise false +
+ + + + +
+
+ + lt (a, b) +
+
+ Return whether the arguments are in ascending order. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + true if a is less then b, otherwise false +
+ + + + +
+
+ + lte (a, b) +
+
+ Return whether the arguments are not in descending order. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + true if a is not greater then b, otherwise false +
+ + + + +
+
+ + mod (a, b) +
+
+ Return the modulus of the arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + the modulus of a and b +
+ + + +

Usage:

+
    +
    + --> 3
    + functional.foldl (mod, {65536, 100, 11})
    +
+ +
+
+ + neg (a) +
+
+ Return the logical negation of the arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
+ +

Returns:

+
    + + not a +
+ + + +

Usage:

+
    +
    + --> {true, false, false, false}
    + functional.bind (functional.map, {std.ielems, neg}) {false, true, 1, 0}
    +
+ +
+
+ + neq (a, b) +
+
+ Return the inequality of the arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + false if a is b, otherwise true +
+ + + +

Usage:

+
    +
    + --> true
    + local f = require "std.functional"
    + table.empty (f.filter (f.bind (neq, {6}), std.ielems, {6, 6, 6})
    +
+ +
+
+ + pow (a, b) +
+
+ Return the exponent of the arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + the a to the power of b +
+ + + +

Usage:

+
    +
    + --> 4096
    + functional.foldl (pow, {2, 3, 4})
    +
+ +
+
+ + prod (a, b) +
+
+ Return the product of the arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + the product of a and b +
+ + + +

Usage:

+
    +
    + --> 10000000
    + functional.foldl (prod, {10000, 100, 10})
    +
+ +
+
+ + quot (a, b) +
+
+ Return the quotient of the arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + the quotient a and b +
+ + + +

Usage:

+
    +
    + --> 1000
    + functional.foldr (quot, {10000, 100, 10})
    +
+ +
+
+ + set (t, k, v) +
+
+ Set a table element, honoring metamethods. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
  • k + a key to lookup in t +
  • +
  • v + a value to set for k +
  • +
+ +

Returns:

+
    + + table + t +
+ + + +

Usage:

+
    +
    + -- destructive table merge:
    + --> {"one", bar="baz", two=5}
    + functional.reduce (set, {"foo", bar="baz"}, {"one", two=5})
    +
+ +
+
+ + sum (a, b) +
+
+ Return the sum of the arguments. + + +

Parameters:

+
    +
  • a + an argument +
  • +
  • b + another argument +
  • +
+ +

Returns:

+
    + + the sum of the a and b +
+ + + +

Usage:

+
    +
    + --> 10110
    + functional.foldl (sum, {10000, 100, 10})
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/modules/std.package.html b/doc/modules/std.package.html new file mode 100644 index 0000000..c1d7e16 --- /dev/null +++ b/doc/modules/std.package.html @@ -0,0 +1,418 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.package

+

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"
+
+ +

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + +
find (pathstrings, patt[, init=1[, plain=false]])Look for a path segment match of patt in pathstrings.
insert (pathstrings[, pos=n+1], value)Insert a new element into a package.path like string of paths.
mappath (pathstrings, callback, ...)Call a function with each element of a path string.
normalize (...)Normalize a path list.
remove (pathstrings[, pos=n])Remove any element from a package.path like string of paths.
+

Tables

+ + + + + +
packageMake named constants for package.config + (undocumented in 5.1; see luaconf.h for C equivalents).
+

Types

+ + + + + +
mappathcb (element, ...)Function signature of a callback for mappath.
+ +
+
+ + +

Functions

+ Methods +
+
+ + find (pathstrings, patt[, init=1[, plain=false]]) +
+
+ Look for a path segment match of patt in pathstrings. + + +

Parameters:

+
    +
  • pathstrings + string + pathsep delimited path elements +
  • +
  • patt + string + a Lua pattern to search for in pathstrings +
  • +
  • init + int + element (not byte index!) to start search at. + Negative numbers begin counting backwards from the last element + (default 1) +
  • +
  • plain + bool + unless false, treat patt as a plain + string, not a pattern. Note that if plain is given, then init + must be given as well. + (default false) +
  • +
+ +

Returns:

+
    + + 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 .. "/]")
    +
+ +
+
+ + insert (pathstrings[, pos=n+1], value) +
+
+ Insert a new element into a package.path like string of paths. + + +

Parameters:

+
    +
  • pathstrings + string + a package.path like string +
  • +
  • pos + int + element index at which to insert value, where n is + the number of elements prior to insertion + (default n+1) +
  • +
  • value + string + new path element to insert +
  • +
+ +

Returns:

+
    + + string + a new string with the new element inserted +
+ + + +

Usage:

+
    +
    package.path = insert (package.path, 1, install_dir .. "/?.lua")
    +
+ +
+
+ + mappath (pathstrings, callback, ...) +
+
+ Call a function with each element of a path string. + + +

Parameters:

+
    +
  • pathstrings + string + a package.path like string +
  • +
  • callback + mappathcb + function to call for each element +
  • +
  • ... + additional arguments passed to callback +
  • +
+ +

Returns:

+
    + + nil, or first non-nil returned by callback +
+ + + +

Usage:

+
    +
    mappath (package.path, searcherfn, transformfn)
    +
+ +
+
+ + normalize (...) +
+
+ 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 + path_mark (unless immediately preceded by a % character). + + +

Parameters:

+
    +
  • ... + path elements +
  • +
+ +

Returns:

+
    + + string + a single normalized pathsep delimited paths string +
+ + + +

Usage:

+
    +
    package.path = normalize (user_paths, sys_paths, package.path)
    +
+ +
+
+ + remove (pathstrings[, pos=n]) +
+
+ Remove any element from a package.path like string of paths. + + +

Parameters:

+
    +
  • pathstrings + string + a package.path like string +
  • +
  • pos + int + element index from which to remove an item, where n + is the number of elements prior to removal + (default n) +
  • +
+ +

Returns:

+
    + + string + a new string with given element removed +
+ + + +

Usage:

+
    +
    package.path = remove (package.path)
    +
+ +
+
+

Tables

+ +
+
+ + package +
+
+ Make named constants for package.config + (undocumented in 5.1; see luaconf.h for C equivalents). + + +

Fields:

+
    +
  • dirsep + string + directory separator +
  • +
  • pathsep + string + path separator +
  • +
  • path_mark + string + string that marks substitution points in a path template +
  • +
  • execdir + string + (Windows only) replaced by the executable's directory in a path +
  • +
  • igmark + string + Mark to ignore all before it when building luaopen_ function name. +
  • +
+ + + + + +
+
+

Types

+ + +
+
+ + mappathcb (element, ...) +
+
+ Function signature of a callback for mappath. + + +

Parameters:

+
    +
  • element + string + an element from a pathsep delimited string of + paths +
  • +
  • ... + additional arguments propagated from mappath +
  • +
+ +

Returns:

+
    + + non-nil to break, otherwise continue with the next element +
+ + + + +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/modules/std.strict.html b/doc/modules/std.strict.html new file mode 100644 index 0000000..ff52f9e --- /dev/null +++ b/doc/modules/std.strict.html @@ -0,0 +1,161 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.strict

+

Checks uses of undeclared global variables.

+

All global variables must be 'declared' through a regular + assignment (even assigning nil will do) in a top-level + chunk before being used anywhere or assigned to inside a function.

+ +

To use this module, just require it near the start of your program.

+ +

From Lua distribution (etc/strict.lua).

+ + +

Functions

+ + + + + + + + + +
__index (t, n)Detect dereference of undeclared global.
__newindex (t, n, v)Detect assignment to undeclared global.
+ +
+
+ + +

Functions

+ Methods +
+
+ + __index (t, n) +
+
+ Detect dereference of undeclared global. + + +

Parameters:

+
    +
  • t + table + _G +
  • +
  • n + string + name of the variable being dereferenced +
  • +
+ + + + + +
+
+ + __newindex (t, n, v) +
+
+ Detect assignment to undeclared global. + + +

Parameters:

+
    +
  • t + table + _G +
  • +
  • n + string + name of the variable being declared +
  • +
  • v + initial value of the variable +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/modules/std.string.html b/doc/modules/std.string.html new file mode 100644 index 0000000..e04aad9 --- /dev/null +++ b/doc/modules/std.string.html @@ -0,0 +1,1214 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.string

+

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"
+
+ +

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
caps (s)Capitalise each word in a string.
chomp (s)Remove any final newline from a string.
escape_pattern (s)Escape a string to be used as a pattern.
escape_shell (s)Escape a string to be used as a shell token.
finds (s, pattern[, init=1[, plain]])Repeatedly string.find until target string is exhausted.
format (f[, ...])Extend to work better with one argument.
ltrim (s[, r="%s+"])Remove leading matter from a string.
monkey_patch ([namespace=_G])Overwrite core string methods with std enhanced versions.
numbertosi (n)Write a number using SI suffixes.
ordinal_suffix (n)Return the English suffix for an ordinal.
pad (s, w[, p=" "])Justify a string.
prettytostring (x[, indent="\t"[, spacing=""]])Pretty-print a table, or other object.
render (x, open, close, elem, pair, sep[, roots])Turn tables into strings with recursion detection.
rtrim (s[, r="%s+"])Remove trailing matter from a string.
split (s[, sep="%s+"])Split a string at a given separator.
tfind (s, pattern[, init=1[, plain]])Do string.find, returning a table of captures.
trim (s[, r="%s+"])Remove leading and trailing matter from a string.
wrap (s[, w=78[, ind=0[, ind1=ind]]])Wrap a string into a paragraph.
+

Fields

+ + + + + + + + + + + + + +
__concatString concatenation operation.
__indexString subscript operation.
pickleConvert a value to a string.
+

Types

+ + + + + + + + + + + + + + + + + + + + + +
closetablecb (t)Signature of render close table callback.
elementcb (x)Signature of render element callback.
opentablecb (t)Signature of render open table callback.
paircb (t, key, value, keystr, valuestr)Signature of render pair callback.
separatorcb (t, pk, pv, fk, fv)Signature of render separator callback.
+ +
+
+ + +

Functions

+ Methods +
+
+ + caps (s) +
+
+ Capitalise each word in a string. + + +

Parameters:

+
    +
  • s + string + any string +
  • +
+ +

Returns:

+
    + + string + s with each word capitalized +
+ + + +

Usage:

+
    +
    userfullname = caps (input_string)
    +
+ +
+
+ + chomp (s) +
+
+ Remove any final newline from a string. + + +

Parameters:

+
    +
  • s + string + any string +
  • +
+ +

Returns:

+
    + + string + s with any single trailing newline removed +
+ + + +

Usage:

+
    +
    line = chomp (line)
    +
+ +
+
+ + escape_pattern (s) +
+
+ Escape a string to be used as a pattern. + + +

Parameters:

+
    +
  • s + string + any string +
  • +
+ +

Returns:

+
    + + string + s with active pattern characters escaped +
+ + + +

Usage:

+
    +
    substr = inputstr:match (escape_pattern (literal))
    +
+ +
+
+ + escape_shell (s) +
+
+ Escape a string to be used as a shell token. + Quotes spaces, parentheses, brackets, quotes, apostrophes and + whitespace. + + +

Parameters:

+
    +
  • s + string + any string +
  • +
+ +

Returns:

+
    + + string + s with active shell characters escaped +
+ + + +

Usage:

+
    +
    os.execute ("echo " .. escape_shell (outputstr))
    +
+ +
+
+ + finds (s, pattern[, init=1[, plain]]) +
+
+ Repeatedly string.find until target string is exhausted. + + +

Parameters:

+
    +
  • s + string + target string +
  • +
  • pattern + string + pattern to match in s +
  • +
  • init + int + start position + (default 1) +
  • +
  • plain + bool + inhibit magic characters + (optional) +
  • +
+ +

Returns:

+
    + + list of {from, to; capt = {captures}} +
+ + +

See also:

+ + +

Usage:

+
    +
    + for t in std.elems (finds ("the target string", "%S+")) do
    +   print (tostring (t.capt))
    + end
    +
+ +
+
+ + format (f[, ...]) +
+
+ Extend to work better with one argument. + If only one argument is passed, no formatting is attempted. + + +

Parameters:

+
    +
  • f + string + format string +
  • +
  • ... + arguments to format + (optional) +
  • +
+ +

Returns:

+
    + + formatted string +
+ + + +

Usage:

+
    +
    print (format "100% stdlib!")
    +
+ +
+
+ + ltrim (s[, r="%s+"]) +
+
+ Remove leading matter from a string. + + +

Parameters:

+
    +
  • s + string + any string +
  • +
  • r + string + leading pattern + (default "%s+") +
  • +
+ +

Returns:

+
    + + string + s with leading r stripped +
+ + + +

Usage:

+
    +
    print ("got: " .. ltrim (userinput))
    +
+ +
+
+ + monkey_patch ([namespace=_G]) +
+
+ Overwrite core string methods with std enhanced versions.

+ +

Also adds auto-stringification to .. operator on core strings, and + integer indexing of strings with [] dereferencing. + + +

Parameters:

+
    +
  • namespace + table + where to install global functions + (default _G) +
  • +
+ +

Returns:

+
    + + table + the module table +
+ + + +

Usage:

+
    +
    local string = require "std.string".monkey_patch ()
    +
+ +
+
+ + numbertosi (n) +
+
+ Write a number using SI suffixes. + The number is always written to 3 s.f. + + +

Parameters:

+
    +
  • n + number or string + any numeric value +
  • +
+ +

Returns:

+
    + + string + n simplifed using largest available SI suffix. +
+ + + +

Usage:

+
    +
    print (numbertosi (bitspersecond) .. "bps")
    +
+ +
+
+ + ordinal_suffix (n) +
+
+ Return the English suffix for an ordinal. + + +

Parameters:

+
    +
  • n + int or string + any integer value +
  • +
+ +

Returns:

+
    + + string + English suffix for n +
+ + + +

Usage:

+
    +
    + local now = os.date "*t"
    + print ("%d%s day of the week", now.day, ordinal_suffix (now.day))
    +
+ +
+
+ + pad (s, w[, p=" "]) +
+
+ Justify a string. + When the string is longer than w, it is truncated (left or right + according to the sign of w). + + +

Parameters:

+
    +
  • s + string + a string to justify +
  • +
  • w + int + width to justify to (-ve means right-justify; +ve means + left-justify) +
  • +
  • p + string + string to pad with + (default " ") +
  • +
+ +

Returns:

+
    + + string + s justified to w characters wide +
+ + + +

Usage:

+
    +
    print (pad (trim (outputstr, 78)) .. "\n")
    +
+ +
+
+ + prettytostring (x[, indent="\t"[, spacing=""]]) +
+
+ Pretty-print a table, or other object. + + +

Parameters:

+
    +
  • x + object to convert to string +
  • +
  • indent + string + indent between levels + (default "\t") +
  • +
  • spacing + string + space before every line + (default "") +
  • +
+ +

Returns:

+
    + + string + pretty string rendering of x +
+ + + +

Usage:

+
    +
    print (prettytostring (std, "  "))
    +
+ +
+
+ + render (x, open, close, elem, pair, sep[, roots]) +
+
+ Turn tables into strings with recursion detection. + N.B. Functions calling render should not recurse, or recursion + detection will not work. + + +

Parameters:

+
    +
  • x + object to convert to string +
  • +
  • open + opentablecb + open table rendering function +
  • +
  • close + closetablecb + close table rendering function +
  • +
  • elem + elementcb + element rendering function +
  • +
  • pair + paircb + pair rendering function +
  • +
  • sep + separatorcb + separator rendering function +
  • +
  • roots + table + accumulates table references to detect recursion + (optional) +
  • +
+ +

Returns:

+
    + + string representation of x +
+ + + +

Usage:

+
    +
    + function tostring (x)
    +   return render (x, lambda '="{"', lambda '="}"', tostring,
    +                  lambda '=_4.."=".._5', lambda '= _4 and "," or ""',
    +                  lambda '=","')
    + end
    +
+ +
+
+ + rtrim (s[, r="%s+"]) +
+
+ Remove trailing matter from a string. + + +

Parameters:

+
    +
  • s + string + any string +
  • +
  • r + string + trailing pattern + (default "%s+") +
  • +
+ +

Returns:

+
    + + string + s with trailing r stripped +
+ + + +

Usage:

+
    +
    print ("got: " .. rtrim (userinput))
    +
+ +
+
+ + split (s[, sep="%s+"]) +
+
+ 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. + + +

Parameters:

+
    +
  • s + string + to split +
  • +
  • sep + string + separator pattern + (default "%s+") +
  • +
+ +

Returns:

+
    + + list of strings +
+ + + +

Usage:

+
    +
    words = split "a very short sentence"
    +
+ +
+
+ + tfind (s, pattern[, init=1[, plain]]) +
+
+ Do string.find, returning a table of captures. + + +

Parameters:

+
    +
  • s + string + target string +
  • +
  • pattern + string + pattern to match in s +
  • +
  • init + int + start position + (default 1) +
  • +
  • plain + bool + inhibit magic characters + (optional) +
  • +
+ +

Returns:

+
    +
  1. + int + start of match
  2. +
  3. + int + end of match
  4. +
  5. + table + list of captured strings
  6. +
+ + +

See also:

+ + +

Usage:

+
    +
    b, e, captures = tfind ("the target string", "%s", 10)
    +
+ +
+
+ + trim (s[, r="%s+"]) +
+
+ Remove leading and trailing matter from a string. + + +

Parameters:

+
    +
  • s + string + any string +
  • +
  • r + string + trailing pattern + (default "%s+") +
  • +
+ +

Returns:

+
    + + string + s with leading and trailing r stripped +
+ + + +

Usage:

+
    +
    print ("got: " .. trim (userinput))
    +
+ +
+
+ + wrap (s[, w=78[, ind=0[, ind1=ind]]]) +
+
+ Wrap a string into a paragraph. + + +

Parameters:

+
    +
  • s + string + a paragraph of text +
  • +
  • w + int + width to wrap to + (default 78) +
  • +
  • ind + int + indent + (default 0) +
  • +
  • ind1 + int + indent of first line + (default ind) +
  • +
+ +

Returns:

+
    + + string + s wrapped to w columns +
+ + + +

Usage:

+
    +
    print (wrap (copyright, 72, 4))
    +
+ +
+
+

Fields

+ +
+
+ + __concat +
+
+ String concatenation operation. + + +
    +
  • s + string + initial string +
  • +
  • o + object to stringify and concatenate +
  • +
+ + + + +

Usage:

+
    +
    + local string = require "std.string".monkey_patch ()
    + concatenated = "foo" .. {"bar"}
    +
+ +
+
+ + __index +
+
+ String subscript operation. + + +
    +
  • s + string + string +
  • +
  • i + int or string + index or method name +
  • +
+ + + + +

Usage:

+
    +
    + getmetatable ("").__index = require "std.string".__index
    + third = ("12345")[3]
    +
+ +
+
+ + pickle +
+
+ Convert a value to a string. + The string can be passed to functional.eval to retrieve the value. + + +
    +
  • x + object to pickle +
  • +
+ + + +

See also:

+ + +

Usage:

+
    +
    function slow_identity (x) return functional.eval (pickle (x)) end
    +
+ +
+
+

Types

+ + +
+
+ + closetablecb (t) +
+
+ Signature of render close table callback. + + +

Parameters:

+
    +
  • t + table + table just rendered +
  • +
+ +

Returns:

+
    + + string + close table rendering +
+ + +

See also:

+ + +

Usage:

+
    +
    function close (t) return "}" end
    +
+ +
+
+ + elementcb (x) +
+
+ Signature of render element callback. + + +

Parameters:

+
    +
  • x + element to render +
  • +
+ +

Returns:

+
    + + string + element rendering +
+ + +

See also:

+ + +

Usage:

+
    +
    function element (e) return require "std".tostring (e) end
    +
+ +
+
+ + opentablecb (t) +
+
+ Signature of render open table callback. + + +

Parameters:

+
    +
  • t + table + table about to be rendered +
  • +
+ +

Returns:

+
    + + string + open table rendering +
+ + +

See also:

+ + +

Usage:

+
    +
    function open (t) return "{" end
    +
+ +
+
+ + paircb (t, key, value, keystr, valuestr) +
+
+ Signature of render pair callback. + Trying to re-render key or value here will break recursion + detection, use strkey and strvalue pre-rendered values instead. + + +

Parameters:

+
    +
  • t + table + table containing pair being rendered +
  • +
  • key + key part of key being rendered +
  • +
  • value + value part of key being rendered +
  • +
  • keystr + string + prerendered key +
  • +
  • valuestr + string + prerendered value +
  • +
+ +

Returns:

+
    + + string + pair rendering +
+ + +

See also:

+ + +

Usage:

+
    +
    function pair (_, _, _, key, value) return key .. "=" .. value end
    +
+ +
+
+ + separatorcb (t, pk, pv, fk, fv) +
+
+ Signature of render separator callback. + + +

Parameters:

+
    +
  • t + table + table currently being rendered +
  • +
  • pk + t key preceding separator, or nil for first key +
  • +
  • pv + t value preceding separator, or nil for first value +
  • +
  • fk + t key following separator, or nil for last key +
  • +
  • fv + t value following separator, or nil for last value +
  • +
+ +

Returns:

+
    + + string + separator rendering +
+ + + +

Usage:

+
    +
    function separator (_, _, _, fk) return fk and "," or "" end
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/doc/modules/std.table.html b/doc/modules/std.table.html new file mode 100644 index 0000000..2184872 --- /dev/null +++ b/doc/modules/std.table.html @@ -0,0 +1,1172 @@ + + + + + stdlib 41.2.2 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.table

+

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"
+
+ +

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
clone (t[, map={}[, nometa]])Make a shallow copy of a table, including any metatable.
clone_select (t[, keys={}[, nometa]])Make a partial clone of a table.
depair (ls)Turn a list of pairs into a table.
empty (t)Return whether table is empty.
enpair (t)Turn a table into a list of pairs.
flatten (t)Flatten a nested table into a list.
insert (t[, pos=len (t)], v)Enhance core table.insert to return its result.
invert (t)Invert a table.
keys (t)Make the list of keys in table.
len (t)Equivalent to # operation, but respecting __len even on Lua 5.1.
maxn (t)Largest integer key in a table.
merge (t, u[, map={}[, nometa]])Destructively merge another table's fields into another.
merge_select (t, u[, keys={}[, nometa]])Destructively merge another table's named fields into table.
monkey_patch ([namespace=_G])Overwrite core table methods with std enhanced versions.
new ([x=nil[, t={}]])Make a table with a default value for unset keys.
okeys (t)Make an ordered list of keys in table.
pack (...)Turn a tuple into a list.
project (fkey, tt)Project a list of fields from a list of tables.
remove (t[, pos=len (t)])Enhance core table.remove to respect __len when pos is omitted.
shape (dims, t)Shape a table according to a list of dimensions.
size (t)Find the number of elements in a table.
sort (t[, c=std.operator.lt])Enhance core table.sort to return its result.
unpack (t[, i=1[, j=table.maxn(t)]])Enhance core table.unpack to always unpack up to maxn (t).
values (t)Make the list of values of a table.
+

Types

+ + + + + +
comparator (a, b)Signature of a sort comparator function.
+ +
+
+ + +

Functions

+ Methods +
+
+ + clone (t[, map={}[, nometa]]) +
+
+ Make a shallow copy of a table, including any metatable.

+ +

To make deep copies, use tree.clone. + + +

Parameters:

+
    +
  • t + table + source table +
  • +
  • map + table + table of {old_key=new_key, ...} + (default {}) +
  • +
  • nometa + bool + if non-nil don't copy metatable + (optional) +
  • +
+ +

Returns:

+
    + + copy of t, also sharing t's metatable unless nometa + is true, and with keys renamed according to map +
+ + +

See also:

+ + +

Usage:

+
    +
    shallowcopy = clone (original, {rename_this = "to_this"}, ":nometa")
    +
+ +
+
+ + clone_select (t[, keys={}[, nometa]]) +
+
+ Make a partial clone of a table.

+ +

Like clone, but does not copy any fields by default. + + +

Parameters:

+
    +
  • t + table + source table +
  • +
  • keys + table + list of keys to copy + (default {}) +
  • +
  • nometa + bool + if non-nil don't copy metatable + (optional) +
  • +
+ +

Returns:

+
    + + table + copy of fields in selection from t, also sharing t's + metatable unless nometa +
+ + +

See also:

+ + +

Usage:

+
    +
    partialcopy = clone_select (original, {"this", "and_this"}, true)
    +
+ +
+
+ + depair (ls) +
+
+ Turn a list of pairs into a table. + + +

Parameters:

+
    +
  • ls + table + list of lists +
  • +
+ +

Returns:

+
    + + table + a flat table with keys and values from ls +
+ + +

See also:

+ + +

Usage:

+
    +
    + --> {a=1, b=2, c=3}
    + depair {{"a", 1}, {"b", 2}, {"c", 3}}
    +
+ +
+
+ + empty (t) +
+
+ Return whether table is empty. + + +

Parameters:

+
    +
  • t + table + any table +
  • +
+ +

Returns:

+
    + + boolean + true if t is empty, otherwise false +
+ + + +

Usage:

+
    +
    if empty (t) then error "ohnoes" end
    +
+ +
+
+ + enpair (t) +
+
+ Turn a table into a list of pairs. + + +

Parameters:

+
    +
  • t + table + a table {i1=v1, ..., in=vn} +
  • +
+ +

Returns:

+
    + + table + a new list of pairs containing {{i1, v1}, ..., {in, vn}} +
+ + +

See also:

+ + +

Usage:

+
    +
    + --> {{1, "a"}, {2, "b"}, {3, "c"}}
    + enpair {"a", "b", "c"}
    +
+ +
+
+ + flatten (t) +
+
+ Flatten a nested table into a list. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    + + table + a list of all non-table elements of t +
+ + + +

Usage:

+
    +
    + --> {1, 2, 3, 4, 5}
    + flatten {{1, {{2}, 3}, 4}, 5}
    +
+ +
+
+ + insert (t[, pos=len (t)], v) +
+
+ 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. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
  • pos + int + index at which to insert new element + (default len (t)) +
  • +
  • v + value to insert into t +
  • +
+ +

Returns:

+
    + + table + t +
+ + + +

Usage:

+
    +
    + --> {1, "x", 2, 3, "y"}
    + insert (insert ({1, 2, 3}, 2, "x"), "y")
    +
+ +
+
+ + invert (t) +
+
+ Invert a table. + + +

Parameters:

+
    +
  • t + table + a table with {k=v, ...} +
  • +
+ +

Returns:

+
    + + table + inverted table {v=k, ...} +
+ + + +

Usage:

+
    +
    + --> {a=1, b=2, c=3}
    + invert {"a", "b", "c"}
    +
+ +
+
+ + keys (t) +
+
+ Make the list of keys in table. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    + + table + list of keys from t +
+ + +

See also:

+ + +

Usage:

+
    +
    globals = keys (_G)
    +
+ +
+
+ + len (t) +
+
+ Equivalent to # operation, but respecting __len even on Lua 5.1. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    + + int + length of list part of t +
+ + + +

Usage:

+
    +
    for i = 1, len (t) do process (t[i]) end
    +
+ +
+
+ + maxn (t) +
+
+ Largest integer key in a table. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    + + int + largest integer key in t +
+ + + +

Usage:

+
    +
    + --> 42
    + maxn {"a", b="c", 99, [42]="x", "x", [5]=67}
    +
+ +
+
+ + merge (t, u[, map={}[, nometa]]) +
+
+ Destructively merge another table's fields into another. + + +

Parameters:

+
    +
  • t + table + destination table +
  • +
  • u + table + table with fields to merge +
  • +
  • map + table + table of {old_key=new_key, ...} + (default {}) +
  • +
  • nometa + bool + if true or ":nometa" don't copy metatable + (optional) +
  • +
+ +

Returns:

+
    + + table + t with fields from u merged in +
+ + +

See also:

+ + +

Usage:

+
    +
    merge (_G, require "std.debug", {say = "log"}, ":nometa")
    +
+ +
+
+ + merge_select (t, u[, keys={}[, nometa]]) +
+
+ Destructively merge another table's named fields into table.

+ +

Like merge, but does not merge any fields by default. + + +

Parameters:

+
    +
  • t + table + destination table +
  • +
  • u + table + table with fields to merge +
  • +
  • keys + table + list of keys to copy + (default {}) +
  • +
  • nometa + bool + if true or ":nometa" don't copy metatable + (optional) +
  • +
+ +

Returns:

+
    + + table + copy of fields in selection from t, also sharing t's + metatable unless nometa +
+ + +

See also:

+ + +

Usage:

+
    +
    merge_select (_G, require "std.debug", {"say"}, false)
    +
+ +
+
+ + monkey_patch ([namespace=_G]) +
+
+ Overwrite core table methods with std enhanced versions. + + +

Parameters:

+
    +
  • namespace + table + where to install global functions + (default _G) +
  • +
+ +

Returns:

+
    + + table + the module table +
+ + + +

Usage:

+
    +
    local table = require "std.table".monkey_patch ()
    +
+ +
+
+ + new ([x=nil[, t={}]]) +
+
+ Make a table with a default value for unset keys. + + +

Parameters:

+
    +
  • x + default entry value + (default nil) +
  • +
  • t + table + initial table + (default {}) +
  • +
+ +

Returns:

+
    + + table + table whose unset elements are x +
+ + + +

Usage:

+
    +
    t = new (0)
    +
+ +
+
+ + okeys (t) +
+
+ Make an ordered list of keys in table. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
+ +

Returns:

+
    + + table + ordered list of keys from t +
+ + +

See also:

+ + +

Usage:

+
    +
    globals = keys (_G)
    +
+ +
+
+ + pack (...) +
+
+ Turn a tuple into a list. + + +

Parameters:

+
    +
  • ... + tuple +
  • +
+ +

Returns:

+
    + + list +
+ + + +

Usage:

+
    +
    + --> {1, 2, "ax"}
    + pack (("ax1"):find "(%D+)")
    +
+ +
+
+ + project (fkey, tt) +
+
+ Project a list of fields from a list of tables. + + +

Parameters:

+
    +
  • fkey + field to project +
  • +
  • tt + table + a list of tables +
  • +
+ +

Returns:

+
    + + 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"})
    +
+ +
+
+ + remove (t[, pos=len (t)]) +
+
+ 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. + + +

Parameters:

+
    +
  • t + table + a table +
  • +
  • pos + int + index from which to remove an element + (default len (t)) +
  • +
+ +

Returns:

+
    + + removed value, or else nil +
+ + + +

Usage:

+
    +
    + --> {1, 2, 5}
    + t = {1, 2, "x", 5}
    + remove (t, 3) == "x" and t
    +
+ +
+
+ + shape (dims, t) +
+
+ Shape a table according to a list of dimensions.

+ +

Dimensions are given outermost first and items from the original + list are distributed breadth first; there may be one 0 indicating + an indefinite number. Hence, {0} is a flat list, + {1} is a singleton, {2, 0} is a list of + two lists, and {0, 2} is a list of pairs.

+ +

Algorithm: turn shape into all positive numbers, calculating + the zero if necessary and making sure there is at most one; + recursively walk the shape, adding empty tables until the bottom + level is reached at which point add table items instead, using a + counter to walk the flattened original list. + + +

Parameters:

+
    +
  • dims + table + table of dimensions {d1, ..., dn} +
  • +
  • t + table + a table of elements +
  • +
+ +

Returns:

+
    + + reshaped list +
+ + + +

Usage:

+
    +
    + --> {{"a", "b"}, {"c", "d"}, {"e", "f"}}
    + shape ({3, 2}, {"a", "b", "c", "d", "e", "f"})
    +
+ +
+
+ + size (t) +
+
+ Find the number of elements in a table. + + +

Parameters:

+
    +
  • t + table + any table +
  • +
+ +

Returns:

+
    + + int + number of non-nil values in t +
+ + + +

Usage:

+
    +
    + --> 3
    + size {foo = true, bar = true, baz = false}
    +
+ +
+
+ + sort (t[, c=std.operator.lt]) +
+
+ Enhance core table.sort to return its result. + + +

Parameters:

+
    +
  • t + table + unsorted table +
  • +
  • c + comparator + ordering function callback + (default std.operator.lt) +
  • +
+ +

Returns:

+
    + + t with keys sorted accordind to c +
+ + + +

Usage:

+
    +
    table.concat (sort (object))
    +
+ +
+
+ + unpack (t[, i=1[, j=table.maxn(t)]]) +
+
+ Enhance core table.unpack to always unpack up to maxn (t). + + +

Parameters:

+
    +
  • t + table + table to act on +
  • +
  • i + int + first index to unpack + (default 1) +
  • +
  • j + int + last index to unpack + (default table.maxn(t)) +
  • +
+ +

Returns:

+
    + + ... values of numeric indices of t +
+ + + +

Usage:

+
    +
    return unpack (results_table)
    +
+ +
+
+ + values (t) +
+
+ Make the list of values of a table. + + +

Parameters:

+
    +
  • t + table + any table +
  • +
+ +

Returns:

+
    + + table + list of values in t +
+ + +

See also:

+ + +

Usage:

+
    +
    + --> {"a", "c", 42}
    + values {"a", b="c", [-1]=42}
    +
+ +
+
+

Types

+ + +
+
+ + comparator (a, b) +
+
+ Signature of a sort comparator function. + + +

Parameters:

+
    +
  • a + any object +
  • +
  • b + any object +
  • +
+ +

Returns:

+
    + + boolean + true if a sorts before b, otherwise false +
+ + +

See also:

+ + +

Usage:

+
    +
    + local reversor = function (a, b) return a > b end
    + sort (t, reversor)
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2018-09-03 17:48:42 +
+
+ + diff --git a/lib/std.lua b/lib/std.lua new file mode 100644 index 0000000..31b1311 --- /dev/null +++ b/lib/std.lua @@ -0,0 +1,304 @@ +--[[-- + Lua Standard Libraries. + + This module contains a selection of improved Lua core functions, among + others. + + Also, after requiring this module, simply referencing symbols in the + submodule hierarchy will load the necessary modules on demand. + + By default there are no changes to any global symbols, or monkey + patching of core module tables and metatables. However, sometimes it's + still convenient to do that: For example, when using stdlib from the + REPL, or in a prototype where you want to throw caution to the wind and + compatibility with other modules be damned. In that case, you can give + `stdlib` permission to scribble all over your namespaces by using the + various `monkey_patch` calls in the library. + + @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. + @module std +]] + + +local base = require "std.base" + +local M, monkeys + + +local function monkey_patch (namespace) + base.copy (namespace or _G, monkeys) + return M +end + + +local function barrel (namespace) + namespace = namespace or _G + + -- Older releases installed the following into _G by default. + for _, name in pairs { + "functional.bind", "functional.collect", "functional.compose", + "functional.curry", "functional.filter", "functional.id", + "functional.map", + + "io.die", "io.warn", + + "string.pickle", "string.prettytostring", "string.render", + + "table.pack", + + "tree.ileaves", "tree.inodes", "tree.leaves", "tree.nodes", + } do + local module, method = name:match "^(.*)%.(.-)$" + namespace[method] = M[module][method] + end + + -- Support old api names, for backwards compatibility. + namespace.fold = M.functional.fold + namespace.metamethod = M.getmetamethod + namespace.op = M.operator + namespace.require_version = M.require + + require "std.io".monkey_patch (namespace) + require "std.math".monkey_patch (namespace) + require "std.string".monkey_patch (namespace) + require "std.table".monkey_patch (namespace) + + return monkey_patch (namespace) +end + + + +--- Module table. +-- +-- In addition to the functions documented on this page, and a `version` +-- field, references to other submodule functions will be loaded on +-- demand. +-- @table std +-- @field version release version string + +local function X (decl, fn) + return require "std.debug".argscheck ("std." .. decl, fn) +end + +M = { + --- 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 (expected ~= nil, "100% unexpected!") + -- std.assert (expected ~= nil, "%s unexpected!", expected) + assert = X ("assert (?any, ?string, [any...])", base.assert), + + --- A [barrel of monkey_patches](http://dictionary.reference.com/browse/barrel+of+monkeys). + -- + -- Apply **all** `monkey_patch` functions. Additionally, for backwards + -- compatibility only, write a selection of sub-module functions into + -- the given namespace. + -- @function barrel + -- @tparam[opt=_G] table namespace where to install global functions + -- @treturn table module table + -- @usage local std = require "std".barrel () + barrel = X ("barrel (?table)", barrel), + + --- An iterator over all elements of a sequence. + -- 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 + -- for value in std.elems {a = 1, b = 2, c = 5} do process (value) end + elems = X ("elems (table)", base.elems), + + --- Evaluate a string as Lua code. + -- @function eval + -- @string s string of Lua code + -- @return result of evaluating `s` + -- @usage std.eval "math.min (2, 10)" + eval = X ("eval (string)", base.eval), + + --- An iterator over the integer keyed elements of a sequence. + -- If *t* has a `__len` metamethod, iterate up to the index it returns. + -- @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 + -- for v in std.ielems {"a", "b", "c"} do process (v) end + ielems = X ("ielems (table)", base.ielems), + + --- An iterator over elements of a sequence, until the first `nil` value. + -- + -- Like Lua 5.1 and 5.3, but unlike Lua 5.2 (which looks for and uses the + -- `__ipairs` metamethod), this iterator returns successive key-value + -- pairs with integer keys starting at 1, up to the first `nil` valued + -- pair. + -- @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 + -- -- length of sequence + -- args = {"first", "second", nil, "last"} + -- --> 1=first + -- --> 2=second + -- for i, v in std.ipairs (args) do + -- print (string.format ("%d=%s", i, v)) + -- end + ipairs = X ("ipairs (table)", base.ipairs), + + --- Return a new table with element order reversed. + -- Apart from the order of the elments returned, this function follows + -- the same rules as @{ipairs} for determining first and last elements. + -- @function ireverse + -- @tparam table t a table + -- @treturn table a new table with integer keyed elements in reverse + -- order with respect to *t* + -- @see ielems + -- @see ipairs + -- @usage + -- local rielems = std.functional.compose (std.ireverse, std.ielems) + -- for e in rielems (l) do process (e) end + ireverse = X ("ireverse (table)", base.ireverse), + + --- Return named metamethod, if any, otherwise `nil`. + -- @function getmetamethod + -- @param x item to act on + -- @string n name of metamethod to lookup + -- @treturn function|nil metamethod function, or `nil` if no metamethod + -- @usage lookup = std.getmetamethod (require "std.object", "__index") + getmetamethod = X ("getmetamethod (?any, string)", base.getmetamethod), + + --- Overwrite core methods and metamethods with `std` enhanced versions. + -- + -- Write all functions from this module, except `std.barrel` and + -- `std.monkey_patch`, into the given namespace. + -- @function monkey_patch + -- @tparam[opt=_G] table namespace where to install global functions + -- @treturn table the module table + -- @usage local std = require "std".monkey_patch () + monkey_patch = X ("monkey_patch (?table)", monkey_patch), + + --- Ordered iterator for integer keyed values. + -- Like ipairs, but does not stop until the largest integer key. + -- @function npairs + -- @tparam table t a table + -- @treturn function iterator function + -- @treturn table t + -- @see ipairs + -- @see rnpairs + -- @usage + -- for i,v in npairs {"one", nil, "three"} do ... end + npairs = X ("npairs (table)", base.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 + -- for k, v in pairs {"a", b = "c", foo = 42} do process (k, v) end + pairs = X ("pairs (table)", base.pairs), + + --- 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)", base.require), + + --- An iterator like ipairs, but in reverse. + -- Apart from the order of the elments 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 for i, v = ripairs (t) do ... end + ripairs = X ("ripairs (table)", base.ripairs), + + --- An iterator like npairs, but in reverse. + -- Apart from the order of the elments 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 + -- for i,v in rnpairs {"one", nil, "three"} do ... end + rnpairs = X ("rnpairs (table)", base.rnpairs), + + --- Enhance core `tostring` to render table contents as a string. + -- @function tostring + -- @param x object to convert to string + -- @treturn string compact string rendering of *x* + -- @usage + -- -- {1=baz,foo=bar} + -- print (std.tostring {foo="bar","baz"}) + tostring = X ("tostring (?any)", base.tostring), + + version = "General Lua libraries / 41.2.2", +} + + +monkeys = base.copy ({}, M) + +-- Don't monkey_patch these apis into _G! +for _, api in ipairs {"barrel", "monkey_patch", "version"} do + monkeys[api] = nil +end + + +--- 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 prototype = 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/lib/std.lua.in b/lib/std.lua.in new file mode 100644 index 0000000..925a750 --- /dev/null +++ b/lib/std.lua.in @@ -0,0 +1,304 @@ +--[[-- + Lua Standard Libraries. + + This module contains a selection of improved Lua core functions, among + others. + + Also, after requiring this module, simply referencing symbols in the + submodule hierarchy will load the necessary modules on demand. + + By default there are no changes to any global symbols, or monkey + patching of core module tables and metatables. However, sometimes it's + still convenient to do that: For example, when using stdlib from the + REPL, or in a prototype where you want to throw caution to the wind and + compatibility with other modules be damned. In that case, you can give + `stdlib` permission to scribble all over your namespaces by using the + various `monkey_patch` calls in the library. + + @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. + @module std +]] + + +local base = require "std.base" + +local M, monkeys + + +local function monkey_patch (namespace) + base.copy (namespace or _G, monkeys) + return M +end + + +local function barrel (namespace) + namespace = namespace or _G + + -- Older releases installed the following into _G by default. + for _, name in pairs { + "functional.bind", "functional.collect", "functional.compose", + "functional.curry", "functional.filter", "functional.id", + "functional.map", + + "io.die", "io.warn", + + "string.pickle", "string.prettytostring", "string.render", + + "table.pack", + + "tree.ileaves", "tree.inodes", "tree.leaves", "tree.nodes", + } do + local module, method = name:match "^(.*)%.(.-)$" + namespace[method] = M[module][method] + end + + -- Support old api names, for backwards compatibility. + namespace.fold = M.functional.fold + namespace.metamethod = M.getmetamethod + namespace.op = M.operator + namespace.require_version = M.require + + require "std.io".monkey_patch (namespace) + require "std.math".monkey_patch (namespace) + require "std.string".monkey_patch (namespace) + require "std.table".monkey_patch (namespace) + + return monkey_patch (namespace) +end + + + +--- Module table. +-- +-- In addition to the functions documented on this page, and a `version` +-- field, references to other submodule functions will be loaded on +-- demand. +-- @table std +-- @field version release version string + +local function X (decl, fn) + return require "std.debug".argscheck ("std." .. decl, fn) +end + +M = { + --- 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 (expected ~= nil, "100% unexpected!") + -- std.assert (expected ~= nil, "%s unexpected!", expected) + assert = X ("assert (?any, ?string, [any...])", base.assert), + + --- A [barrel of monkey_patches](http://dictionary.reference.com/browse/barrel+of+monkeys). + -- + -- Apply **all** `monkey_patch` functions. Additionally, for backwards + -- compatibility only, write a selection of sub-module functions into + -- the given namespace. + -- @function barrel + -- @tparam[opt=_G] table namespace where to install global functions + -- @treturn table module table + -- @usage local std = require "std".barrel () + barrel = X ("barrel (?table)", barrel), + + --- An iterator over all elements of a sequence. + -- 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 + -- for value in std.elems {a = 1, b = 2, c = 5} do process (value) end + elems = X ("elems (table)", base.elems), + + --- Evaluate a string as Lua code. + -- @function eval + -- @string s string of Lua code + -- @return result of evaluating `s` + -- @usage std.eval "math.min (2, 10)" + eval = X ("eval (string)", base.eval), + + --- An iterator over the integer keyed elements of a sequence. + -- If *t* has a `__len` metamethod, iterate up to the index it returns. + -- @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 + -- for v in std.ielems {"a", "b", "c"} do process (v) end + ielems = X ("ielems (table)", base.ielems), + + --- An iterator over elements of a sequence, until the first `nil` value. + -- + -- Like Lua 5.1 and 5.3, but unlike Lua 5.2 (which looks for and uses the + -- `__ipairs` metamethod), this iterator returns successive key-value + -- pairs with integer keys starting at 1, up to the first `nil` valued + -- pair. + -- @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 + -- -- length of sequence + -- args = {"first", "second", nil, "last"} + -- --> 1=first + -- --> 2=second + -- for i, v in std.ipairs (args) do + -- print (string.format ("%d=%s", i, v)) + -- end + ipairs = X ("ipairs (table)", base.ipairs), + + --- Return a new table with element order reversed. + -- Apart from the order of the elments returned, this function follows + -- the same rules as @{ipairs} for determining first and last elements. + -- @function ireverse + -- @tparam table t a table + -- @treturn table a new table with integer keyed elements in reverse + -- order with respect to *t* + -- @see ielems + -- @see ipairs + -- @usage + -- local rielems = std.functional.compose (std.ireverse, std.ielems) + -- for e in rielems (l) do process (e) end + ireverse = X ("ireverse (table)", base.ireverse), + + --- Return named metamethod, if any, otherwise `nil`. + -- @function getmetamethod + -- @param x item to act on + -- @string n name of metamethod to lookup + -- @treturn function|nil metamethod function, or `nil` if no metamethod + -- @usage lookup = std.getmetamethod (require "std.object", "__index") + getmetamethod = X ("getmetamethod (?any, string)", base.getmetamethod), + + --- Overwrite core methods and metamethods with `std` enhanced versions. + -- + -- Write all functions from this module, except `std.barrel` and + -- `std.monkey_patch`, into the given namespace. + -- @function monkey_patch + -- @tparam[opt=_G] table namespace where to install global functions + -- @treturn table the module table + -- @usage local std = require "std".monkey_patch () + monkey_patch = X ("monkey_patch (?table)", monkey_patch), + + --- Ordered iterator for integer keyed values. + -- Like ipairs, but does not stop until the largest integer key. + -- @function npairs + -- @tparam table t a table + -- @treturn function iterator function + -- @treturn table t + -- @see ipairs + -- @see rnpairs + -- @usage + -- for i,v in npairs {"one", nil, "three"} do ... end + npairs = X ("npairs (table)", base.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 + -- for k, v in pairs {"a", b = "c", foo = 42} do process (k, v) end + pairs = X ("pairs (table)", base.pairs), + + --- 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)", base.require), + + --- An iterator like ipairs, but in reverse. + -- Apart from the order of the elments 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 for i, v = ripairs (t) do ... end + ripairs = X ("ripairs (table)", base.ripairs), + + --- An iterator like npairs, but in reverse. + -- Apart from the order of the elments 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 + -- for i,v in rnpairs {"one", nil, "three"} do ... end + rnpairs = X ("rnpairs (table)", base.rnpairs), + + --- Enhance core `tostring` to render table contents as a string. + -- @function tostring + -- @param x object to convert to string + -- @treturn string compact string rendering of *x* + -- @usage + -- -- {1=baz,foo=bar} + -- print (std.tostring {foo="bar","baz"}) + tostring = X ("tostring (?any)", base.tostring), + + version = "General Lua libraries / @VERSION@", +} + + +monkeys = base.copy ({}, M) + +-- Don't monkey_patch these apis into _G! +for _, api in ipairs {"barrel", "monkey_patch", "version"} do + monkeys[api] = nil +end + + +--- 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 prototype = 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/lib/std/base.lua b/lib/std/base.lua new file mode 100644 index 0000000..5f2139f --- /dev/null +++ b/lib/std/base.lua @@ -0,0 +1,499 @@ +--[[-- + 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. + + @module std.base +]] + + +local dirsep = string.match (package.config, "^(%S+)\n") +local loadstring = rawget (_G, "loadstring") or load + + +local function raise (bad, to, name, i, extramsg, level) + level = level or 1 + local s = string.format ("bad %s #%d %s '%s'", bad, i, to, name) + if extramsg ~= nil then + s = s .. " (" .. extramsg .. ")" + end + error (s, level + 1) +end + + +local function argerror (name, i, extramsg, level) + level = level or 1 + raise ("argument", "to", name, i, extramsg, level + 1) +end + + +local function assert (expect, fmt, arg1, ...) + local msg = (arg1 ~= nil) and string.format (fmt, arg1, ...) or fmt or "" + return expect or error (msg, 2) +end + + +local function getmetamethod (x, n) + local _, m = pcall (function (x) + return getmetatable (x)[n] + end, + x) + if type (m) ~= "function" then + m = nil + end + return m +end + + +local function callable (x) + if type (x) == "function" then return x end + return getmetamethod (x, "__call") +end + + +local function catfile (...) + return table.concat ({...}, dirsep) +end + + +-- Lua < 5.2 doesn't call `__len` automatically! +local function len (t) + local m = getmetamethod (t, "__len") + return m and m (t) or #t +end + + +-- Iterate over keys 1..n, where n is the key before the first nil +-- valued ordinal key (like Lua 5.3). +local function ipairs (l) + return function (l, n) + n = n + 1 + if l[n] ~= nil then + return n, l[n] + end + end, l, 0 +end + + +local _pairs = pairs + +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 + + +local _unpack = table.unpack or unpack + +local function unpack (t, i, j) + return _unpack (t, i or 1, j or maxn (t)) +end + + +local function compare (l, m) + local lenl, lenm = len (l), len (m) + for i = 1, math.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 _pairs = pairs + +-- Respect __pairs metamethod, even in Lua 5.1. +local function pairs (t) + return (getmetamethod (t, "__pairs") or _pairs) (t) +end + + +local function copy (dest, src) + if src == nil then dest, src = {}, dest end + for k, v in pairs (src) do dest[k] = v end + return dest +end + + +--- Iterator adaptor for discarding first value from core iterator function. +-- @func factory iterator to be wrapped +-- @param ... *factory* arguments +-- @treturn function iterator that discards first returned value of +-- factory iterator +-- @return invariant state from *factory* +-- @return `true` +-- @usage +-- for v in wrapiterator (ipairs {"a", "b", "c"}) do process (v) end +local function wrapiterator (factory, ...) + -- Capture wrapped ctrl variable into an upvalue... + local fn, istate, ctrl = factory (...) + -- Wrap the returned iterator fn to maintain wrapped ctrl. + return function (state, _) + local v + ctrl, v = fn (state, ctrl) + if ctrl then return v end + end, istate, true -- wrapped initial state, and wrapper ctrl +end + + +local function elems (t) + return wrapiterator (pairs, t) +end + + +local function escape_pattern (s) + return (s:gsub ("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0")) +end + + +local function eval (s) + return loadstring ("return " .. s)() +end + + +local function ielems (l) + return wrapiterator (ipairs, l) +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 invert (t) + local i = {} + for k, v in pairs (t) do + i[v] = k + end + return i +end + + +-- Be careful to reverse only the valid sequence part of a table. +local function ireverse (t) + local oob = 1 + while t[oob] ~= nil do + oob = oob + 1 + end + + local r = {} + for i = 1, oob - 1 do r[oob - i] = t[i] end + return r +end + + +-- Sort numbers first then asciibetically +local function keysort (a, b) + if type (a) == "number" then + return type (b) ~= "number" or a < b + else + return type (b) ~= "number" and tostring (a) < tostring (b) + end +end + + +local function okeys (t) + local r = {} + for k in pairs (t) do r[#r + 1] = k end + table.sort (r, keysort) + return r +end + + +local function last (t) return t[len (t)] 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 + coroutine.yield (n) + end + end + return coroutine.wrap (visit), tr +end + + +local function merge (dest, src) + for k, v in pairs (src) do dest[k] = dest[k] or v end + return dest +end + + +local function npairs (t) + local i, n = 0, maxn (t) + return function (t) + i = i + 1 + if i <= n then return i, t[i] end + end, + t, i +end + + +local function collect (ifn, ...) + local argt, r = {...}, {} + if not callable (ifn) then + ifn, argt = npairs, {ifn, ...} + end + + -- How many return values from ifn? + local arity = 1 + for e, v in ifn (unpack (argt)) do + if v then arity, r = 2, {} break end + -- Build an arity-1 result table on first pass... + r[#r + 1] = e + end + + if arity == 2 then + -- ...oops, it was arity-2 all along, start again! + for k, v in ifn (unpack (argt)) do + r[k] = v + end + end + + return r +end + + +local function prototype (o) + return (getmetatable (o) or {})._type or io.type (o) or type (o) +end + + +local function reduce (fn, d, ifn, ...) + local argt = {...} + if not callable (ifn) then + ifn, argt = pairs, {ifn, ...} + end + + local nextfn, state, k = ifn (unpack (argt)) + local t = {nextfn (state, k)} -- table of iteration 1 + + local r = d -- initialise accumulator + while t[1] ~= nil do -- until iterator returns nil + k = t[1] + r = fn (r, unpack (t)) -- pass all iterator results to fn + t = {nextfn (state, k)} -- maintain loop invariant + end + return r +end + + +-- Write pretty-printing based on: +-- +-- John Hughes's and Simon Peyton Jones's Pretty Printer Combinators +-- +-- Based on "The Design of a Pretty-printing Library in Advanced +-- Functional Programming", Johan Jeuring and Erik Meijer (eds), LNCS 925 +-- http://www.cs.chalmers.se/~rjmh/Papers/pretty.ps +-- Heavily modified by Simon Peyton Jones, Dec 96 + +local function render (x, opencb, closecb, elemcb, paircb, sepcb, roots) + roots = roots or {} + local function stop_roots (x) + return roots[x] or render (x, opencb, closecb, elemcb, paircb, sepcb, copy (roots)) + end + + if type (x) ~= "table" or getmetamethod (x, "__tostring") then + return elemcb (x) + else + local buf, k_, v_ = { opencb (x) } -- pre-buffer table open + roots[x] = elemcb (x) -- initialise recursion protection + + for _, k in ipairs (okeys (x)) do -- for ordered table members + local v = x[k] + buf[#buf + 1] = sepcb (x, k_, v_, k, v) -- | buffer separator + buf[#buf + 1] = paircb (x, k, v, stop_roots (k), stop_roots (v)) + -- | buffer key/value pair + k_, v_ = k, v + end + buf[#buf + 1] = sepcb (x, k_, v_) -- buffer trailing separator + buf[#buf + 1] = closecb (x) -- buffer table close + + return table.concat (buf) -- stringify buffer + end +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 oob = maxn (t) + 1 + + return function (t, n) + n = n - 1 + if n > 0 then + return n, t[n] + end + end, t, oob +end + + +local function split (s, sep) + local r, patt = {} + if sep == "" then + patt = "(.)" + insert (r, "") + else + patt = "(.-)" .. (sep or "%s+") + end + local b, lens = 0, len (s) + while b <= lens do + local e, n, m = string.find (s, patt, b + 1) + insert (r, m or s:sub (b + 1, lens)) + b = n or lens + 1 + end + return r +end + + +local function vcompare (a, b) + return compare (split (a, "%."), split (b, "%.")) +end + + +local _require = require + +local function require (module, min, too_big, pattern) + local m = _require (module) + local v = tostring (m.version or m._VERSION or ""):match (pattern or "([%.%d]+)%D*$") + 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 + + +local _tostring = _G.tostring + +local function tostring (x) + return render (x, + function () return "{" end, + function () return "}" end, + _tostring, + function (_, _, _, is, vs) return is .."=".. vs end, + function (_, i, _, k) return i and k and "," or "" end) +end + + + +return { + copy = copy, + keysort = keysort, + merge = merge, + okeys = okeys, + raise = raise, + + -- std.lua -- + assert = assert, + eval = eval, + elems = elems, + ielems = ielems, + ipairs = ipairs, + ireverse = ireverse, + npairs = npairs, + pairs = pairs, + ripairs = ripairs, + rnpairs = rnpairs, + require = require, + tostring = tostring, + + -- debug.lua -- + argerror = argerror, + + -- functional.lua -- + callable = callable, + collect = collect, + nop = function () end, + reduce = reduce, + + -- io.lua -- + catfile = catfile, + + -- list.lua -- + compare = compare, + + -- object.lua -- + prototype = prototype, + + -- package.lua -- + dirsep = dirsep, + + -- string.lua -- + escape_pattern = escape_pattern, + render = render, + split = split, + + -- table.lua -- + getmetamethod = getmetamethod, + insert = insert, + invert = invert, + last = last, + len = len, + maxn = maxn, + unpack = unpack, + + -- tree.lua -- + leaves = leaves, + +} diff --git a/lib/std/container.lua b/lib/std/container.lua new file mode 100644 index 0000000..3136d4e --- /dev/null +++ b/lib/std/container.lua @@ -0,0 +1,306 @@ +--[[-- + Container prototype. + + A container is a @{std.object} with no methods. It's functionality is + instead defined by its *meta*methods. + + Where an Object uses the `__index` metatable entry to hold object + methods, a Container stores its contents using `__index`, preventing + it from having methods in there too. + + Although there are no actual methods, Containers are free to use + metamethods (`__index`, `__sub`, etc) and, like Objects, can supply + module functions by listing them in `_functions`. Also, since a + @{std.container} is a @{std.object}, it can be passed to the + @{std.object} module functions, or anywhere else a @{std.object} is + expected. + + When making your own prototypes, derive from @{std.container} if you want + to access the contents of your objects with the `[]` operator, or from + @{std.object} if you want to access the functionality of your objects with + named object methods. + + Prototype Chain + --------------- + + table + `-> Object + `-> Container + + @classmod std.container +]] + + +local _DEBUG = require "std.debug_init"._DEBUG + +local base = require "std.base" +local debug = require "std.debug" + +local ipairs, pairs, okeys = base.ipairs, base.pairs, base.okeys +local insert, len, maxn = base.insert, base.len, base.maxn +local okeys, prototype, tostring = base.okeys, base.prototype, base.tostring +local argcheck = debug.argcheck + + + +--[[ ================= ]]-- +--[[ Helper Functions. ]]-- +--[[ ================= ]]-- + + +-- Instantiate a new object based on *proto*. +-- +-- This is equivalent to: +-- +-- table.merge (table.clone (proto), t or {}) +-- +-- But, not typechecking arguments or checking for metatables, is +-- slightly faster. +-- @tparam table proto base object to copy from +-- @tparam[opt={}] table t additional fields to merge in +-- @treturn table a new table with fields from proto and t merged in. +local function instantiate (proto, t) + local obj = {} + local k, v = next (proto) + while k do + obj[k] = v + k, v = next (proto, k) + end + + t = t or {} + k, v = next (t) + while k do + obj[k] = v + k, v = next (t, k) + end + return obj +end + + +local ModuleFunction = { + __tostring = function (self) return tostring (self.call) end, + __call = function (self, ...) return self.call (...) end, +} + + +--- Mark a function not to be copied into clones. +-- +-- It responds to `type` with `table`, but otherwise behaves like a +-- regular function. Marking uncopied module functions in-situ like this +-- (as opposed to doing book keeping in the metatable) means that we +-- don't have to create a new metatable with the book keeping removed for +-- cloned objects, we can just share our existing metatable directly. +-- @func fn a function +-- @treturn functable a callable functable for `fn` +local function modulefunction (fn) + if getmetatable (fn) == ModuleFunction then + -- Don't double wrap! + return fn + else + return setmetatable ({_type = "modulefunction", call = fn}, ModuleFunction) + end +end + + + +--[[ ================= ]]-- +--[[ Container Object. ]]-- +--[[ ================= ]]-- + + +local function mapfields (obj, src, map) + local mt = getmetatable (obj) or {} + + -- Map key pairs. + -- Copy all pairs when `map == nil`, but discard unmapped src keys + -- when map is provided (i.e. if `map == {}`, copy nothing). + if map == nil or next (map) then + map = map or {} + local k, v = next (src) + while k do + local key, dst = map[k] or k, obj + local kind = type (key) + if kind == "string" and key:sub (1, 1) == "_" then + mt[key] = v + elseif next (map) and kind == "number" and len (dst) + 1 < key then + -- When map is given, but has fewer entries than src, stop copying + -- fields when map is exhausted. + break + else + dst[key] = v + end + k, v = next (src, k) + end + end + + -- Quicker to remove this after copying fields than test for it + -- it on every iteration above. + mt._functions = nil + + -- Inject module functions. + local t = src._functions or {} + local k, v = next (t) + while (k) do + obj[k] = modulefunction (v) + k, v = next (t, k) + end + + -- Only set non-empty metatable. + if next (mt) then + setmetatable (obj, mt) + end + return obj +end + + +local function __call (self, x, ...) + local mt = getmetatable (self) + local obj_mt = mt + local obj = {} + + -- This is the slowest part of cloning for any objects that have + -- a lot of fields to test and copy. If you need to clone a lot of + -- objects from a prototype with several module functions, it's much + -- faster to clone objects from each other than the prototype! + local k, v = next (self) + while (k) do + if type (v) ~= "table" or v._type ~= "modulefunction" then + obj[k] = v + end + k, v = next (self, k) + end + + if type (mt._init) == "function" then + obj = mt._init (obj, x, ...) + else + obj = (self.mapfields or mapfields) (obj, x, mt._init) + end + + -- If a metatable was set, then merge our fields and use it. + if next (getmetatable (obj) or {}) then + obj_mt = instantiate (mt, getmetatable (obj)) + + -- Merge object methods. + if type (obj_mt.__index) == "table" and + type ((mt or {}).__index) == "table" + then + obj_mt.__index = instantiate (mt.__index, obj_mt.__index) + end + end + + return setmetatable (obj, obj_mt) +end + + +local function X (decl, fn) + return debug.argscheck ("std.container." .. decl, fn) +end + +local M = { + mapfields = X ("mapfields (table, table|object, ?table)", mapfields), +} + + +if _DEBUG.argcheck then + + local argerror, extramsg_toomany = debug.argerror, debug.extramsg_toomany + + M.__call = function (self, x, ...) + local mt = getmetatable (self) + + -- A function initialised object can be passed arguments of any + -- type, so only argcheck non-function initialised objects. + if type (mt._init) ~= "function" then + local name, argt = mt._type, {...} + -- Don't count `self` as an argument for error messages, because + -- it just refers back to the object being called: `Container {"x"}. + argcheck (name, 1, "table", x) + if next (argt) then + argerror (name, 2, extramsg_toomany ("argument", 1, 1 + maxn (argt)), 2) + end + end + + return __call (self, x, ...) + end + +else + + M.__call = __call + +end + + +function M.__tostring (self) + local n, k_ = 1, nil + local buf = { prototype (self), " {" } -- pre-buffer object open + for _, k in ipairs (okeys (self)) do -- for ordered public members + local v = self[k] + + if k_ ~= nil then -- | buffer separator + if k ~= n and type (k_) == "number" and k_ == n - 1 then + -- `;` separates `v` elements from `k=v` elements + buf[#buf + 1] = "; " + elseif k ~= nil then + -- `,` separator everywhere else + buf[#buf + 1] = ", " + end + end + + if type (k) == "number" and k == n then -- | buffer key/value pair + -- render initial array-like elements as just `v` + buf[#buf + 1] = tostring (v) + n = n + 1 + else + -- render remaining elements as `k=v` + buf[#buf + 1] = tostring (k) .. "=" .. tostring (v) + end + + k_ = k -- maintain loop invariant: k_ is previous key + end + buf[#buf + 1] = "}" -- buffer object close + + return table.concat (buf) -- stringify buffer +end + + +--- Container prototype. +-- +-- Container also inherits all the fields and methods from +-- @{std.object.Object}. +-- @object Container +-- @string[opt="Container"] _type object name +-- @see std.object +-- @see std.object.__call +-- @usage +-- local std = require "std" +-- local Container = std.container {} +-- +-- local Graph = Container { +-- _type = "Graph", +-- _functions = { +-- nodes = function (graph) +-- local n = 0 +-- for _ in std.pairs (graph) do n = n + 1 end +-- return n +-- end, +-- }, +-- } +-- local g = Graph { "node1", "node2" } +-- --> 2 +-- print (Graph.nodes (g)) + +return setmetatable ({ + + -- Normally, these are set and wrapped automatically during cloning. + -- But, we have to bootstrap the first object, so in this one instance + -- it has to be done manually. + + mapfields = modulefunction (M.mapfields), + prototype = modulefunction (prototype), +}, { + _type = "Container", + + __call = M.__call, + __tostring = M.__tostring, + __pairs = M.__pairs, +}) diff --git a/lib/std/debug.lua b/lib/std/debug.lua new file mode 100644 index 0000000..9657846 --- /dev/null +++ b/lib/std/debug.lua @@ -0,0 +1,902 @@ +--[[-- + 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" + + The behaviour of the functions in this module are controlled by the value + of the global `_DEBUG`. Not setting `_DEBUG` prior to requiring **any** of + stdlib's modules is equivalent to having `_DEBUG = true`. + + The first line of Lua code in production quality projects that use stdlib + should be either: + + _DEBUG = false + + or alternatively, if you need to be careful not to damage the global + environment: + + local init = require "std.debug_init" + init._DEBUG = false + + This mitigates almost all of the overhead of argument typechecking in + stdlib API functions. + + @module std.debug +]] + + +local debug_init = require "std.debug_init" +local base = require "std.base" + +local _DEBUG = debug_init._DEBUG +local argerror, raise = base.argerror, base.raise +local prototype, unpack = base.prototype, base.unpack +local copy, split, tostring = base.copy, base.split, base.tostring +local insert, last, len, maxn = base.insert, base.last, base.len, base.maxn +local ipairs, pairs = base.ipairs, base.pairs + + +local M + + +-- Return a deprecation message if _DEBUG.deprecate is `nil`, otherwise "". +local function DEPRECATIONMSG (version, name, extramsg, level) + if level == nil then level, extramsg = extramsg, nil end + extramsg = extramsg or "and will be removed entirely in a future release" + + local _, where = pcall (function () error ("", level + 3) end) + if _DEBUG.deprecate == nil then + return (where .. string.format ("%s was deprecated in release %s, %s.\n", + name, tostring (version), extramsg)) + end + + return "" +end + + +-- Define deprecated functions when _DEBUG.deprecate is not "truthy", +-- and write `DEPRECATIONMSG` output to stderr. +local function DEPRECATED (version, name, extramsg, fn) + if fn == nil then fn, extramsg = extramsg, nil end + + if not _DEBUG.deprecate then + return function (...) + io.stderr:write (DEPRECATIONMSG (version, name, extramsg, 2)) + return fn (...) + end + end +end + + +local _setfenv = debug.setfenv + +local function setfenv (fn, env) + -- Unwrap functable: + if type (fn) == "table" then + fn = fn.call or (getmetatable (fn) or {}).__call + end + + if _setfenv then + return _setfenv (fn, env) + + else + -- From http://lua-users.org/lists/lua-l/2010-06/msg00313.html + local name + local up = 0 + repeat + up = up + 1 + name = debug.getupvalue (fn, up) + until name == '_ENV' or name == nil + if name then + debug.upvaluejoin (fn, up, function () return name end, 1) + debug.setupvalue (fn, up, env) + end + + return fn + end +end + + +local _getfenv = rawget (_G, "getfenv") + +local getfenv = function (fn) + fn = fn or 1 + + -- Unwrap functable: + if type (fn) == "table" then + fn = fn.call or (getmetatable (fn) or {}).__call + end + + if _getfenv then + if type (fn) == "number" then fn = fn + 1 end + + -- Stack frame count is critical here, so ensure we don't optimise one + -- away in LuaJIT... + return _getfenv (fn), nil + + else + if type (fn) == "number" then + fn = debug.getinfo (fn + 1, "f").func + end + + local name, env + local up = 0 + repeat + up = up + 1 + name, env = debug.getupvalue (fn, up) + until name == '_ENV' or name == nil + return env + end +end + + +local function resulterror (name, i, extramsg, level) + level = level or 1 + raise ("result", "from", name, i, extramsg, level + 1) +end + + +local function extramsg_toomany (bad, expected, actual) + local s = "no more than %d %s%s expected, got %d" + return s:format (expected, bad, expected == 1 and "" or "s", actual) +end + + +--- Strip trailing ellipsis from final argument if any, storing maximum +-- number of values that can be matched directly in `t.maxvalues`. +-- @tparam table t table to act on +-- @string v element added to *t*, to match against ... suffix +-- @treturn table *t* with ellipsis stripped and maxvalues field set +local function markdots (t, v) + return (v:gsub ("%.%.%.$", function () t.dots = true return "" end)) +end + + +--- Calculate permutations of type lists with and without [optionals]. +-- @tparam table t a list of expected types by argument position +-- @treturn table set of possible type lists +local function permute (t) + if t[#t] then t[#t] = t[#t]:gsub ("%]%.%.%.$", "...]") end + + local p = {{}} + for i, v in ipairs (t) do + local optional = v:match "%[(.+)%]" + + if optional == nil then + -- Append non-optional type-spec to each permutation. + for b = 1, #p do + insert (p[b], markdots (p[b], v)) + end + else + -- Duplicate all existing permutations, and add optional type-spec + -- to the unduplicated permutations. + local o = #p + for b = 1, o do + p[b + o] = copy (p[b]) + insert (p[b], markdots (p[b], optional)) + end + end + end + return p +end + + +local function typesplit (types) + if type (types) == "string" then + types = split (types:gsub ("%s+or%s+", "|"), "%s*|%s*") + end + local r, seen, add_nil = {}, {}, false + for _, v in ipairs (types) do + local m = v:match "^%?(.+)$" + if m then + add_nil, v = true, m + end + if not seen[v] then + r[#r + 1] = v + seen[v] = true + end + end + if add_nil then + r[#r + 1] = "nil" + end + return r +end + + +local function projectuniq (fkey, tt) + -- project + local t = {} + for _, u in ipairs (tt) do + t[#t + 1] = u[fkey] + end + + -- split and remove duplicates + local r, s = {}, {} + for _, e in ipairs (t) do + for _, v in ipairs (typesplit (e)) do + if s[v] == nil then + r[#r + 1], s[v] = v, true + end + end + end + return r +end + + +local function parsetypes (types) + local r, permutations = {}, permute (types) + for i = 1, #permutations[1] do + r[i] = projectuniq (i, permutations) + end + r.dots = permutations[1].dots + return r +end + + +--- Concatenate a table of strings using ", " and " or " delimiters. +-- @tparam table alternatives a table of strings +-- @treturn string string of elements from alternatives delimited by ", " +-- and " or " +local function concat (alternatives) + if len (alternatives) > 1 then + local t = copy (alternatives) + local top = table.remove (t) + t[#t] = t[#t] .. " or " .. top + alternatives = t + end + return table.concat (alternatives, ", ") +end + + +local function extramsg_mismatch (expectedtypes, actual, index) + local actualtype = prototype (actual) + + -- Tidy up actual type for display. + if actualtype == "nil" then + actualtype = "no value" + elseif actualtype == "string" and actual:sub (1, 1) == ":" then + actualtype = actual + elseif type (actual) == "table" and next (actual) == nil then + local matchstr = "," .. table.concat (expectedtypes, ",") .. "," + if actualtype == "table" and matchstr == ",#list," then + actualtype = "empty list" + elseif actualtype == "table" or matchstr:match ",#" then + actualtype = "empty " .. actualtype + end + end + + if index then + actualtype = actualtype .. " at index " .. tostring (index) + end + + -- Tidy up expected types for display. + local expectedstr = expectedtypes + if type (expectedtypes) == "table" then + local t = {} + for i, v in ipairs (expectedtypes) do + if v == "func" then + t[i] = "function" + elseif v == "bool" then + t[i] = "boolean" + elseif v == "any" then + t[i] = "any value" + elseif v == "file" then + t[i] = "FILE*" + elseif not index then + t[i] = v:match "(%S+) of %S+" or v + else + t[i] = v + end + end + expectedstr = (concat (t) .. " expected"): + gsub ("#table", "non-empty table"): + gsub ("#list", "non-empty list"): + gsub ("(%S+ of [^,%s]-)s? ", "%1s "): + gsub ("(%S+ of [^,%s]-)s?,", "%1s,"): + gsub ("(s, [^,%s]-)s? ", "%1s "): + gsub ("(s, [^,%s]-)s?,", "%1s,"): + gsub ("(of .-)s? or ([^,%s]-)s? ", "%1s or %2s ") + end + + return expectedstr .. ", got " .. actualtype +end + + +local argcheck, argscheck -- forward declarations + +if _DEBUG.argcheck then + + --- Return index of the first mismatch between types and values, or `nil`. + -- @tparam table typelist a list of expected types + -- @tparam table valuelist a table of arguments to compare + -- @treturn int|nil position of first mismatch in *typelist* + local function match (typelist, valuelist) + local n = #typelist + for i = 1, n do -- normal parameters + local ok = pcall (argcheck, "pcall", i, typelist[i], valuelist[i]) + if not ok then return i end + end + for i = n + 1, maxn (valuelist) do -- additional values against final type + local ok = pcall (argcheck, "pcall", i, typelist[n], valuelist[i]) + if not ok then return i end + end + end + + + --- Compare *check* against type of *actual* + -- @string check extended type name expected + -- @param actual object being typechecked + -- @treturn boolean `true` if *actual* is of type *check*, otherwise + -- `false` + local function checktype (check, actual) + if check == "any" and actual ~= nil then + return true + elseif check == "file" and io.type (actual) == "file" then + return true + end + + local actualtype = type (actual) + if check == actualtype then + return true + elseif check == "bool" and actualtype == "boolean" then + return true + elseif check == "#table" then + if actualtype == "table" and next (actual) then + return true + end + elseif check == "function" or check == "func" then + if actualtype == "function" or + (getmetatable (actual) or {}).__call ~= nil + then + return true + end + elseif check == "int" then + if actualtype == "number" and actual == math.floor (actual) then + return true + end + elseif type (check) == "string" and check:sub (1, 1) == ":" then + if check == actual then + return true + end + end + + actualtype = prototype (actual) + if check == actualtype then + return true + elseif check == "list" or check == "#list" then + if actualtype == "table" or actualtype == "List" then + local len, count = len (actual), 0 + local i = next (actual) + repeat + if i ~= nil then count = count + 1 end + i = next (actual, i) + until i == nil or count > len + if count == len and (check == "list" or count > 0) then + return true + end + end + elseif check == "object" then + if actualtype ~= "table" and type (actual) == "table" then + return true + end + end + + return false + end + + + local function empty (t) return not next (t) end + + -- Pattern to normalize: [types...] to [types]... + local last_pat = "^%[([^%]%.]+)%]?(%.*)%]?" + + --- Diagnose mismatches between *valuelist* and type *permutations*. + -- @tparam table valuelist list of actual values to be checked + -- @tparam table argt table of precalculated values and handler functiens + local function diagnose (valuelist, argt) + local permutations = argt.permutations + + local bestmismatch, t = 0 + for i, typelist in ipairs (permutations) do + local mismatch = match (typelist, valuelist) + if mismatch == nil then + bestmismatch, t = nil, nil + break -- every *valuelist* matched types from this *typelist* + elseif mismatch > bestmismatch then + bestmismatch, t = mismatch, permutations[i] + end + end + + if bestmismatch ~= nil then + -- Report an error for all possible types at bestmismatch index. + local i, expected = bestmismatch + if t.dots and i > #t then + expected = typesplit (t[#t]) + else + expected = projectuniq (i, permutations) + end + + -- This relies on the `permute()` algorithm leaving the longest + -- possible permutation (with dots if necessary) at permutations[1]. + local typelist = permutations[1] + + -- For "container of things", check all elements are a thing too. + if typelist[i] then + local check, contents = typelist[i]:match "^(%S+) of (%S-)s?$" + if contents and type (valuelist[i]) == "table" then + for k, v in pairs (valuelist[i]) do + if not checktype (contents, v) then + argt.badtype (i, extramsg_mismatch (expected, v, k), 3) + end + end + end + end + + -- Otherwise the argument type itself was mismatched. + if t.dots or #t >= maxn (valuelist) then + argt.badtype (i, extramsg_mismatch (expected, valuelist[i]), 3) + end + end + + local n, t = maxn (valuelist), t or permutations[1] + if t and t.dots == nil and n > #t then + argt.badtype (#t + 1, extramsg_toomany (argt.bad, #t, n), 3) + end + end + + + function argcheck (name, i, expected, actual, level) + level = level or 2 + expected = typesplit (expected) + + -- Check actual has one of the types from expected + local ok = false + for _, expect in ipairs (expected) do + local check, contents = expect:match "^(%S+) of (%S-)s?$" + check = check or expect + + -- Does the type of actual check out? + ok = checktype (check, actual) + + -- For "table of things", check all elements are a thing too. + if ok and contents and type (actual) == "table" then + for k, v in pairs (actual) do + if not checktype (contents, v) then + argerror (name, i, extramsg_mismatch (expected, v, k), level + 1) + end + end + end + if ok then break end + end + + if not ok then + argerror (name, i, extramsg_mismatch (expected, actual), level + 1) + end + end + + + -- Pattern to extract: fname ([types]?[, types]*) + local args_pat = "^%s*([%w_][%.%:%d%w_]*)%s*%(%s*(.*)%s*%)" + + function argscheck (decl, inner) + -- Parse "fname (argtype, argtype, argtype...)". + local fname, argtypes = decl:match (args_pat) + if argtypes == "" then + argtypes = {} + elseif argtypes then + argtypes = split (argtypes, "%s*,%s*") + else + fname = decl:match "^%s*([%w_][%.%:%d%w_]*)" + end + + -- Precalculate vtables once to make multiple calls faster. + local input, output = { + bad = "argument", + badtype = function (i, extramsg, level) + level = level or 1 + argerror (fname, i, extramsg, level + 1) + end, + permutations = permute (argtypes), + } + + -- Parse "... => returntype, returntype, returntype...". + local returntypes = decl:match "=>%s*(.+)%s*$" + if returntypes then + local i, permutations = 0, {} + for _, group in ipairs (split (returntypes, "%s+or%s+")) do + returntypes = split (group, ",%s*") + for _, t in ipairs (permute (returntypes)) do + i = i + 1 + permutations[i] = t + end + end + + -- Ensure the longest permutation is first in the list. + table.sort (permutations, function (a, b) return #a > #b end) + + output = { + bad = "result", + badtype = function (i, extramsg, level) + level = level or 1 + resulterror (fname, i, extramsg, level + 1) + end, + permutations = permutations, + } + end + + return function (...) + local argt = {...} + + -- Don't check type of self if fname has a ':' in it. + if fname:find (":") then table.remove (argt, 1) end + + -- Diagnose bad inputs. + diagnose (argt, input) + + -- Propagate outer environment to inner function. + local x = math.max -- ??? getfenv(1) fails if we remove this ??? + setfenv (inner, getfenv (1)) + + -- Execute. + local results = {inner (...)} + + -- Diagnose bad outputs. + if returntypes then + diagnose (results, output) + end + + return unpack (results, 1, maxn (results)) + end + end + +else + + -- Turn off argument checking if _DEBUG is false, or a table containing + -- a false valued `argcheck` field. + + argcheck = base.nop + argscheck = function (decl, inner) return inner end + +end + + +local function say (n, ...) + local level, argt = n, {...} + if type (n) ~= "number" then + level, argt = 1, {n, ...} + end + if _DEBUG.level ~= math.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] = tostring (v) end + io.stderr:write (table.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 = math.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 + io.stderr:write (s .. "\n") +end + +-- Set hooks according to _DEBUG +if type (_DEBUG) == "table" and _DEBUG.call then + debug.sethook (trace, "cr") +end + + + +M = { + --- Provide a deprecated function definition according to _DEBUG.deprecate. + -- You can check whether your covered code uses deprecated functions by + -- setting `_DEBUG.deprecate` to `true` before loading any stdlib modules, + -- or silence deprecation warnings by setting `_DEBUG.deprecate = false`. + -- @function DEPRECATED + -- @string version first deprecation release version + -- @string name function name for automatic warning message + -- @string[opt] extramsg additional warning text + -- @func fn deprecated function + -- @return a function to show the warning on first call, and hand off to *fn* + -- @usage + -- M.op = DEPRECATED ("41", "'std.functional.op'", std.operator) + DEPRECATED = DEPRECATED, + + --- Format a deprecation warning message. + -- @function DEPRECATIONMSG + -- @string version first deprecation release version + -- @string name function name for automatic warning message + -- @string[opt] extramsg additional warning text + -- @int level call stack level to blame for the error + -- @treturn string deprecation warning message, or empty string + -- @usage + -- io.stderr:write (DEPRECATIONMSG ("42", "multi-argument 'module.fname'", 2)) + DEPRECATIONMSG = DEPRECATIONMSG, + + --- Check the type of an argument against expected types. + -- Equivalent to luaL_argcheck in the Lua C API. + -- + -- Call `argerror` if there is a type mismatch. + -- + -- Argument `actual` must match one of the types from in `expected`, each + -- of which can be the name of a primitive Lua type, a stdlib object type, + -- or one of the special options below: + -- + -- #table accept any non-empty table + -- any accept any non-nil argument type + -- file accept an open file object + -- function accept a function, or object with a __call metamethod + -- int accept an integer valued number + -- list accept a table where all keys are a contiguous 1-based integer range + -- #list accept any non-empty list + -- object accept any std.Object derived type + -- :foo accept only the exact string ":foo", works for any :-prefixed string + -- + -- The `:foo` format allows for type-checking of self-documenting + -- boolean-like constant string parameters predicated on `nil` versus + -- `:option` instead of `false` versus `true`. Or you could support + -- both: + -- + -- argcheck ("table.copy", 2, "boolean|:nometa|nil", nometa) + -- + -- A very common pattern is to have a list of possible types including + -- "nil" when the argument is optional. Rather than writing long-hand + -- as above, prepend a question mark to the list of types and omit the + -- explicit "nil" entry: + -- + -- argcheck ("table.copy", 2, "?boolean|:nometa", predicate) + -- + -- Normally, you should not need to use the `level` parameter, as the + -- default is to blame the caller of the function using `argcheck` in + -- error messages; which is almost certainly what you want. + -- @function argcheck + -- @string name function to blame in error message + -- @int i argument number to blame in error message + -- @string expected specification for acceptable argument types + -- @param actual argument passed + -- @int[opt=2] level call stack level to blame for the error + -- @usage + -- local function case (with, branches) + -- argcheck ("std.functional.case", 2, "#table", branches) + -- ... + argcheck = argcheck, + + --- Raise a bad argument error. + -- Equivalent to luaL_argerror in the Lua C API. This function does not + -- return. The `level` argument behaves just like the core `error` + -- function. + -- @function argerror + -- @string name function to callout in error message + -- @int i argument number + -- @string[opt] extramsg additional text to append to message inside parentheses + -- @int[opt=1] level call stack level to blame for the error + -- @see resulterror + -- @see extramsg_mismatch + -- @usage + -- local function slurp (file) + -- local h, err = input_handle (file) + -- if h == nil then argerror ("std.io.slurp", 1, err, 2) end + -- ... + argerror = argerror, + + --- Wrap a function definition with argument type and arity checking. + -- In addition to checking that each argument type matches the corresponding + -- element in the *types* table with `argcheck`, if the final element of + -- *types* ends with an ellipsis, remaining unchecked arguments are checked + -- against that type: + -- + -- format = argscheck ("string.format (string, ?any...)", string.format) + -- + -- A colon in the function name indicates that the argument type list does + -- not have a type for `self`: + -- + -- format = argscheck ("string:format (?any...)", string.format) + -- + -- If an argument can be omitted entirely, then put its type specification + -- in square brackets: + -- + -- insert = argscheck ("table.insert (table, [int], ?any)", table.insert) + -- + -- Similarly return types can be checked with the same list syntax as + -- arguments: + -- + -- len = argscheck ("string.len (string) => int", string.len) + -- + -- Additionally, variant return type lists can be listed like this: + -- + -- open = argscheck ("io.open (string, ?string) => file or nil, string", + -- io.open) + -- + -- @function argscheck + -- @string decl function type declaration string + -- @func inner function to wrap with argument checking + -- @usage + -- local case = argscheck ("std.functional.case (?any, #table) => [any...]", + -- function (with, branches) + -- ... + -- end) + argscheck = argscheck, + + --- Format a type mismatch error. + -- @function extramsg_mismatch + -- @string expected a pipe delimited list of matchable types + -- @param actual the actual argument to match with + -- @number[opt] index erroring container element index + -- @treturn string formatted *extramsg* for this mismatch for @{argerror} + -- @see argerror + -- @see resulterror + -- @usage + -- if fmt ~= nil and type (fmt) ~= "string" then + -- argerror ("format", 1, extramsg_mismatch ("?string", fmt)) + -- end + extramsg_mismatch = function (expected, actual, index) + return extramsg_mismatch (typesplit (expected), actual, index) + end, + + --- Format a too many things error. + -- @string bad the thing there are too many of + -- @int expected maximum number of *bad* things expected + -- @int actual actual number of *bad* things that triggered the error + -- @see argerror + -- @see resulterror + -- @see extramsg_mismatch + -- @usage + -- if maxn (argt) > 7 then + -- argerror ("sevenses", 8, extramsg_toomany ("argument", 7, maxn (argt))) + -- end + extramsg_toomany = extramsg_toomany, + + --- Extend `debug.getfenv` to unwrap functables correctly. + -- @tparam int|function|functable fn target function, or stack level + -- @treturn table environment of *fn* + getfenv = getfenv, + + --- Compact permutation list into a list of valid types at each argument. + -- Eliminate bracketed types by combining all valid types at each position + -- for all permutations of *typelist*. + -- @function parsetypes + -- @tparam list types a normalized list of type names + -- @treturn list valid types for each positional parameter + parsetypes = parsetypes, + + --- Raise a bad result error. + -- Like @{argerror} for bad results. This function does not + -- return. The `level` argument behaves just like the core `error` + -- function. + -- @string name function to callout in error message + -- @int i argument number + -- @string[opt] extramsg additional text to append to message inside parentheses + -- @int[opt=1] level call stack level to blame for the error + -- @usage + -- local function slurp (file) + -- local h, err = input_handle (file) + -- if h == nil then argerror ("std.io.slurp", 1, err, 2) end + -- ... + resulterror = resulterror, + + --- Extend `debug.setfenv` to unwrap functables correctly. + -- @tparam function|functable fn target function + -- @tparam table env new function environment + -- @treturn function *fn* + setfenv = setfenv, + + --- Print a debugging message to `io.stderr`. + -- Display arguments passed through `std.tostring` and separated by tab + -- characters when `_DEBUG` is `true` and *n* is 1 or less; or `_DEBUG.level` + -- is a number greater than or equal to *n*. If `_DEBUG` 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_init"._DEBUG + -- _DEBUG.level = 3 + -- say (2, "_DEBUG table contents:", _DEBUG) + say = say, + + --- Trace function calls. + -- Use as debug.sethook (trace, "cr"), which is done automatically + -- when `_DEBUG.call` is set. + -- Based on test/trace-calls.lua from the Lua distribution. + -- @function trace + -- @string event event causing the call + -- @usage + -- _DEBUG = { call = true } + -- local debug = require "std.debug" + trace = trace, + + --- Split a typespec string into a table of normalized type names. + -- @tparam string|table either `"?bool|:nometa"` or `{"boolean", ":nometa"}` + -- @treturn table a new list with duplicates removed and leading "?"s + -- replaced by a "nil" element + typesplit = typesplit, + + + -- Private: + _setdebug = function (t) + for k, v in pairs (t) do + if v == "nil" then v = nil end + _DEBUG[k] = v + end + end, +} + + +for k, v in pairs (debug) do + M[k] = M[k] or v +end + +--- Equivalent to calling `debug.say (1, ...)` +-- @function debug +-- @see say +-- @usage +-- local debug = require "std.debug" +-- debug "oh noes!" +local metatable = { + __call = function (self, ...) + M.say (1, ...) + end, +} + + + +--[[ =========== ]]-- +--[[ Deprecated. ]]-- +--[[ =========== ]]-- + + +M.toomanyargmsg = DEPRECATED ("41.2.0", "debug.toomanyargmsg", + "use 'debug.extramsg_toomany' instead", + function (name, expect, actual) + local s = "bad argument #%d to '%s' (no more than %d argument%s expected, got %d)" + return s:format (expect + 1, name, expect, expect == 1 and "" or "s", actual) + end) + + +return setmetatable (M, metatable) + + + +--- Control std.debug function behaviour. +-- To declare debugging state, set _DEBUG either to `false` to disable all +-- runtime debugging; to any "truthy" value (equivalent to enabling everything +-- except *call*, or as documented below. +-- @class table +-- @name _DEBUG +-- @tfield[opt=true] boolean argcheck honor argcheck and argscheck calls +-- @tfield[opt=false] boolean call do call trace debugging +-- @field[opt=nil] deprecate if `false`, deprecated APIs are defined, +-- and do not issue deprecation warnings when used; if `nil` issue a +-- deprecation warning each time a deprecated api is used; any other +-- value causes deprecated APIs not to be defined at all +-- @tfield[opt=1] int level debugging level +-- @usage _DEBUG = { argcheck = false, level = 9 } diff --git a/lib/std/debug_init/init.lua b/lib/std/debug_init/init.lua new file mode 100644 index 0000000..ccdcb69 --- /dev/null +++ b/lib/std/debug_init/init.lua @@ -0,0 +1,47 @@ +-- Debugging is on by default +local M = {} + +-- Use rawget to satisfy std.strict. +local _DEBUG = rawget (_G, "_DEBUG") + +-- User specified fields. +if type (_DEBUG) == "table" then + M._DEBUG = _DEBUG + +-- Turn everything off. +elseif _DEBUG == false then + M._DEBUG = { + argcheck = false, + call = false, + deprecate = false, + level = math.huge, + } + +-- Turn everything on (except _DEBUG.call must be set explicitly). +elseif _DEBUG == true then + M._DEBUG = { + argcheck = true, + call = false, + deprecate = true, + } + +else + M._DEBUG = {} +end + + +local function setdefault (field, value) + if M._DEBUG[field] == nil then + M._DEBUG[field] = value + end +end + + +-- Default settings if otherwise unspecified. +setdefault ("argcheck", true) +setdefault ("call", false) +setdefault ("deprecate", nil) +setdefault ("level", 1) + + +return M diff --git a/lib/std/functional.lua b/lib/std/functional.lua new file mode 100644 index 0000000..b20a8bc --- /dev/null +++ b/lib/std/functional.lua @@ -0,0 +1,620 @@ +--[[-- + Functional programming. + + A selection of higher-order functions to enable a functional style of + programming in Lua. + + @module std.functional +]] + + +local base = require "std.base" +local debug = require "std.debug" + +local ielems, ipairs, ireverse, npairs, pairs = + base.ielems, base.ipairs, base.ireverse, base.npairs, base.pairs +local callable, copy, len, reduce, unpack = + base.callable, base.copy, base.len, base.reduce, base.unpack +local loadstring = loadstring or load + + +local function bind (fn, ...) + local bound = {...} + if type (bound[1]) == "table" and bound[2] == nil then + bound = bound[1] + else + io.stderr:write (debug.DEPRECATIONMSG ("39", + "multi-argument 'std.functional.bind'", + "use a table of arguments as the second parameter instead", 2)) + end + + return function (...) + local argt, i = copy (bound), 1 + for _, v in npairs {...} do + while argt[i] ~= nil do i = i + 1 end + argt[i], i = v, i + 1 + end + return fn (unpack (argt)) + end +end + + +local function case (with, branches) + local match = branches[with] or branches[1] + if callable (match) then + return match (with) + end + return match +end + + +local function compose (...) + local fns = {...} + + return function (...) + local argt = {...} + for _, fn in npairs (fns) do + argt = {fn (unpack (argt))} + end + return unpack (argt) + end +end + + +local function cond (expr, branch, ...) + if branch == nil and select ("#", ...) == 0 then + expr, branch = true, expr + end + if expr then + if callable (branch) then + return branch (expr) + end + return branch + end + return cond (...) +end + + +local function curry (fn, n) + if n <= 1 then + return fn + else + return function (x) + return curry (bind (fn, x), n - 1) + end + end +end + + +local function filter (pfn, ifn, ...) + local argt, r = {...}, {} + if not callable (ifn) then + ifn, argt = pairs, {ifn, ...} + end + + local nextfn, state, k = ifn (unpack (argt)) + + local t = {nextfn (state, k)} -- table of iteration 1 + local arity = #t -- How many return values from ifn? + + if arity == 1 then + local v = t[1] + while v ~= nil do -- until iterator returns nil + if pfn (unpack (t)) then -- pass all iterator results to p + r[#r + 1] = v + end + + t = {nextfn (state, v)} -- maintain loop invariant + v = t[1] + + if #t > 1 then -- unless we discover arity is not 1 after all + arity, r = #t, {} break + end + end + end + + if arity > 1 then + -- No need to start over here, because either: + -- (i) arity was never 1, and the original value of t is correct + -- (ii) arity used to be 1, but we only consumed nil values, so the + -- current t with arity > 1 is the correct next value to use + while t[1] ~= nil do + local k = t[1] + if pfn (unpack (t)) then r[k] = t[2] end + t = {nextfn (state, k)} + end + end + + return r +end + + +local function foldl (fn, d, t) + if t == nil then + local tail = {} + for i = 2, len (d) do tail[#tail + 1] = d[i] end + d, t = d[1], tail + end + return reduce (fn, d, ielems, t) +end + + +local function foldr (fn, d, t) + if t == nil then + local u, last = {}, len (d) + for i = 1, last - 1 do u[#u + 1] = d[i] end + d, t = d[last], u + end + return reduce (function (x, y) return fn (y, x) end, d, ielems, ireverse (t)) +end + + +local function id (...) + return ... +end + + +local function memoize (fn, normalize) + if normalize == nil then + normalize = function (...) return base.tostring {...} end + end + + return setmetatable ({}, { + __call = function (self, ...) + local k = normalize (...) + local t = self[k] + if t == nil then + t = {fn (...)} + self[k] = t + end + return unpack (t) + end + }) +end + + +local lambda = memoize (function (s) + local expr + + -- Support "|args|expression" format. + local args, body = s:match "^%s*|%s*([^|]*)|%s*(.+)%s*$" + if args and body then + expr = "return function (" .. args .. ") return " .. body .. " end" + end + + -- Support "expression" format. + if not expr then + body = s:match "^%s*(_.*)%s*$" or s:match "^=%s*(.+)%s*$" + if body then + expr = [[ + return function (...) + local unpack = table.unpack or unpack + local _1,_2,_3,_4,_5,_6,_7,_8,_9 = unpack {...} + local _ = _1 + return ]] .. body .. [[ + end + ]] + end + end + + local ok, fn + if expr then + ok, fn = pcall (loadstring (expr)) + end + + -- Diagnose invalid input. + if not ok then + return nil, "invalid lambda string '" .. s .. "'" + end + + return fn +end, id) + + +local function map (mapfn, ifn, ...) + local argt, r = {...}, {} + if not callable (ifn) or not next (argt) then + ifn, argt = pairs, {ifn, ...} + end + + local nextfn, state, k = ifn (unpack (argt)) + local mapargs = {nextfn (state, k)} + + local arity = 1 + while mapargs[1] ~= nil do + local d, v = mapfn (unpack (mapargs)) + if v ~= nil then + arity, r = 2, {} break + end + r[#r + 1] = d + mapargs = {nextfn (state, mapargs[1])} + end + + if arity > 1 then + -- No need to start over here, because either: + -- (i) arity was never 1, and the original value of mapargs is correct + -- (ii) arity used to be 1, but we only consumed nil values, so the + -- current mapargs with arity > 1 is the correct next value to use + while mapargs[1] ~= nil do + local k, v = mapfn (unpack (mapargs)) + r[k] = v + mapargs = {nextfn (state, mapargs[1])} + end + end + return r +end + + +local function map_with (mapfn, tt) + local r = {} + for k, v in pairs (tt) do + r[k] = mapfn (unpack (v)) + end + return r +end + + +local function zip (tt) + local r = {} + for outerk, inner in pairs (tt) do + for k, v in pairs (inner) do + r[k] = r[k] or {} + r[k][outerk] = v + end + end + return r +end + + +local function zip_with (fn, tt) + return map_with (fn, zip (tt)) +end + + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X (decl, fn) + return debug.argscheck ("std.functional." .. decl, fn) +end + +local M = { + --- Partially apply a function. + -- @function bind + -- @func fn function to apply partially + -- @tparam table argt table of *fn* arguments to bind + -- @return function with *argt* arguments already bound + -- @usage + -- cube = bind (std.operator.pow, {[2] = 3}) + bind = X ("bind (func, ?any...)", bind), + + --- Identify callable types. + -- @function callable + -- @param x an object or primitive + -- @return `true` if *x* can be called, otherwise `false` + -- @usage + -- if callable (functable) then functable (args) end + callable = X ("callable (?any)", callable), + + --- A rudimentary case statement. + -- Match *with* against keys in *branches* table. + -- @function case + -- @param with expression to match + -- @tparam table branches map possible matches to functions + -- @return the value associated with a matching key, or the first non-key + -- value if no key matches. Function or functable valued matches are + -- called using *with* as the sole argument, and the result of that call + -- returned; otherwise the matching value associated with the matching + -- key is returned directly; or else `nil` if there is no match and no + -- default. + -- @see cond + -- @usage + -- return case (type (object), { + -- table = "table", + -- string = function () return "string" end, + -- function (s) error ("unhandled type: " .. s) end, + -- }) + case = X ("case (?any, #table)", case), + + --- Collect the results of an iterator. + -- @function collect + -- @func[opt=std.npairs] ifn iterator function + -- @param ... *ifn* arguments + -- @treturn table of results from running *ifn* on *args* + -- @see filter + -- @see map + -- @usage + -- --> {"a", "b", "c"} + -- collect {"a", "b", "c", x=1, y=2, z=5} + collect = X ("collect ([func], any...)", base.collect), + + --- Compose functions. + -- @function compose + -- @func ... functions to compose + -- @treturn function composition of fnN .. fn1: note that this is the + -- reverse of what you might expect, but means that code like: + -- + -- functional.compose (function (x) return f (x) end, + -- function (x) return g (x) end)) + -- + -- can be read from top to bottom. + -- @usage + -- vpairs = compose (table.invert, ipairs) + -- for v, i in vpairs {"a", "b", "c"} do process (v, i) end + compose = X ("compose (func...)", compose), + + --- A rudimentary condition-case statement. + -- If *expr* is "truthy" return *branch* if given, otherwise *expr* + -- itself. If the return value is a function or functable, then call it + -- with *expr* as the sole argument and return the result; otherwise + -- return it explicitly. If *expr* is "falsey", then recurse with the + -- first two arguments stripped. + -- @function cond + -- @param expr a Lua expression + -- @param branch a function, functable or value to use if *expr* is + -- "truthy" + -- @param ... additional arguments to retry if *expr* is "falsey" + -- @see case + -- @usage + -- -- recursively calculate the nth triangular number + -- function triangle (n) + -- return cond ( + -- n <= 0, 0, + -- n == 1, 1, + -- function () return n + triangle (n - 1) end) + -- end + cond = cond, -- any number of any type arguments! + + --- Curry a function. + -- @function curry + -- @func fn function to curry + -- @int n number of arguments + -- @treturn function curried version of *fn* + -- @usage + -- add = curry (function (x, y) return x + y end, 2) + -- incr, decr = add (1), add (-1) + curry = X ("curry (func, int)", curry), + + --- Filter an iterator with a predicate. + -- @function filter + -- @tparam predicate pfn predicate function + -- @func[opt=std.pairs] ifn iterator function + -- @param ... iterator arguments + -- @treturn table elements e for which `pfn (e)` is not "falsey". + -- @see collect + -- @see map + -- @usage + -- --> {2, 4} + -- filter (lambda '|e|e%2==0', std.elems, {1, 2, 3, 4}) + filter = X ("filter (func, [func], any...)", filter), + + --- Fold a binary function left associatively. + -- If parameter *d* is omitted, the first element of *t* is used, + -- and *t* treated as if it had been passed without that element. + -- @function foldl + -- @func fn binary function + -- @param[opt=t[1]] d initial left-most argument + -- @tparam table t a table + -- @return result + -- @see foldr + -- @see reduce + -- @usage + -- foldl (std.operator.quot, {10000, 100, 10}) == (10000 / 100) / 10 + foldl = X ("foldl (function, [any], table)", foldl), + + --- Fold a binary function right associatively. + -- If parameter *d* is omitted, the last element of *t* is used, + -- and *t* treated as if it had been passed without that element. + -- @function foldr + -- @func fn binary function + -- @param[opt=t[1]] d initial right-most argument + -- @tparam table t a table + -- @return result + -- @see foldl + -- @see reduce + -- @usage + -- foldr (std.operator.quot, {10000, 100, 10}) == 10000 / (100 / 10) + foldr = X ("foldr (function, [any], table)", foldr), + + --- Identity function. + -- @function id + -- @param ... arguments + -- @return *arguments* + id = id, -- any number of any type arguments! + + --- Compile a lambda string into a Lua function. + -- + -- A valid lambda string takes one of the following forms: + -- + -- 1. `'=expression'`: equivalent to `function (...) return expression end` + -- 1. `'|args|expression'`: equivalent to `function (args) return expression end` + -- + -- The first form (starting with `'='`) automatically assigns the first + -- nine arguments to parameters `'_1'` through `'_9'` for use within the + -- expression body. The parameter `'_1'` is aliased to `'_'`, and if the + -- first non-whitespace of the whole expression is `'_'`, then the + -- leading `'='` can be omitted. + -- + -- The results are memoized, so recompiling a previously compiled + -- lambda string is extremely fast. + -- @function lambda + -- @string s a lambda string + -- @treturn functable compiled lambda string, can be called like a function + -- @usage + -- -- The following are equivalent: + -- lambda '= _1 < _2' + -- lambda '|a,b| a {1, 4, 9, 16} + -- map (lambda '=_1*_1', std.ielems, {1, 2, 3, 4}) + map = X ("map (func, [func], any...)", map), + + --- Map a function over a table of argument lists. + -- @function map_with + -- @func fn map function + -- @tparam table tt a table of *fn* argument lists + -- @treturn table new table of *fn* results + -- @see map + -- @see zip_with + -- @usage + -- --> {"123", "45"}, {a="123", b="45"} + -- conc = bind (map_with, {lambda '|...|table.concat {...}'}) + -- conc {{1, 2, 3}, {4, 5}}, conc {a={1, 2, 3, x="y"}, b={4, 5, z=6}} + map_with = X ("map_with (function, table of tables)", map_with), + + --- Memoize a function, by wrapping it in a functable. + -- + -- To ensure that memoize always returns the same results for the same + -- arguments, it passes arguments to *fn*. You can specify a more + -- sophisticated function if memoize should handle complicated argument + -- equivalencies. + -- @function memoize + -- @func fn pure function: a function with no side effects + -- @tparam[opt=std.tostring] normalize normfn function to normalize arguments + -- @treturn functable memoized function + -- @usage + -- local fast = memoize (function (...) --[[ slow code ]] end) + memoize = X ("memoize (func, ?func)", memoize), + + --- No operation. + -- This function ignores all arguments, and returns no values. + -- @function nop + -- @see id + -- @usage + -- if unsupported then vtable["memrmem"] = nop end + nop = base.nop, -- ignores all arguments + + --- Fold a binary function into an iterator. + -- @function reduce + -- @func fn reduce function + -- @param d initial first argument + -- @func[opt=std.pairs] ifn iterator function + -- @param ... iterator arguments + -- @return result + -- @see foldl + -- @see foldr + -- @usage + -- --> 2 ^ 3 ^ 4 ==> 4096 + -- reduce (std.operator.pow, 2, std.ielems, {3, 4}) + reduce = X ("reduce (func, any, [func], any...)", reduce), + + --- Zip a table of tables. + -- Make a new table, with lists of elements at the same index in the + -- original table. This function is effectively its own inverse. + -- @function zip + -- @tparam table tt a table of tables + -- @treturn table new table with lists of elements of the same key + -- from *tt* + -- @see map + -- @see zip_with + -- @usage + -- --> {{1, 3, 5}, {2, 4}}, {a={x=1, y=3, z=5}, b={x=2, y=4}} + -- zip {{1, 2}, {3, 4}, {5}}, zip {x={a=1, b=2}, y={a=3, b=4}, z={a=5}} + zip = X ("zip (table of tables)", zip), + + --- Zip a list of tables together with a function. + -- @function zip_with + -- @tparam function fn function + -- @tparam table tt table of tables + -- @treturn table a new table of results from calls to *fn* with arguments + -- made from all elements the same key in the original tables; effectively + -- the "columns" in a simple list + -- of lists. + -- @see map_with + -- @see zip + -- @usage + -- --> {"135", "24"}, {a="1", b="25"} + -- conc = bind (zip_with, {lambda '|...|table.concat {...}'}) + -- conc {{1, 2}, {3, 4}, {5}}, conc {{a=1, b=2}, x={a=3, b=4}, {b=5}} + zip_with = X ("zip_with (function, table of tables)", zip_with), +} + + + +--[[ ============= ]]-- +--[[ Deprecations. ]]-- +--[[ ============= ]]-- + + +local DEPRECATED = debug.DEPRECATED + + +M.eval = DEPRECATED ("41", "'std.functional.eval'", + "use 'std.eval' instead", base.eval) + + +local function fold (fn, d, ifn, ...) + local nextfn, state, k = ifn (...) + local t = {nextfn (state, k)} + + local r = d + while t[1] ~= nil do + r = fn (r, t[#t]) + t = {nextfn (state, t[1])} + end + return r +end + +M.fold = DEPRECATED ("41", "'std.functional.fold'", + "use 'std.functional.reduce' instead", fold) + + +local operator = require "std.operator" + +local function DEPRECATEOP (old, new) + return DEPRECATED ("41", "'std.functional.op[" .. old .. "]'", + "use 'std.operator." .. new .. "' instead", operator[new]) +end + +M.op = { + ["[]"] = DEPRECATEOP ("[]", "get"), + ["+"] = DEPRECATEOP ("+", "sum"), + ["-"] = DEPRECATEOP ("-", "diff"), + ["*"] = DEPRECATEOP ("*", "prod"), + ["/"] = DEPRECATEOP ("/", "quot"), + ["and"] = DEPRECATEOP ("and", "conj"), + ["or"] = DEPRECATEOP ("or", "disj"), + ["not"] = DEPRECATEOP ("not", "neg"), + ["=="] = DEPRECATEOP ("==", "eq"), + ["~="] = DEPRECATEOP ("~=", "neq"), +} + +return M + + + +--- Types +-- @section Types + + +--- Signature of a @{memoize} argument normalization callback function. +-- @function normalize +-- @param ... arguments +-- @treturn string normalized arguments +-- @usage +-- local normalize = function (name, value, props) return name end +-- local intern = std.functional.memoize (mksymbol, normalize) + + +--- Signature of a @{filter} predicate callback function. +-- @function predicate +-- @param ... arguments +-- @treturn boolean "truthy" if the predicate condition succeeds, +-- "falsey" otherwise +-- @usage +-- local predicate = lambda '|k,v|type(v)=="string"' +-- local strvalues = filter (predicate, std.pairs, {name="Roberto", id=12345}) diff --git a/lib/std/io.lua b/lib/std/io.lua new file mode 100644 index 0000000..056d7cc --- /dev/null +++ b/lib/std/io.lua @@ -0,0 +1,295 @@ +--[[-- + 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" + + @module std.io +]] + + +local base = require "std.base" +local debug = require "std.debug" + +local argerror = debug.argerror +local catfile, dirsep, insert, len, leaves, split = + base.catfile, base.dirsep, base.insert, base.len, base.leaves, base.split +local ipairs, pairs = base.ipairs, base.pairs +local setmetatable = debug.setmetatable + + + +local M, monkeys + + +local function input_handle (h) + if h == nil then + return io.input () + elseif type (h) == "string" then + return io.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 + io.write (h, "\n") + h = io.output () + end + for v in leaves (ipairs, {...}) do + h:write (v, "\n") + end +end + + +local function monkey_patch (namespace) + namespace = namespace or _G + namespace.io = base.copy (namespace.io or {}, monkeys) + + if namespace.io.stdin then + local mt = getmetatable (namespace.io.stdin) or {} + mt.readlines = M.readlines + mt.writelines = M.writelines + setmetatable (namespace.io.stdin, mt) + end + + return M +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 + io.input (io.stdin) + else + io.input (v) + end + fn (v, i) + end +end + + +local function warnfmt (msg, ...) + local prefix = "" + if (prog or {}).name then + prefix = prog.name .. ":" + if prog.line then + prefix = prefix .. tostring (prog.line) .. ":" + end + elseif (prog or {}).file then + prefix = prog.file .. ":" + if prog.line then + prefix = prefix .. tostring (prog.line) .. ":" + end + elseif (opts or {}).program then + prefix = opts.program .. ":" + if opts.line then + prefix = prefix .. tostring (opts.line) .. ":" + end + end + if #prefix > 0 then prefix = prefix .. " " end + return prefix .. string.format (msg, ...) +end + + +local function warn (msg, ...) + writelines (io.stderr, warnfmt (msg, ...)) +end + + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X (decl, fn) + return debug.argscheck ("std.io." .. decl, fn) +end + + +M = { + --- 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 (table.concat ({...}, dirsep):gsub("^$", 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...)", base.catfile), + + --- 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), + + --- 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 (path:gsub (catfile ("", "[^", "]*$"), "")) + end), + + --- Overwrite core `io` methods with `std` enhanced versions. + -- + -- Also adds @{readlines} and @{writelines} metamethods to core file objects. + -- @function monkey_patch + -- @tparam[opt=_G] table namespace where to install global functions + -- @treturn table the `std.io` module table + -- @usage local io = require "std.io".monkey_patch () + monkey_patch = X ("monkey_patch (?table)", monkey_patch), + + --- 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 (io.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), + + --- 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), + + --- 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. @{std.optparse:parse} + -- returns an `opts` table that provides the required `program` + -- field, as long as you assign it back to `_G.opts`. + -- @function warn + -- @string msg format string + -- @param ... additional arguments to plug format string specifiers + -- @see std.optparse:parse + -- @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), + + --- 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), +} + + +monkeys = base.copy ({}, M) -- before deprecations and core merge + + +return base.merge (M, io) + + + +--- 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/lib/std/list.lua b/lib/std/list.lua new file mode 100644 index 0000000..ad04c06 --- /dev/null +++ b/lib/std/list.lua @@ -0,0 +1,515 @@ +--[[-- + Tables as lists. + + Prototype Chain + --------------- + + table + `-> Object + `-> List + + @classmod std.list +]] + + +local base = require "std.base" +local debug = require "std.debug" + +local Object = require "std.object" {} + +local ipairs, pairs = base.ipairs, base.pairs +local len = base.len +local compare = base.compare +local prototype = base.prototype +local unpack = base.unpack + +local M, List + + +local function append (l, x) + local r = l {} + r[#r + 1] = x + return r +end + + +local function concat (l, ...) + local r = List {} + for _, e in ipairs {l, ...} do + for _, v in ipairs (e) do + r[#r + 1] = v + end + end + return r +end + + +local function rep (l, n) + local r = List {} + for i = 1, n do + r = concat (r, l) + end + return r +end + + +local function sub (l, from, to) + local r = List {} + local lenl = len (l) + from = from or 1 + to = to or lenl + if from < 0 then + from = from + lenl + 1 + end + if to < 0 then + to = to + lenl + 1 + end + for i = from, to do + r[#r + 1] = l[i] + end + return r +end + + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X (decl, fn) + return debug.argscheck ("std.list." .. decl, fn) +end + + +M = { + --- Append an item to a list. + -- @static + -- @function append + -- @tparam List l a list + -- @param x item + -- @treturn List new list with *x* appended + -- @usage + -- longer = append (short, "last") + append = X ("append (List, any)", append), + + --- Compare two lists element-by-element, from left-to-right. + -- @static + -- @function compare + -- @tparam List l a list + -- @tparam List|table m another list, or table + -- @return -1 if *l* is less than *m*, 0 if they are the same, and 1 + -- if *l* is greater than *m* + -- @usage + -- if a_list:compare (another_list) == 0 then print "same" end + compare = X ("compare (List, List|table)", compare), + + --- Concatenate the elements from any number of lists. + -- @static + -- @function concat + -- @tparam List l a list + -- @param ... tuple of lists + -- @treturn List new list with elements from arguments + -- @usage + -- --> {1, 2, 3, {4, 5}, 6, 7} + -- list.concat ({1, 2, 3}, {{4, 5}, 6, 7}) + concat = X ("concat (List, List|table...)", concat), + + --- Prepend an item to a list. + -- @static + -- @function cons + -- @tparam List l a list + -- @param x item + -- @treturn List new list with *x* followed by elements of *l* + -- @usage + -- --> {"x", 1, 2, 3} + -- list.cons ({1, 2, 3}, "x") + cons = X ("cons (List, any)", function (l, x) return List {x, unpack (l)} end), + + --- Repeat a list. + -- @static + -- @function rep + -- @tparam List l a list + -- @int n number of times to repeat + -- @treturn List *n* copies of *l* appended together + -- @usage + -- --> {1, 2, 3, 1, 2, 3, 1, 2, 3} + -- list.rep ({1, 2, 3}, 3) + rep = X ("rep (List, int)", rep), + + --- Return a sub-range of a list. + -- (The equivalent of @{string.sub} on strings; negative list indices + -- count from the end of the list.) + -- @static + -- @function sub + -- @tparam List l a list + -- @int[opt=1] from start of range + -- @int[opt=#l] to end of range + -- @treturn List new list containing elements between *from* and *to* + -- inclusive + -- @usage + -- --> {3, 4, 5} + -- list.sub ({1, 2, 3, 4, 5, 6}, 3, 5) + sub = X ("sub (List, ?int, ?int)", sub), + + --- Return a list with its first element removed. + -- @static + -- @function tail + -- @tparam List l a list + -- @treturn List new list with all but the first element of *l* + -- @usage + -- --> {3, {4, 5}, 6, 7} + -- list.tail {{1, 2}, 3, {4, 5}, 6, 7} + tail = X ("tail (List)", function (l) return sub (l, 2) end), +} + + + +--[[ ============= ]]-- +--[[ Deprecations. ]]-- +--[[ ============= ]]-- + +-- This entire section can be deleted in due course, with just one +-- additional small correction noted in FIXME comments in the List +-- object constructor at the end of this file. + + +local DEPRECATED = debug.DEPRECATED + + +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 ls = List {} + for i, v in pairs (t) do + ls[#ls + 1] = List {i, v} + end + return ls +end + + +local function filter (pfn, l) + local r = List {} + for _, e in ipairs (l) do + if pfn (e) then + r[#r + 1] = e + end + end + return r +end + + +local function flatten (l) + local r = List {} + for v in base.leaves (ipairs, l) do + r[#r + 1] = v + end + return r +end + + +local function foldl (fn, d, t) + if t == nil then + local tail = {} + for i = 2, len (d) do tail[#tail + 1] = d[i] end + d, t = d[1], tail + end + return base.reduce (fn, d, base.ielems, t) +end + + +local function foldr (fn, d, t) + if t == nil then + local u, last = {}, len (d) + for i = 1, last - 1 do u[#u + 1] = d[i] end + d, t = d[last], u + end + return base.reduce ( + function (x, y) return fn (y, x) end, d, base.ielems, base.ireverse (t)) +end + + +local function index_key (f, l) + local r = {} + for i, v in ipairs (l) do + local k = v[f] + if k then + r[k] = i + end + end + return r +end + + +local function index_value (f, l) + local r = {} + for i, v in ipairs (l) do + local k = v[f] + if k then + r[k] = v + end + end + return r +end + + +local function map (fn, l) + local r = List {} + for _, e in ipairs (l) do + local v = fn (e) + if v ~= nil then + r[#r + 1] = v + end + end + return r +end + + +local function map_with (fn, ls) + return map (function (...) return fn (unpack (...)) end, ls) +end + + +local function project (x, l) + return map (function (t) return t[x] end, l) +end + + +local function relems (l) return base.ielems (base.ireverse (l)) end + + +local function reverse (l) return List (base.ireverse (l)) end + + +local function shape (s, l) + l = flatten (l) + -- Check the shape and calculate the size of the zero, if any + local size = 1 + local zero + for i, v in ipairs (s) do + if v == 0 then + if zero then -- bad shape: two zeros + return nil + else + zero = i + end + else + size = size * v + end + end + if zero then + s[zero] = math.ceil (len (l) / size) + end + local function fill (i, d) + if d > len (s) then + return l[i], i + 1 + else + local r = List {} + for j = 1, s[d] do + local e + e, i = fill (i, d + 1) + r[#r + 1] = e + end + return r, i + end + end + return (fill (1, 1)) +end + + +local function transpose (ls) + local rs, lenls, dims = List {}, len (ls), map (len, ls) + if len (dims) > 0 then + for i = 1, math.max (unpack (dims)) do + rs[i] = List {} + for j = 1, lenls do + rs[i][j] = ls[j][i] + end + end + end + return rs +end + + +local function zip_with (ls, fn) + return map_with (fn, transpose (ls)) +end + + +local m = { + append = M.append, + compare = M.compare, + concat = M.concat, + cons = M.cons, + rep = M.rep, + sub = M.sub, + tail = M.tail, +} + + +m.depair = DEPRECATED ("38", "'std.list:depair'", depair) +m.map_with = DEPRECATED ("38", "'std.list:map_with'", + function (self, fn) return map_with (fn, self) end) +m.transpose = DEPRECATED ("38", "'std.list:transpose'", transpose) +m.zip_with = DEPRECATED ("38", "'std.list:zip_with'", zip_with) + + +M.depair = DEPRECATED ("41", "'std.list.depair'", depair) + +M.enpair = DEPRECATED ("41", "'std.list.enpair'", enpair) +m.enpair = DEPRECATED ("41", "'std.list:enpair'", enpair) + +M.elems = DEPRECATED ("41", "'std.list.elems'", + "use 'std.ielems' instead", base.ielems) +m.elems = DEPRECATED ("41", "'std.list:elems'", + "use 'std.ielems' instead", base.ielems) + +M.filter = DEPRECATED ("41", "'std.list.filter'", + "use 'std.functional.filter' instead", filter) +m.filter = DEPRECATED ("41", "'std.list:filter'", + "use 'std.functional.filter' instead", + function (self, p) return filter (p, self) end) + + +M.flatten = DEPRECATED ("41", "'std.list.flatten'", + "use 'std.functional.flatten' instead", flatten) +m.flatten = DEPRECATED ("41", "'std.list:flatten'", + "use 'std.functional.flatten' instead", flatten) + + +M.foldl = DEPRECATED ("41", "'std.list.foldl'", + "use 'std.functional.foldl' instead", foldl) +m.foldl = DEPRECATED ("41", "'std.list:foldl'", + "use 'std.functional.foldl' instead", + function (self, fn, e) + if e ~= nil then return foldl (fn, e, self) end + return foldl (fn, self) + end) + +M.foldr = DEPRECATED ("41", "'std.list.foldr'", + "use 'std.functional.foldr' instead", foldr) +m.foldr = DEPRECATED ("41", "'std.list:foldr'", + "use 'std.functional.foldr' instead", + function (self, fn, e) + if e ~= nil then return foldr (fn, e, self) end + return foldr (fn, self) + end) + +M.index_key = DEPRECATED ("41", "'std.list.index_key'", + "compose 'std.functional.filter' and 'std.table.invert' instead", + index_key) +m.index_key = DEPRECATED ("41", "'std.list:index_key'", + function (self, fn) return index_key (fn, self) end) + + +M.index_value = DEPRECATED ("41", "'std.list.index_value'", + "compose 'std.functional.filter' and 'std.table.invert' instead", + index_value) +m.index_value = DEPRECATED ("41", "'std.list:index_value'", + function (self, fn) return index_value (fn, self) end) + + +M.map = DEPRECATED ("41", "'std.list.map'", + "use 'std.functional.map' instead", map) +m.map = DEPRECATED ("41", "'std.list:map'", + "use 'std.functional.map' instead", + function (self, fn) return map (fn, self) end) + + + +M.map_with = DEPRECATED ("41", "'std.list.map_with'", + "use 'std.functional.map_with' instead", map_with) + +M.project = DEPRECATED ("41", "'std.list.project'", + "use 'std.table.project' instead", project) +m.project = DEPRECATED ("41", "'std.list:project'", + "use 'std.table.project' instead", + function (self, x) return project (x, self) end) + +M.relems = DEPRECATED ("41", "'std.list.relems'", + "compose 'std.ielems' and 'std.ireverse' instead", relems) +m.relems = DEPRECATED ("41", "'std.list:relems'", relems) + +M.reverse = DEPRECATED ("41", "'std.list.reverse'", + "compose 'std.list' and 'std.ireverse' instead", reverse) +m.reverse = DEPRECATED ("41", "'std.list:reverse'", + "compose 'std.list' and 'std.ireverse' instead", reverse) + +M.shape = DEPRECATED ("41", "'std.list.shape'", + "use 'std.table.shape' instead", shape) +m.shape = DEPRECATED ("41", "'std.list:shape'", + "use 'std.table.shape' instead", + function (t, l) return shape (l, t) end) + +M.transpose = DEPRECATED ("41", "'std.list.transpose'", + "use 'std.functional.zip' instead", transpose) + +M.zip_with = DEPRECATED ("41", "'std.list.zip_with'", + "use 'std.functional.zip_with' instead", zip_with) + + + +--[[ ================== ]]-- +--[[ Type Declarations. ]]-- +--[[ ================== ]]-- + + +--- An Object derived List. +-- @object List + +List = Object { + -- Derived object type. + _type = "List", + _functions = M, -- FIXME: remove this when DEPRECATIONS have gone + __index = m, -- FIXME: `__index = M` when DEPRECATIONS have gone + + ------ + -- Concatenate lists. + -- @function __concat + -- @tparam List l a list + -- @tparam List|table m another list, or table (hash part is ignored) + -- @see concat + -- @usage + -- new = alist .. {"append", "these", "elements"} + __concat = concat, + + ------ + -- Append element to list. + -- @function __add + -- @tparam List l a list + -- @param e element to append + -- @see append + -- @usage + -- list = list + "element" + __add = append, + + ------ + -- List order operator. + -- @function __lt + -- @tparam List l a list + -- @tparam List m another list + -- @see compare + -- @usage + -- max = list1 > list2 and list1 or list2 + __lt = function (list1, list2) return compare (list1, list2) < 0 end, + + ------ + -- List equality or order operator. + -- @function __le + -- @tparam List l a list + -- @tparam List m another list + -- @see compare + -- @usage + -- min = list1 <= list2 and list1 or list2 + __le = function (list1, list2) return compare (list1, list2) <= 0 end, +} + + +return List diff --git a/lib/std/math.lua b/lib/std/math.lua new file mode 100644 index 0000000..2a87904 --- /dev/null +++ b/lib/std/math.lua @@ -0,0 +1,81 @@ +--[[-- + 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" + + @module std.math +]] + + +local base = require "std.base" + +local M + + +local _floor = math.floor + +local function floor (n, p) + if p and p ~= 0 then + local e = 10 ^ p + return _floor (n * e) / e + else + return _floor (n) + end +end + + +local function monkey_patch (namespace) + namespace = namespace or _G + namespace.math = base.copy (namespace.math or {}, M) + return M +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 require "std.debug".argscheck ("std.math." .. decl, fn) +end + + +M = { + --- 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), + + --- Overwrite core `math` methods with `std` enhanced versions. + -- @function monkey_patch + -- @tparam[opt=_G] table namespace where to install global functions + -- @treturn table the module table + -- @usage require "std.math".monkey_patch () + monkey_patch = X ("monkey_patch (?table)", monkey_patch), + + --- 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 base.merge (M, math) diff --git a/lib/std/object.lua b/lib/std/object.lua new file mode 100644 index 0000000..423507f --- /dev/null +++ b/lib/std/object.lua @@ -0,0 +1,227 @@ +--[[-- + Prototype-based objects. + + This module creates the root prototype object from which every other + object is descended. There are no classes as such, rather new objects + are created by cloning an existing object, and then changing or adding + to the clone. Further objects can then be made by cloning the changed + object, and so on. + + Objects are cloned by simply calling an existing object, which then + serves as a prototype from which the new object is copied. + + Note that Object methods are stored in the `__index` field of their + metatable, and so cannot also use `__index` to lookup references with + square brackets. See @{std.container} objects if you want to do that. + + Prototype Chain + --------------- + + table + `-> Object + + @classmod std.object +]] + + +-- Surprise!! The real root object is Container, which has less +-- functionality than Object, but that makes the heirarchy hard to +-- explain, so the documentation pretends this is the root object, and +-- Container is derived from it. Confused? ;-) + + +local base = require "std.base" +local container = require "std.container" + +local Container = container {} +local getmetamethod, prototype = base.getmetamethod, base.prototype + + + +--- Root object. +-- +-- Changing the values of these fields in a new object will change the +-- corresponding behaviour. +-- @object Object +-- @string[opt="Object"] _type object name +-- @tfield[opt={}] table|function _init object initialisation +-- @tfield table _functions module functions omitted when cloned +-- @see __call +-- @usage +-- -- `_init` can be a list of keys; then the unnamed `init_1` through +-- -- `init_m` values from the argument table are assigned to the +-- -- corresponding keys in `new_object`. +-- local Process = Object { +-- _type = "Process", +-- _init = { "status", "out", "err" }, +-- } +-- local process = Process { +-- procs[pid].status, procs[pid].out, procs[pid].err, -- auto assigned +-- command = pipeline[pid], -- manual assignment +-- } +-- @usage +-- -- Or it can be a function, in which the arguments passed to the +-- -- prototype during cloning are simply handed to the `_init` function. +-- local Bag = Object { +-- _type = "Bag", +-- _init = function (obj, ...) +-- for e in std.elems {...} do +-- obj[#obj + 1] = e +-- end +-- return obj +-- end, +-- } +-- local bag = Bag ("function", "arguments", "sent", "to", "_init") + +return Container { + _type = "Object", + + -- No need for explicit module functions here, because calls to, e.g. + -- `Object.prototype` will automatically fall back metamethods in + -- `__index`. + + __index = { + --- Clone an Object. + -- + -- Objects are essentially tables of `field_n = value_n` pairs. + -- + -- Normally `new_object` automatically shares a metatable with + -- `proto_object`. However, field names beginning with "_" are *private*, + -- and moved into the object metatable during cloning. So, adding new + -- private fields to an object during cloning will result in a new + -- metatable for `new_object` that also happens to contain a copy of all + -- the entries from the `proto_object` metatable. + -- + -- While clones of @{Object} inherit all properties of their prototype, + -- it's idiomatic to always keep separate tables for the module table and + -- the root object itself: That way you can't mistakenly engage the slower + -- clone-from-module-table process unnecessarily. + -- @static + -- @function clone + -- @tparam Object obj an object + -- @param ... a list of arguments if *obj.\_init* is a function, or a + -- single table if *obj.\_init* is a table. + -- @treturn Object a clone of *obj* + -- @see __call + -- @usage + -- local object = require "std.object" -- module table + -- local Object = object {} -- root object + -- local o = Object { + -- field_1 = "value_1", + -- method_1 = function (self) return self.field_1 end, + -- } + -- print (o.field_1) --> value_1 + -- o.field_2 = 2 + -- function o:method_2 (n) return self.field_2 + n end + -- print (o:method_2 (2)) --> 4 + -- os.exit (0) + clone = getmetamethod (container, "__call"), + + --- Type of an object, or primitive. + -- + -- It's conventional to organise similar objects according to a + -- string valued *\_type* field, which can then be queried using this + -- function. + -- + -- Additionally, this function returns the results of @{io.type} for + -- file objects, or @{type} otherwise. + -- + -- @static + -- @function prototype + -- @param x anything + -- @treturn string type of *x* + -- @usage + -- local Stack = Object { + -- _type = "Stack", + -- + -- __tostring = function (self) ... end, + -- + -- __index = { + -- push = function (self) ... end, + -- pop = function (self) ... end, + -- }, + -- } + -- local stack = Stack {} + -- assert (stack:prototype () == getmetatable (stack)._type) + -- + -- local prototype = Object.prototype + -- assert (prototype (stack) == getmetatable (stack)._type) + -- + -- local h = io.open (os.tmpname (), "w") + -- assert (prototype (h) == io.type (h)) + -- + -- assert (prototype {} == type {}) + prototype = prototype, + + + --- Return *obj* with references to the fields of *src* merged in. + -- + -- More importantly, split the fields in *src* between *obj* and its + -- metatable. If any field names begin with "_", attach a metatable + -- to *obj* by cloning the metatable from *src*, and then copy the + -- "private" `_` prefixed fields there. + -- + -- You might want to use this function to instantiate your derived + -- object clones when the *src.\_init* is a function -- when + -- *src.\_init* is a table, the default (inherited unless you overwrite + -- it) clone method calls @{mapfields} automatically. When you're + -- using a function `_init` setting, @{clone} doesn't know what to + -- copy into a new object from the `_init` function's arguments... + -- so you're on your own. Except that calling @{mapfields} inside + -- `_init` is safer than manually splitting `src` into `obj` and + -- its metatable, because you'll pick up any fixes and changes when + -- you upgrade stdlib. + -- @static + -- @function mapfields + -- @tparam table obj destination object + -- @tparam table src fields to copy int clone + -- @tparam[opt={}] table map key renames as `{old_key=new_key, ...}` + -- @treturn table *obj* with non-private fields from *src* merged, + -- and a metatable with private fields (if any) merged, both sets + -- of keys renamed according to *map* + -- @usage + -- myobject.mapfields = function (obj, src, map) + -- object.mapfields (obj, src, map) + -- ... + -- end + mapfields = container.mapfields.call, + + + -- Backwards compatibility: + type = prototype, + }, + + + --- Return a @{clone} of this object, and its metatable. + -- + -- Private fields are stored in the metatable. + -- @function __call + -- @param ... arguments for prototype's *\_init* + -- @treturn Object a clone of the this object. + -- @see clone + -- @usage + -- local Object = require "std.object" {} -- not a typo! + -- new = Object {"initialisation", "elements"} + + + --- Return an in-order iterator over public object fields. + -- @function __pairs + -- @treturn function iterator function + -- @treturn Object *self* + -- @usage + -- for k, v in std.pairs (anobject) do process (k, v) end + + + --- Return a string representation of this object. + -- + -- First the object type, and then between { and } a list of the + -- array part of the object table (without numeric keys) followed + -- by the remaining key-value pairs. + -- + -- This function doesn't recurse explicity, but relies upon suitable + -- `__tostring` metamethods in field values. + -- @function __tostring + -- @treturn string stringified object representation + -- @see tostring + -- @usage print (anobject) +} diff --git a/lib/std/operator.lua b/lib/std/operator.lua new file mode 100644 index 0000000..1f77e78 --- /dev/null +++ b/lib/std/operator.lua @@ -0,0 +1,163 @@ +--[[-- + Functional forms of Lua operators. + + @module std.operator +]] + +local base = require "std.base" + +local tostring = base.tostring + + +local M = { + --- Stringify and concatenate arguments. + -- @param a an argument + -- @param b another argument + -- @return concatenation of stringified arguments. + -- @usage + -- --> "=> 1000010010" + -- functional.foldl (concat, "=> ", {10000, 100, 10}) + concat = function (a, b) return tostring (a) .. tostring (b) end, + + --- Dereference a table. + -- @tparam table t a table + -- @param k a key to lookup in *t* + -- @return value stored at *t[k]* if any, otherwise `nil` + -- @usage + -- --> 4 + -- functional.foldl (get, {1, {{2, 3, 4}, 5}}, {2, 1, 3}) + get = function (t, k) return t and t[k] or nil end, + + --- Set a table element, honoring metamethods. + -- @tparam table t a table + -- @param k a key to lookup in *t* + -- @param v a value to set for *k* + -- @treturn table *t* + -- @usage + -- -- destructive table merge: + -- --> {"one", bar="baz", two=5} + -- functional.reduce (set, {"foo", bar="baz"}, {"one", two=5}) + set = function (t, k, v) t[k]=v; return t end, + + --- Return the sum of the arguments. + -- @param a an argument + -- @param b another argument + -- @return the sum of the *a* and *b* + -- @usage + -- --> 10110 + -- functional.foldl (sum, {10000, 100, 10}) + sum = function (a, b) return a + b end, + + --- Return the difference of the arguments. + -- @param a an argument + -- @param b another argument + -- @return the difference between *a* and *b* + -- @usage + -- --> 890 + -- functional.foldl (diff, {10000, 100, 10}) + diff = function (a, b) return a - b end, + + --- Return the product of the arguments. + -- @param a an argument + -- @param b another argument + -- @return the product of *a* and *b* + -- @usage + -- --> 10000000 + -- functional.foldl (prod, {10000, 100, 10}) + prod = function (a, b) return a * b end, + + --- Return the quotient of the arguments. + -- @param a an argument + -- @param b another argument + -- @return the quotient *a* and *b* + -- @usage + -- --> 1000 + -- functional.foldr (quot, {10000, 100, 10}) + quot = function (a, b) return a / b end, + + --- Return the modulus of the arguments. + -- @param a an argument + -- @param b another argument + -- @return the modulus of *a* and *b* + -- @usage + -- --> 3 + -- functional.foldl (mod, {65536, 100, 11}) + mod = function (a, b) return a % b end, + + --- Return the exponent of the arguments. + -- @param a an argument + -- @param b another argument + -- @return the *a* to the power of *b* + -- @usage + -- --> 4096 + -- functional.foldl (pow, {2, 3, 4}) + pow = function (a, b) return a ^ b end, + + --- Return the logical conjunction of the arguments. + -- @param a an argument + -- @param b another argument + -- @return logical *a* and *b* + -- @usage + -- --> true + -- functional.foldl (conj, {true, 1, "false"}) + conj = function (a, b) return a and b end, + + --- Return the logical disjunction of the arguments. + -- @param a an argument + -- @param b another argument + -- @return logical *a* or *b* + -- @usage + -- --> true + -- functional.foldl (disj, {true, 1, false}) + disj = function (a, b) return a or b end, + + --- Return the logical negation of the arguments. + -- @param a an argument + -- @return not *a* + -- @usage + -- --> {true, false, false, false} + -- functional.bind (functional.map, {std.ielems, neg}) {false, true, 1, 0} + neg = function (a) return not a end, + + --- Return the equality of the arguments. + -- @param a an argument + -- @param b another argument + -- @return `true` if *a* is *b*, otherwise `false` + eq = function (a, b) return a == b end, + + --- Return the inequality of the arguments. + -- @param a an argument + -- @param b another argument + -- @return `false` if *a* is *b*, otherwise `true` + -- @usage + -- --> true + -- local f = require "std.functional" + -- table.empty (f.filter (f.bind (neq, {6}), std.ielems, {6, 6, 6}) + neq = function (a, b) return a ~= b end, + + --- Return whether the arguments are in ascending order. + -- @param a an argument + -- @param b another argument + -- @return `true` if *a* is less then *b*, otherwise `false` + lt = function (a, b) return a < b end, + + --- Return whether the arguments are not in descending order. + -- @param a an argument + -- @param b another argument + -- @return `true` if *a* is not greater then *b*, otherwise `false` + lte = function (a, b) return a <= b end, + + --- Return whether the arguments are in descending order. + -- @param a an argument + -- @param b another argument + -- @return `true` if *a* is greater then *b*, otherwise `false` + gt = function (a, b) return a > b end, + + --- Return whether the arguments are not in ascending order. + -- @param a an argument + -- @param b another argument + -- @return `true` if *a* is not greater then *b*, otherwise `false` + gte = function (a, b) return a >= b end, +} + +return M diff --git a/lib/std/optparse.lua b/lib/std/optparse.lua new file mode 100644 index 0000000..29ebe93 --- /dev/null +++ b/lib/std/optparse.lua @@ -0,0 +1,728 @@ +--[=[-- + Parse and process command line options. + + Prototype Chain + --------------- + + table + `-> Object + `-> OptionParser + + @classmod std.optparse +]=] + + +local base = require "std.base" + +local Object = require "std.object" {} + +local ipairs, pairs = base.ipairs, base.pairs +local insert, last, len = base.insert, base.last, base.len + + + +--[[ ================= ]]-- +--[[ Helper Functions. ]]-- +--[[ ================= ]]-- + + +local optional, required + + +--- Normalise an argument list. +-- Separate short options, remove `=` separators from +-- `--long-option=optarg` etc. +-- @local +-- @function normalise +-- @tparam table arglist list of arguments to normalise +-- @treturn table normalised argument list +local function normalise (self, arglist) + local normal = {} + local i = 0 + while i < len (arglist) do + i = i + 1 + local opt = arglist[i] + + -- Split '--long-option=option-argument'. + if opt:sub (1, 2) == "--" then + local x = opt:find ("=", 3, true) + if x then + local optname = opt:sub (1, x -1) + + -- Only split recognised long options. + if self[optname] then + insert (normal, optname) + insert (normal, opt:sub (x + 1)) + else + x = nil + end + end + + if x == nil then + -- No '=', or substring before '=' is not a known option name. + insert (normal, opt) + end + + elseif opt:sub (1, 1) == "-" and string.len (opt) > 2 then + local orig, split, rest = opt, {} + repeat + opt, rest = opt:sub (1, 2), opt:sub (3) + + split[#split + 1] = opt + + -- If there's no handler, the option was a typo, or not supposed + -- to be an option at all. + if self[opt] == nil then + opt, split = nil, { orig } + + -- Split '-xyz' into '-x -yz', and reiterate for '-yz' + elseif self[opt].handler ~= optional and + self[opt].handler ~= required then + if string.len (rest) > 0 then + opt = "-" .. rest + else + opt = nil + end + + -- Split '-xshortargument' into '-x shortargument'. + else + split[#split + 1] = rest + opt = nil + end + until opt == nil + + -- Append split options to normalised list + for _, v in ipairs (split) do insert (normal, v) end + else + insert (normal, opt) + end + end + + normal[-1], normal[0] = arglist[-1], arglist[0] + return normal +end + + +--- Store `value` with `opt`. +-- @local +-- @function set +-- @string opt option name +-- @param value option argument value +local function set (self, opt, value) + local key = self[opt].key + local opts = self.opts[key] + + if type (opts) == "table" then + insert (opts, value) + elseif opts ~= nil then + self.opts[key] = { opts, value } + else + self.opts[key] = value + end +end + + + +--[[ ============= ]]-- +--[[ Option Types. ]]-- +--[[ ============= ]]-- + + +--- Option at `arglist[i]` can take an argument. +-- Argument is accepted only if there is a following entry that does not +-- begin with a '-'. +-- +-- This is the handler automatically assigned to options that have +-- `--opt=[ARG]` style specifications in the @{OptionParser} spec +-- argument. You can also pass it as the `handler` argument to @{on} for +-- options you want to add manually without putting them in the +-- @{OptionParser} spec. +-- +-- Like @{required}, this handler will store multiple occurrences of a +-- command-line option. +-- @static +-- @tparam table arglist list of arguments +-- @int i index of last processed element of *arglist* +-- @param[opt=true] value either a function to process the option +-- argument, or a default value if encountered without an optarg +-- @treturn int index of next element of *arglist* to process +-- @usage +-- parser:on ("--enable-nls", parser.option, parser.boolean) +function optional (self, arglist, i, value) + if i + 1 <= len (arglist) and arglist[i + 1]:sub (1, 1) ~= "-" then + return self:required (arglist, i, value) + end + + if type (value) == "function" then + value = value (self, opt, nil) + elseif value == nil then + value = true + end + + set (self, arglist[i], value) + return i + 1 +end + + +--- Option at `arglist[i}` requires an argument. +-- +-- This is the handler automatically assigned to options that have +-- `--opt=ARG` style specifications in the @{OptionParser} spec argument. +-- You can also pass it as the `handler` argument to @{on} for options +-- you want to add manually without putting them in the @{OptionParser} +-- spec. +-- +-- Normally the value stored in the `opt` table by this handler will be +-- the string given as the argument to that option on the command line. +-- However, if the option is given on the command-line multiple times, +-- `opt["name"]` will end up with all those arguments stored in the +-- array part of a table: +-- +-- $ cat ./prog +-- ... +-- parser:on ({"-e", "-exec"}, required) +-- _G.arg, _G.opt = parser:parse (_G.arg) +-- print std.string.tostring (_G.opt.exec) +-- ... +-- $ ./prog -e '(foo bar)' -e '(foo baz)' -- qux +-- {1=(foo bar),2=(foo baz)} +-- @static +-- @tparam table arglist list of arguments +-- @int i index of last processed element of *arglist* +-- @param[opt] value either a function to process the option argument, +-- or a forced value to replace the user's option argument. +-- @treturn int index of next element of *arglist* to process +-- @usage +-- parser:on ({"-o", "--output"}, parser.required) +function required (self, arglist, i, value) + local opt = arglist[i] + if i + 1 > len (arglist) then + self:opterr ("option '" .. opt .. "' requires an argument") + return i + 1 + end + + if type (value) == "function" then + value = value (self, opt, arglist[i + 1]) + elseif value == nil then + value = arglist[i + 1] + end + + set (self, opt, value) + return i + 2 +end + + +--- Finish option processing +-- +-- This is the handler automatically assigned to the option written as +-- `--` in the @{OptionParser} spec argument. You can also pass it as +-- the `handler` argument to @{on} if you want to manually add an end +-- of options marker without writing it in the @{OptionParser} spec. +-- +-- This handler tells the parser to stop processing arguments, so that +-- anything after it will be an argument even if it otherwise looks +-- like an option. +-- @static +-- @tparam table arglist list of arguments +-- @int i index of last processed element of `arglist` +-- @treturn int index of next element of `arglist` to process +-- @usage +-- parser:on ("--", parser.finished) +local function finished (self, arglist, i) + for opt = i + 1, len (arglist) do + insert (self.unrecognised, arglist[opt]) + end + return 1 + len (arglist) +end + + +--- Option at `arglist[i]` is a boolean switch. +-- +-- This is the handler automatically assigned to options that have +-- `--long-opt` or `-x` style specifications in the @{OptionParser} spec +-- argument. You can also pass it as the `handler` argument to @{on} for +-- options you want to add manually without putting them in the +-- @{OptionParser} spec. +-- +-- Beware that, _unlike_ @{required}, this handler will store multiple +-- occurrences of a command-line option as a table **only** when given a +-- `value` function. Automatically assigned handlers do not do this, so +-- the option will simply be `true` if the option was given one or more +-- times on the command-line. +-- @static +-- @tparam table arglist list of arguments +-- @int i index of last processed element of *arglist* +-- @param[opt] value either a function to process the option argument, +-- or a value to store when this flag is encountered +-- @treturn int index of next element of *arglist* to process +-- @usage +-- parser:on ({"--long-opt", "-x"}, parser.flag) +local function flag (self, arglist, i, value) + local opt = arglist[i] + if type (value) == "function" then + set (self, opt, value (self, opt, true)) + elseif value == nil then + local key = self[opt].key + self.opts[key] = true + end + + return i + 1 +end + + +--- Option should display help text, then exit. +-- +-- This is the handler automatically assigned tooptions that have +-- `--help` in the specification, e.g. `-h, -?, --help`. +-- @static +-- @function help +-- @usage +-- parser:on ("-?", parser.version) +local function help (self) + print (self.helptext) + os.exit (0) +end + + +--- Option should display version text, then exit. +-- +-- This is the handler automatically assigned tooptions that have +-- `--version` in the specification, e.g. `-V, --version`. +-- @static +-- @function version +-- @usage +-- parser:on ("-V", parser.version) +local function version (self) + print (self.versiontext) + os.exit (0) +end + + + +--[[ =============== ]]-- +--[[ Argument Types. ]]-- +--[[ =============== ]]-- + + +--- Map various option strings to equivalent Lua boolean values. +-- @table boolvals +-- @field false false +-- @field 0 false +-- @field no false +-- @field n false +-- @field true true +-- @field 1 true +-- @field yes true +-- @field y true +local boolvals = { + ["false"] = false, ["true"] = true, + ["0"] = false, ["1"] = true, + no = false, yes = true, + n = false, y = true, +} + + +--- Return a Lua boolean equivalent of various *optarg* strings. +-- Report an option parse error if *optarg* is not recognised. +-- +-- Pass this as the `value` function to @{on} when you want various +-- "truthy" or "falsey" option arguments to be coerced to a Lua `true` +-- or `false` respectively in the options table. +-- @static +-- @string opt option name +-- @string[opt="1"] optarg option argument, must be a key in @{boolvals} +-- @treturn bool `true` or `false` +-- @usage +-- parser:on ("--enable-nls", parser.optional, parser.boolean) +local function boolean (self, opt, optarg) + if optarg == nil then optarg = "1" end -- default to truthy + local b = boolvals[tostring (optarg):lower ()] + if b == nil then + return self:opterr (optarg .. ": Not a valid argument to " ..opt[1] .. ".") + end + return b +end + + +--- Report an option parse error unless *optarg* names an +-- existing file. +-- +-- Pass this as the `value` function to @{on} when you want to accept +-- only option arguments that name an existing file. +-- @fixme this only checks whether the file has read permissions +-- @static +-- @string opt option name +-- @string optarg option argument, must be an existing file +-- @treturn string *optarg* +-- @usage +-- parser:on ("--config-file", parser.required, parser.file) +local function file (self, opt, optarg) + local h, errmsg = io.open (optarg, "r") + if h == nil then + return self:opterr (optarg .. ": " .. errmsg) + end + h:close () + return optarg +end + + + +--[[ =============== ]]-- +--[[ Option Parsing. ]]-- +--[[ =============== ]]-- + + +--- Report an option parse error, then exit with status 2. +-- +-- Use this in your custom option handlers for consistency with the +-- error output from built-in @{std.optparse} error messages. +-- @static +-- @string msg error message +local function opterr (self, msg) + local prog = self.program + -- Ensure final period. + if msg:match ("%.$") == nil then msg = msg .. "." end + io.stderr:write (prog .. ": error: " .. msg .. "\n") + io.stderr:write (prog .. ": Try '" .. prog .. " --help' for help.\n") + os.exit (2) +end + + +------ +-- Function signature of an option handler for @{on}. +-- @function on_handler +-- @tparam table arglist list of arguments +-- @int i index of last processed element of *arglist* +-- @param[opt=nil] value additional `value` registered with @{on} +-- @treturn int index of next element of *arglist* to process + + +--- Add an option handler. +-- +-- When the automatically assigned option handlers don't do everything +-- you require, or when you don't want to put an option into the +-- @{OptionParser} `spec` argument, use this function to specify custom +-- behaviour. If you write the option into the `spec` argument anyway, +-- calling this function will replace the automatically assigned handler +-- with your own. +-- +-- When writing your own handlers for @{std.optparse:on}, you only need +-- to deal with normalised arguments, because combined short arguments +-- (`-xyz`), equals separators to long options (`--long=ARG`) are fully +-- expanded before any handler is called. +-- @function on +-- @tparam[string|table] opts name of the option, or list of option names +-- @tparam on_handler handler function to call when any of *opts* is +-- encountered +-- @param value additional value passed to @{on_handler} +-- @usage +-- -- Don't process any arguments after `--` +-- parser:on ('--', parser.finished) +local function on (self, opts, handler, value) + if type (opts) == "string" then opts = { opts } end + handler = handler or flag -- unspecified options behave as flags + + local normal = {} + for _, optspec in ipairs (opts) do + optspec:gsub ("(%S+)", + function (opt) + -- 'x' => '-x' + if string.len (opt) == 1 then + opt = "-" .. opt + + -- 'option-name' => '--option-name' + elseif opt:match ("^[^%-]") ~= nil then + opt = "--" .. opt + end + + if opt:match ("^%-[^%-]+") ~= nil then + -- '-xyz' => '-x -y -z' + for i = 2, string.len (opt) do + insert (normal, "-" .. opt:sub (i, i)) + end + else + insert (normal, opt) + end + end) + end + + -- strip leading '-', and convert non-alphanums to '_' + local key = last (normal):match ("^%-*(.*)$"):gsub ("%W", "_") + + for _, opt in ipairs (normal) do + self[opt] = { key = key, handler = handler, value = value } + end +end + + +------ +-- Parsed options table, with a key for each encountered option, each +-- with value set by that option's @{on_handler}. Where an option +-- has one or more long-options specified, the key will be the first +-- one of those with leading hyphens stripped and non-alphanumeric +-- characters replaced with underscores. For options that can only be +-- specified by a short option, the key will be the letter of the first +-- of the specified short options: +-- +-- {"-e", "--eval-file"} => opts.eval_file +-- {"-n", "--dryrun", "--dry-run"} => opts.dryrun +-- {"-t", "-T"} => opts.t +-- +-- Generally there will be one key for each previously specified +-- option (either automatically assigned by @{OptionParser} or +-- added manually with @{on}) containing the value(s) assigned by the +-- associated @{on_handler}. For automatically assigned handlers, +-- that means `true` for straight-forward flags and +-- optional-argument options for which no argument was given; or else +-- the string value of the argument passed with an option given only +-- once; or a table of string values of the same for arguments given +-- multiple times. +-- +-- ./prog -x -n -x => opts = { x = true, dryrun = true } +-- ./prog -e '(foo bar)' -e '(foo baz)' +-- => opts = {eval_file = {"(foo bar)", "(foo baz)"} } +-- +-- If you write your own handlers, or otherwise specify custom +-- handling of options with @{on}, then whatever value those handlers +-- return will be assigned to the respective keys in `opts`. +-- @table opts + + +--- Parse an argument list. +-- @tparam table arglist list of arguments +-- @tparam[opt] table defaults table of default option values +-- @treturn table a list of unrecognised *arglist* elements +-- @treturn opts parsing results +local function parse (self, arglist, defaults) + self.unrecognised, self.opts = {}, {} + + arglist = normalise (self, arglist) + + local i = 1 + while i > 0 and i <= len (arglist) do + local opt = arglist[i] + + if self[opt] == nil then + insert (self.unrecognised, opt) + i = i + 1 + + -- Following non-'-' prefixed argument is an optarg. + if i <= len (arglist) and arglist[i]:match "^[^%-]" then + insert (self.unrecognised, arglist[i]) + i = i + 1 + end + + -- Run option handler functions. + else + assert (type (self[opt].handler) == "function") + + i = self[opt].handler (self, arglist, i, self[opt].value) + end + end + + -- Merge defaults into user options. + for k, v in pairs (defaults or {}) do + if self.opts[k] == nil then self.opts[k] = v end + end + + -- metatable allows `io.warn` to find `parser.program` when assigned + -- back to _G.opts. + return self.unrecognised, setmetatable (self.opts, {__index = self}) +end + + +--- Take care not to register duplicate handlers. +-- @param current current handler value +-- @param new new handler value +-- @return `new` if `current` is nil +local function set_handler (current, new) + assert (current == nil, "only one handler per option") + return new +end + + +local function _init (_, spec) + local parser = {} + + parser.versiontext, parser.version, parser.helptext, parser.program = + spec:match ("^([^\n]-(%S+)\n.-)%s*([Uu]sage: (%S+).-)%s*$") + + if parser.versiontext == nil then + error ("OptionParser spec argument must match '\\n" .. + "...Usage: ...'") + end + + -- Collect helptext lines that begin with two or more spaces followed + -- by a '-'. + local specs = {} + parser.helptext:gsub ("\n %s*(%-[^\n]+)", + function (spec) insert (specs, spec) end) + + -- Register option handlers according to the help text. + for _, spec in ipairs (specs) do + local options, handler = {} + + -- Loop around each '-' prefixed option on this line. + while spec:sub (1, 1) == "-" do + + -- Capture end of options processing marker. + if spec:match "^%-%-,?%s" then + handler = set_handler (handler, finished) + + -- Capture optional argument in the option string. + elseif spec:match "^%-[%-%w]+=%[.+%],?%s" then + handler = set_handler (handler, optional) + + -- Capture required argument in the option string. + elseif spec:match "^%-[%-%w]+=%S+,?%s" then + handler = set_handler (handler, required) + + -- Capture any specially handled arguments. + elseif spec:match "^%-%-help,?%s" then + handler = set_handler (handler, help) + + elseif spec:match "^%-%-version,?%s" then + handler = set_handler (handler, version) + end + + -- Consume argument spec, now that it was processed above. + spec = spec:gsub ("^(%-[%-%w]+)=%S+%s", "%1 ") + + -- Consume short option. + local _, c = spec:gsub ("^%-([-%w]),?%s+(.*)$", + function (opt, rest) + if opt == "-" then opt = "--" end + insert (options, opt) + spec = rest + end) + + -- Be careful not to consume more than one option per iteration, + -- otherwise we might miss a handler test at the next loop. + if c == 0 then + -- Consume long option. + spec:gsub ("^%-%-([%-%w]+),?%s+(.*)$", + function (opt, rest) + insert (options, opt) + spec = rest + end) + end + end + + -- Unless specified otherwise, treat each option as a flag. + on (parser, options, handler or flag) + end + + return parser +end + + +--- Signature for initialising a custom OptionParser. +-- +-- Read the documented options from *spec* and return custom parser that +-- can be used for parsing the options described in *spec* from a run-time +-- argument list. Options in *spec* are recognised as lines that begin +-- with at least two spaces, followed by a hyphen. +-- @static +-- @function OptionParser_Init +-- @string spec option parsing specification +-- @treturn OptionParser a parser for options described by *spec* +-- @usage +-- customparser = std.optparse (optparse_spec) + + +--- OptionParser prototype object. +-- +-- Most often, after instantiating an @{OptionParser}, everything else +-- is handled automatically. +-- +-- Then, calling `parser:parse` as shown below saves unparsed arguments +-- into `_G.arg` (usually filenames or similar), and `_G.opts` will be a +-- table of successfully parsed option values. The keys into this table +-- are the long-options with leading hyphens stripped, and non-word +-- characters turned to `_`. For example if `--another-long` had been +-- found in the initial `_G.arg`, then `_G.opts` will have a key named +-- `another_long`, with an appropriate value. If there is no long +-- option name, then the short option is used, i.e. `_G.opts.b` will be +-- set. +-- +-- The values saved against those keys are controlled by the option +-- handler, usually just `true` or the option argument string as +-- appropriate. +-- @object OptionParser +-- @tparam OptionParser_Init _init initialisation function +-- @string program the first word following "Usage:" from *spec* +-- @string version the last white-space delimited word on the first line +-- of text from *spec* +-- @string versiontext everything preceding "Usage:" from *spec*, and +-- which will be displayed by the @{version} @{on_handler} +-- @string helptext everything including and following "Usage:" from +-- *spec* string and which will be displayed by the @{help} +-- @{on_handler} +-- @usage +-- local std = require "std" +-- +-- local optparser = std.optparse [[ +-- any text VERSION +-- Additional lines of text to show when the --version +-- option is passed. +-- +-- Several lines or paragraphs are permitted. +-- +-- Usage: PROGNAME +-- +-- Banner text. +-- +-- Optional long description text to show when the --help +-- option is passed. +-- +-- Several lines or paragraphs of long description are permitted. +-- +-- Options: +-- +-- -b a short option with no long option +-- --long a long option with no short option +-- --another-long a long option with internal hypen +-- -v, --verbose a combined short and long option +-- -n, --dryrun, --dry-run several spellings of the same option +-- -u, --name=USER require an argument +-- -o, --output=[FILE] accept an optional argument +-- --version display version information, then exit +-- --help display this help, then exit +-- +-- Footer text. Several lines or paragraphs are permitted. +-- +-- Please report bugs at bug-list@yourhost.com +-- ]] +-- +-- -- Note that @{std.io.die} and @{std.io.warn} will only prefix messages +-- -- with `parser.program` if the parser options are assigned back to +-- -- `_G.opts`: +-- _G.arg, _G.opts = optparser:parse (_G.arg) +return Object { + _type = "OptionParser", + + _init = _init, + + -- Prototype initial values. + opts = {}, + helptext = "", + program = "", + versiontext = "", + version = 0, + + --- @export + __index = { + boolean = boolean, + file = file, + finished = finished, + flag = flag, + help = help, + optional = optional, + required = required, + version = version, + + on = on, + opterr = opterr, + parse = parse, + }, +} diff --git a/lib/std/package.lua b/lib/std/package.lua new file mode 100644 index 0000000..562c531 --- /dev/null +++ b/lib/std/package.lua @@ -0,0 +1,217 @@ +--[[-- + 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" + + @module std.package +]] + + +local base = require "std.base" +local debug = require "std.debug" + +local catfile, escape_pattern, invert = + base.catfile, base.escape_pattern, base.invert +local ipairs, pairs, split, unpack = + base.ipairs, base.pairs, base.split, base.unpack + +local M + + + +--- 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 path_mark 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 dirsep, pathsep, path_mark, execdir, igmark = + string.match (package.config, "^([^\n]+)\n([^\n]+)\n([^\n]+)\n([^\n]+)\n([^\n]+)") + + +local function pathsub (path) + return path:gsub ("%%?.", function (capture) + if capture == "?" then + return path_mark + elseif capture == "/" then + return dirsep + else + return capture:gsub ("^%%", "", 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 paths[i]:find (patt) then return i, paths[i] end + end +end + + +local function normalize (...) + local i, paths, pathstrings = 1, {}, table.concat ({...}, pathsep) + for _, path in ipairs (split (pathstrings, pathsep)) do + path = pathsub (path): + gsub (catfile ("^[^", "]"), catfile (".", "%0")): + gsub (catfile ("", "%.", ""), dirsep): + gsub (catfile ("", "%.$"), ""): + gsub (catfile ("^%.", "%..", ""), catfile ("..", "")): + gsub (catfile ("", "$"), "") + + -- Carefully remove redundant /foo/../ matches. + repeat + local again = false + path = path:gsub (catfile ("", "([^", "]+)", "%.%.", ""), + function (dir1) + if dir1 == ".." then -- don't remove /../../ + return catfile ("", "..", "..", "") + else + again = true + return dirsep + end + end): + gsub (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 table.concat (invert (paths), pathsep) +end + + +local function insert (pathstrings, ...) + local paths = split (pathstrings, pathsep) + table.insert (paths, ...) + return normalize (unpack (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 table.concat (paths, pathsep) +end + + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X (decl, fn) + return debug.argscheck ("std.package." .. decl, fn) +end + +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 + -- `path_mark` (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), +} + + +M.dirsep = dirsep +M.execdir = execdir +M.igmark = igmark +M.path_mark = path_mark +M.pathsep = pathsep + + +for k, v in pairs (package) do + M[k] = M[k] or v +end + +return 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/lib/std/set.lua b/lib/std/set.lua new file mode 100644 index 0000000..dacb33a --- /dev/null +++ b/lib/std/set.lua @@ -0,0 +1,363 @@ +--[[-- + Set container prototype. + + Note that Functions listed below are only available from the Set + prototype returned by requiring this module, because Container + objects cannot have object methods. + + Prototype Chain + --------------- + + table + `-> Object + `-> Container + `-> Set + + @classmod std.set + @see std.container + ]] + +local base = require "std.base" + +local Container = require "std.container" {} + +local ielems, pairs, prototype = base.ielems, base.pairs, base.prototype + + +local Set -- forward declaration + + + +--[[ ==================== ]]-- +--[[ Primitive Functions. ]]-- +--[[ ==================== ]]-- + + +-- These functions know about internal implementatation. +-- The representation is a table whose tags are the elements, and +-- whose values are true. + + +local elems = base.pairs + + +local function insert (set, e) + return rawset (set, e, true) +end + + +local function member (set, e) + return rawget (set, e) == true +end + + + +--[[ ===================== ]]-- +--[[ High Level Functions. ]]-- +--[[ ===================== ]]-- + + +-- These functions are independent of the internal implementation. + + +local difference, symmetric_difference, intersection, union, subset, + proper_subset, equal + + +function difference (set1, set2) + local r = Set {} + for e in elems (set1) do + if not member (set2, e) then + insert (r, e) + end + end + return r +end + + +function symmetric_difference (set1, set2) + return difference (union (set1, set2), intersection (set2, set1)) +end + + +function intersection (set1, set2) + local r = Set {} + for e in elems (set1) do + if member (set2, e) then + insert (r, e) + end + end + return r +end + + +function union (set1, set2) + local r = set1 {} + for e in elems (set2) do + insert (r, e) + end + return r +end + + +function subset (set1, set2) + for e in elems (set1) do + if not member (set2, e) then + return false + end + end + return true +end + + +function proper_subset (set1, set2) + return subset (set1, set2) and not subset (set2, set1) +end + + +function equal (set1, set2) + return subset (set1, set2) and subset (set2, set1) +end + + + +--[[ =========== ]]-- +--[[ Set Object. ]]-- +--[[ =========== ]]-- + + +local function X (decl, fn) + return require "std.debug".argscheck ("std.set." .. decl, fn) +end + + +--- Set prototype object. +-- +-- Set also inherits all the fields and methods from +-- @{std.container.Container}. +-- @object Set +-- @string[opt="Set"] _type object name +-- @see std.container +-- @see std.object.__call +-- @usage +-- local std = require "std" +-- std.prototype (std.set) --> "Set" +-- os.exit (0) +Set = Container { + _type = "Set", + + _init = function (self, t) + for e in ielems (t) do + insert (self, e) + end + return self + end, + + --- Union operator. + -- @static + -- @function __add + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn Set everything from *set1* plus everything from *set2* + -- @see union + -- @usage + -- union = set1 + set2 + __add = union, + + --- Difference operator. + -- @static + -- @function __sub + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn Set everything from *set1* that is not also in *set2* + -- @see difference + -- @usage + -- difference = set1 - set2 + __sub = difference, + + --- Intersection operator. + -- @static + -- @function __mul + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn Set anything this is in both *set1* and *set2* + -- @see intersection + -- @usage + -- intersection = set1 * set2 + __mul = intersection, + + --- Symmetric difference operator. + -- @function __div + -- @static + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn Set everything from *set1* or *set2* but not both + -- @see symmetric_difference + -- @usage + -- symmetric_difference = set1 / set2 + __div = symmetric_difference, + + --- Subset operator. + -- @static + -- @function __le + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn boolean `true` if everything in *set1* is also in *set2* + -- @see subset + -- @usage + -- issubset = set1 <= set2 + __le = subset, + + --- Proper subset operator. + -- @static + -- @function __lt + -- @tparam Set set1 set + -- @tparam Set set2 another set + -- @treturn boolean `true` if *set2* is not equal to *set1*, but does + -- contain everything from *set1* + -- @see proper_subset + -- @usage + -- ispropersubset = set1 < set2 + __lt = proper_subset, + + -- Return a string representation of this set. + -- @treturn string string representation of a set. + -- @see std.tostring + __tostring = function (self) + local keys = {} + for k in pairs (self) do + keys[#keys + 1] = tostring (k) + end + table.sort (keys) + return prototype (self) .. " {" .. table.concat (keys, ", ") .. "}" + end, + + + _functions = { + --- Delete an element from a set. + -- @static + -- @function delete + -- @tparam Set set a set + -- @param e element + -- @treturn Set the modified *set* + -- @usage + -- set.delete (available, found) + delete = X ("delete (Set, any)", + function (set, e) return rawset (set, e, nil) end), + + --- Find the difference of two sets. + -- @static + -- @function difference + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn Set a copy of *set1* with elements of *set2* removed + -- @usage + -- all = set.difference (all, {32, 49, 56}) + difference = X ("difference (Set, Set)", difference), + + --- Iterator for sets. + -- @static + -- @function elems + -- @tparam Set set a set + -- @treturn *set* iterator + -- @todo Make the iterator return only the key + -- @usage + -- for code in set.elems (isprintable) do print (code) end + elems = X ("elems (Set)", elems), + + --- Find whether two sets are equal. + -- @static + -- @function equal + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn boolean `true` if *set1* and *set2* each contain identical + -- elements, `false` otherwise + -- @usage + -- if set.equal (keys, {META, CTRL, "x"}) then process (keys) end + equal = X ( "equal (Set, Set)", equal), + + --- Insert an element into a set. + -- @static + -- @function insert + -- @tparam Set set a set + -- @param e element + -- @treturn Set the modified *set* + -- @usage + -- for byte = 32,126 do + -- set.insert (isprintable, string.char (byte)) + -- end + insert = X ("insert (Set, any)", insert), + + --- Find the intersection of two sets. + -- @static + -- @function intersection + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn Set a new set with elements in both *set1* and *set2* + -- @usage + -- common = set.intersection (a, b) + intersection = X ("intersection (Set, Set)", intersection), + + --- Say whether an element is in a set. + -- @static + -- @function difference + -- @tparam Set set a set + -- @param e element + -- @return `true` if *e* is in *set*, otherwise `false` + -- otherwise + -- @usage + -- if not set.member (keyset, pressed) then return nil end + member = X ("member (Set, any)", member), + + --- Find whether one set is a proper subset of another. + -- @static + -- @function proper_subset + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn boolean `true` if *set2* contains all elements in *set1* + -- but not only those elements, `false` otherwise + -- @usage + -- if set.proper_subset (a, b) then + -- for e in set.elems (set.difference (b, a)) do + -- set.delete (b, e) + -- end + -- end + -- assert (set.equal (a, b)) + proper_subset = X ("proper_subset (Set, Set)", proper_subset), + + --- Find whether one set is a subset of another. + -- @static + -- @function subset + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn boolean `true` if all elements in *set1* are also in *set2*, + -- `false` otherwise + -- @usage + -- if set.subset (a, b) then a = b end + subset = X ("subset (Set, Set)", subset), + + --- Find the symmetric difference of two sets. + -- @static + -- @function symmetric_difference + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn Set a new set with elements that are in *set1* or *set2* + -- but not both + -- @usage + -- unique = set.symmetric_difference (a, b) + symmetric_difference = X ("symmetric_difference (Set, Set)", + symmetric_difference), + + --- Find the union of two sets. + -- @static + -- @function union + -- @tparam Set set1 a set + -- @tparam Set set2 another set + -- @treturn Set a copy of *set1* with elements in *set2* merged in + -- @usage + -- all = set.union (a, b) + union = X ("union (Set, Set)", union), + }, +} + +return Set diff --git a/lib/std/strbuf.lua b/lib/std/strbuf.lua new file mode 100644 index 0000000..d23af9a --- /dev/null +++ b/lib/std/strbuf.lua @@ -0,0 +1,130 @@ +--[[-- + String buffers. + + Buffers are mutable by default, but being based on objects, they can + also be used in a functional style: + + local StrBuf = require "std.strbuf" {} + local a = StrBuf {"a"} + local b = a:concat "b" -- mutate *a* + print (a, b) --> ab ab + local c = a {} .. "c" -- copy and append + print (a, c) --> ab abc + + Prototype Chain + --------------- + + table + `-> Object + `-> StrBuf + + @classmod std.strbuf +]] + +local base = require "std.base" +local debug = require "std.debug" + +local Object = require "std.object" {} + +local ielems, insert, prototype = base.ielems, base.insert, base.prototype + +local M, StrBuf + + +local function __concat (self, x) + return insert (self, x) +end + + +local function __tostring (self) + local strs = {} + for e in ielems (self) do strs[#strs + 1] = tostring (e) end + return table.concat (strs) +end + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X (decl, fn) + return debug.argscheck ("std.strbuf." .. decl, fn) +end + + +M = { + --- Add a object to a buffer. + -- Elements are stringified lazily, so if add a table and then change + -- its contents, the contents of the buffer will be affected too. + -- @static + -- @function concat + -- @param x object to add to buffer + -- @treturn StrBuf modified buffer + -- @usage + -- buf = buf:concat "append this" {" and", " this"} + concat = X ("concat (StrBuf, any)", __concat), +} + + + +--[[ ============= ]]-- +--[[ Deprecations. ]]-- +--[[ ============= ]]-- + + +local DEPRECATED = debug.DEPRECATED + +M.tostring = DEPRECATED ("41.1", "std.strbuf.tostring", + "use 'tostring (strbuf)' instead", + X ("tostring (StrBuf)", __tostring)) + + +--[[ ================== ]]-- +--[[ Type Declarations. ]]-- +--[[ ================== ]]-- + + +--- StrBuf prototype object. +-- +-- Set also inherits all the fields and methods from +-- @{std.object.Object}. +-- @object StrBuf +-- @string[opt="StrBuf"] _type object name +-- @see std.object.__call +-- @usage +-- local std = require "std" +-- local StrBuf = std.strbuf {} +-- local a = {1, 2, 3} +-- local b = {a, "five", "six"} +-- a = a .. 4 +-- b = b:concat "seven" +-- print (a, b) --> 1234 1234fivesixseven +-- os.exit (0) +StrBuf = Object { + _type = "StrBuf", + + __index = M, + + --- Support concatenation to StrBuf objects. + -- @function __concat + -- @tparam StrBuf buffer object + -- @param x a string, or object that can be coerced to a string + -- @treturn StrBuf modified *buf* + -- @see concat + -- @usage + -- buf = buf .. x + __concat = __concat, + + --- Support fast conversion to Lua string. + -- @function __tostring + -- @tparam StrBuf buffer object + -- @treturn string concatenation of buffer contents + -- @see tostring + -- @usage + -- str = tostring (buf) + __tostring = __tostring, +} + + +return StrBuf diff --git a/lib/std/strict.lua b/lib/std/strict.lua new file mode 100644 index 0000000..6d7be04 --- /dev/null +++ b/lib/std/strict.lua @@ -0,0 +1,62 @@ +--[[-- + Checks uses of undeclared global variables. + + All global variables must be 'declared' through a regular + assignment (even assigning `nil` will do) in a top-level + chunk before being used anywhere or assigned to inside a function. + + To use this module, just require it near the start of your program. + + From Lua distribution (`etc/strict.lua`). + + @module std.strict +]] + +local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget + +local mt = getmetatable (_G) +if mt == nil then + mt = {} + setmetatable (_G, mt) +end + + +-- The set of globally declared variables. +mt.__declared = {} + + +--- What kind of variable declaration is this? +-- @treturn string "C", "Lua" or "main" +local function what () + local d = getinfo (3, "S") + return d and d.what or "C" +end + + +--- Detect assignment to undeclared global. +-- @function __newindex +-- @tparam table t `_G` +-- @string n name of the variable being declared +-- @param v initial value of the variable +mt.__newindex = function (t, n, v) + if not mt.__declared[n] then + local w = what () + if w ~= "main" and w ~= "C" then + error ("assignment to undeclared variable '" .. n .. "'", 2) + end + mt.__declared[n] = true + end + rawset (t, n, v) +end + + +--- Detect dereference of undeclared global. +-- @function __index +-- @tparam table t `_G` +-- @string n name of the variable being dereferenced +mt.__index = function (t, n) + if not mt.__declared[n] and what () ~= "C" then + error ("variable '" .. n .. "' is not declared", 2) + end + return rawget (t, n) +end diff --git a/lib/std/string.lua b/lib/std/string.lua new file mode 100644 index 0000000..ca0e151 --- /dev/null +++ b/lib/std/string.lua @@ -0,0 +1,552 @@ +--[[-- + 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" + + @module std.string +]] + +local base = require "std.base" +local debug = require "std.debug" + +local StrBuf = require "std.strbuf" {} + +local copy = base.copy +local getmetamethod = base.getmetamethod +local insert, len = base.insert, base.len +local pairs = base.pairs +local render = base.render + +local M + + + +local _tostring = base.tostring + +local function __concat (s, o) + return _tostring (s) .. _tostring (o) +end + + +local function __index (s, i) + if type (i) == "number" then + return s:sub (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 (s:find (...)) +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 monkey_patch (namespace) + namespace = namespace or _G + namespace.string = base.copy (namespace.string or {}, M) + + local string_metatable = getmetatable "" + string_metatable.__concat = M.__concat + string_metatable.__index = M.__index + + return M +end + + +local function caps (s) + return (s:gsub ("(%w)([%w]*)", function (l, ls) return l:upper () .. ls end)) +end + + +local function escape_shell (s) + return (s:gsub ("([ %(%)%\\%[%]\"'])", "\\%1")) +end + + +local function ordinal_suffix (n) + n = math.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 " ", math.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 = StrBuf { 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 + r:concat (s:sub (i, j)) + i = ni + if i < lens then + r:concat ("\n" .. string.rep (" ", ind)) + lstart = ind + end + end + return r:tostring () +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 = t:find(".(.%...)e(.+)") + local man, exp = tonumber (m), tonumber (e) + local siexp = math.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 + + +local function trim (s, r) + r = r or "%s+" + return (s:gsub ("^" .. r, ""):gsub (r .. "$", "")) +end + + +local function prettytostring (x, indent, spacing) + indent = indent or "\t" + spacing = spacing or "" + return render (x, + function () + local s = spacing .. "{" + spacing = spacing .. indent + return s + end, + function () + spacing = string.gsub (spacing, indent .. "$", "") + return spacing .. "}" + end, + function (x) + if type (x) == "string" then + return format ("%q", x) + else + return tostring (x) + end + end, + function (x, k, v, ks, vs) + local s = spacing + if type (k) ~= "string" or k:match "[^%w_]" then + s = s .. "[" + if type (k) == "table" then + s = s .. "\n" + end + s = s .. ks + 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 .. vs + return s + end, + function (_, k) + local s = "\n" + if k then + s = "," .. s + end + return s + end) +end + + +local function pickle (x) + if type (x) == "string" then + return format ("%q", x) + elseif type (x) == "number" or type (x) == "boolean" or + type (x) == "nil" then + return tostring (x) + else + x = copy (x) or x + if type (x) == "table" then + local s, sep = "{", "" + for i, v in pairs (x) do + s = s .. sep .. "[" .. M.pickle (i) .. "]=" .. M.pickle (v) + sep = "," + end + s = s .. "}" + return s + else + die ("cannot pickle " .. tostring (x)) + end + end +end + + + +--[[ ================= ]]-- +--[[ Public Interface. ]]-- +--[[ ================= ]]-- + + +local function X (decl, fn) + return debug.argscheck ("std.string." .. decl, fn) +end + +M = { + --- String concatenation operation. + -- @string s initial string + -- @param o object to stringify and concatenate + -- @return s .. tostring (o) + -- @usage + -- local string = require "std.string".monkey_patch () + -- concatenated = "foo" .. {"bar"} + __concat = __concat, + + --- String subscript operation. + -- @string s string + -- @tparam int|string i index or method name + -- @return `s:sub (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, + + --- 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 (s:gsub ("\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 = inputstr:match (escape_pattern (literal)) + escape_pattern = X ("escape_pattern (string)", base.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 (s:gsub ("^" .. (r or "%s+"), "")) end), + + --- Overwrite core `string` methods with `std` enhanced versions. + -- + -- Also adds auto-stringification to `..` operator on core strings, and + -- integer indexing of strings with `[]` dereferencing. + -- @function monkey_patch + -- @tparam[opt=_G] table namespace where to install global functions + -- @treturn table the module table + -- @usage local string = require "std.string".monkey_patch () + monkey_patch = X ("monkey_patch (?table)", monkey_patch), + + --- 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), + + --- Convert a value to a string. + -- The string can be passed to `functional.eval` to retrieve the value. + -- @todo Make it work for recursive tables. + -- @param x object to pickle + -- @treturn string reversible string rendering of *x* + -- @see std.eval + -- @usage + -- function slow_identity (x) return functional.eval (pickle (x)) end + pickle = pickle, + + --- 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), + + --- Turn tables into strings with recursion detection. + -- N.B. Functions calling render should not recurse, or recursion + -- detection will not work. + -- @function render + -- @param x object to convert to string + -- @tparam opentablecb open open table rendering function + -- @tparam closetablecb close close table rendering function + -- @tparam elementcb elem element rendering function + -- @tparam paircb pair pair rendering function + -- @tparam separatorcb sep separator rendering function + -- @tparam[opt] table roots accumulates table references to detect recursion + -- @return string representation of *x* + -- @usage + -- function tostring (x) + -- return render (x, lambda '="{"', lambda '="}"', tostring, + -- lambda '=_4.."=".._5', lambda '= _4 and "," or ""', + -- lambda '=","') + -- end + render = X ("render (?any, func, func, func, func, func, ?table)", render), + + --- 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 (s:gsub ((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)", base.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), +} + + + +--[[ ============= ]]-- +--[[ Deprecations. ]]-- +--[[ ============= ]]-- + + +local DEPRECATED = debug.DEPRECATED + + +M.assert = DEPRECATED ("41", "'std.string.assert'", + "use 'std.assert' instead", base.assert) + + +M.require_version = DEPRECATED ("41", "'std.string.require_version'", + "use 'std.require' instead", base.require) + + +M.tostring = DEPRECATED ("41", "'std.string.tostring'", + "use 'std.tostring' instead", base.tostring) + + + +return base.merge (M, string) + + + +--- Types +-- @section Types + +--- Signature of @{render} open table callback. +-- @function opentablecb +-- @tparam table t table about to be rendered +-- @treturn string open table rendering +-- @see render +-- @usage function open (t) return "{" end + + +--- Signature of @{render} close table callback. +-- @function closetablecb +-- @tparam table t table just rendered +-- @treturn string close table rendering +-- @see render +-- @usage function close (t) return "}" end + + +--- Signature of @{render} element callback. +-- @function elementcb +-- @param x element to render +-- @treturn string element rendering +-- @see render +-- @usage function element (e) return require "std".tostring (e) end + + +--- Signature of @{render} pair callback. +-- Trying to re-render *key* or *value* here will break recursion +-- detection, use *strkey* and *strvalue* pre-rendered values instead. +-- @function paircb +-- @tparam table t table containing pair being rendered +-- @param key key part of key being rendered +-- @param value value part of key being rendered +-- @string keystr prerendered *key* +-- @string valuestr prerendered *value* +-- @treturn string pair rendering +-- @see render +-- @usage +-- function pair (_, _, _, key, value) return key .. "=" .. value end + + +--- Signature of @{render} separator callback. +-- @function separatorcb +-- @tparam table t table currently being rendered +-- @param pk *t* key preceding separator, or `nil` for first key +-- @param pv *t* value preceding separator, or `nil` for first value +-- @param fk *t* key following separator, or `nil` for last key +-- @param fv *t* value following separator, or `nil` for last value +-- @treturn string separator rendering +-- @usage +-- function separator (_, _, _, fk) return fk and "," or "" end diff --git a/lib/std/table.lua b/lib/std/table.lua new file mode 100644 index 0000000..e12d02e --- /dev/null +++ b/lib/std/table.lua @@ -0,0 +1,529 @@ +--[[-- + 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" + + @module std.table +]] + + +local core = _G.table + +local base = require "std.base" +local debug = require "std.debug" + +local argerror = debug.argerror +local collect = base.collect +local leaves = base.leaves +local ipairs, pairs = base.ipairs, base.pairs +local len = base.len + + +local M, monkeys + + +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 function flatten (t) + return collect (leaves, ipairs, 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 shape (dims, t) + t = flatten (t) + -- Check the shape and calculate the size of the zero, if any + local size = 1 + local zero + for i, v in ipairs (dims) do + if v == 0 then + if zero then -- bad shape: two zeros + return nil + else + zero = i + end + else + size = size * v + end + end + if zero then + dims[zero] = math.ceil (len (t) / size) + end + local function fill (i, d) + if d > len (dims) then + return t[i], i + 1 + else + local r = {} + for j = 1, dims[d] do + local e + e, i = fill (i, d + 1) + r[#r + 1] = e + end + return r, i + end + end + return (fill (1, 1)) +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 function monkey_patch (namespace) + namespace = namespace or _G + namespace.table = base.copy (namespace.table or {}, monkeys) + return M +end + + +local _remove = table.remove + +local function remove (t, pos) + local lent = len (t) + pos = pos or lent + if pos < math.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 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 debug.argscheck ("std.table." .. decl, fn) +end + +M = { + --- Make a shallow copy of a table, including any metatable. + -- + -- To make deep copies, use @{tree.clone}. + -- @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), + + --- Flatten a nested table into a list. + -- @function flatten + -- @tparam table t a table + -- @treturn table a list of all non-table elements of *t* + -- @usage + -- --> {1, 2, 3, 4, 5} + -- flatten {{1, {{2}, 3}, 4}, 5} + flatten = X ("flatten (table)", flatten), + + --- 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)", base.insert), + + --- 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)", base.invert), + + --- Make the list of keys in table. + -- @function keys + -- @tparam table t a table + -- @treturn table list of keys from *t* + -- @see okeys + -- @see values + -- @usage globals = keys (_G) + keys = X ("keys (table)", keys), + + --- Equivalent to `#` operation, but respecting `__len` even on Lua 5.1. + -- @function len + -- @tparam table t a table + -- @treturn int length of list part of *t* + -- @usage for i = 1, len (t) do process (t[i]) end + len = X ("len (table)", base.len), + + --- 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)", base.maxn), + + --- Destructively merge another 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), + + --- Overwrite core `table` methods with `std` enhanced versions. + -- @function monkey_patch + -- @tparam[opt=_G] table namespace where to install global functions + -- @treturn table the module table + -- @usage local table = require "std.table".monkey_patch () + monkey_patch = X ("monkey_patch (?table)", monkey_patch), + + --- 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), + + --- Make an ordered list of keys in table. + -- @function okeys + -- @tparam table t a table + -- @treturn table ordered list of keys from *t* + -- @see keys + -- @usage globals = keys (_G) + okeys = X ("okeys (table)", base.okeys), + + --- Turn a tuple into a list. + -- @function pack + -- @param ... tuple + -- @return list + -- @usage + -- --> {1, 2, "ax"} + -- pack (("ax1"):find "(%D+)") + pack = function (...) return {...} end, + + --- 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), + + --- 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), + + --- Shape a table according to a list of dimensions. + -- + -- Dimensions are given outermost first and items from the original + -- list are distributed breadth first; there may be one 0 indicating + -- an indefinite number. Hence, `{0}` is a flat list, + -- `{1}` is a singleton, `{2, 0}` is a list of + -- two lists, and `{0, 2}` is a list of pairs. + -- + -- Algorithm: turn shape into all positive numbers, calculating + -- the zero if necessary and making sure there is at most one; + -- recursively walk the shape, adding empty tables until the bottom + -- level is reached at which point add table items instead, using a + -- counter to walk the flattened original list. + -- + -- @todo Use ileaves instead of flatten (needs a while instead of a + -- for in fill function) + -- @function shape + -- @tparam table dims table of dimensions `{d1, ..., dn}` + -- @tparam table t a table of elements + -- @return reshaped list + -- @usage + -- --> {{"a", "b"}, {"c", "d"}, {"e", "f"}} + -- shape ({3, 2}, {"a", "b", "c", "d", "e", "f"}) + shape = X ("shape (table, table)", shape), + + --- 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), + + --- 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 accordind to *c* + -- @usage table.concat (sort (object)) + sort = X ("sort (table, ?function)", sort), + + --- Enhance core *table.unpack* to always unpack up to `maxn (t)`. + -- @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)", base.unpack), + + --- 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), +} + + +monkeys = base.copy ({}, M) -- before deprecations and core merge + + +--[[ ============= ]]-- +--[[ Deprecations. ]]-- +--[[ ============= ]]-- + + +local DEPRECATED = debug.DEPRECATED + +M.clone_rename = DEPRECATED ("39", "'std.table.clone_rename'", + "use the new `map` argument to 'std.table.clone' instead", + function (map, t) + local r = merge_allfields ({}, t) + for i, v in pairs (map) do + r[v] = t[i] + r[i] = nil + end + return r + end) + + +M.metamethod = DEPRECATED ("41", "'std.table.metamethod'", + "use 'std.getmetamethod' instead", base.getmetamethod) + + +M.ripairs = DEPRECATED ("41", "'std.table.ripairs'", + "use 'std.ripairs' instead", base.ripairs) + + +M.totable = DEPRECATED ("41", "'std.table.totable'", + "use 'std.pairs' instead", + function (x) + local m = base.getmetamethod (x, "__totable") + if m then + return m (x) + elseif type (x) == "table" then + return x + elseif type (x) == "string" then + local t = {} + x:gsub (".", function (c) t[#t + 1] = c end) + return t + else + return nil + end + end) + + + +return base.merge (M, table) + + + +--- 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/lib/std/tree.lua b/lib/std/tree.lua new file mode 100644 index 0000000..2feaedc --- /dev/null +++ b/lib/std/tree.lua @@ -0,0 +1,280 @@ +--[[-- + Tree container prototype. + + Note that Functions listed below are only available from the Tree + prototype returned by requiring this module, because Container objects + cannot have object methods. + + Prototype Chain + --------------- + + table + `-> Object + `-> Container + `-> Tree + + @classmod std.tree + @see std.container +]] + +local base = require "std.base" +local operator = require "std.operator" + +local Container = require "std.container" {} + +local ielems, ipairs, leaves, pairs, prototype = + base.ielems, base.ipairs, base.leaves, base.pairs, base.prototype +local last, len = base.last, base.len +local reduce = base.reduce + +local Tree -- forward declaration + + + +--- Tree iterator. +-- @tparam function it iterator function +-- @tparam tree|table tr tree or tree-like table +-- @treturn string type ("leaf", "branch" (pre-order) or "join" (post-order)) +-- @treturn table path to node (`{i1, ...in}`) +-- @treturn node node +local function _nodes (it, tr) + local p = {} + local function visit (n) + if type (n) == "table" then + coroutine.yield ("branch", p, n) + for i, v in it (n) do + p[#p + 1] = i + visit (v) + table.remove (p) + end + coroutine.yield ("join", p, n) + else + coroutine.yield ("leaf", p, n) + end + end + return coroutine.wrap (visit), tr +end + + +local function clone (t, nometa) + local r = {} + if not nometa then + setmetatable (r, getmetatable (t)) + end + local d = {[t] = r} + local function copy (o, x) + for i, v in pairs (x) do + if type (v) == "table" then + if not d[v] then + d[v] = {} + if not nometa then + setmetatable (d[v], getmetatable (v)) + end + o[i] = copy (d[v], v) + else + o[i] = d[v] + end + else + o[i] = v + end + end + return o + end + return copy (r, t) +end + + +local function merge (t, u) + for ty, p, n in _nodes (pairs, u) do + if ty == "leaf" then + t[p] = n + end + end + return t +end + + + +--[[ ============ ]]-- +--[[ Tree Object. ]]-- +--[[ ============ ]]-- + + +local function X (decl, fn) + return require "std.debug".argscheck ("std.tree." .. decl, fn) +end + + +--- Tree prototype object. +-- @object Tree +-- @string[opt="Tree"] _type object name +-- @see std.container +-- @see std.object.__call +-- @usage +-- local std = require "std" +-- local Tree = std.tree {} +-- local tr = Tree {} +-- tr[{"branch1", 1}] = "leaf1" +-- tr[{"branch1", 2}] = "leaf2" +-- tr[{"branch2", 1}] = "leaf3" +-- print (tr[{"branch1"}]) --> Tree {leaf1, leaf2} +-- print (tr[{"branch1", 2}]) --> leaf2 +-- print (tr[{"branch1", 3}]) --> nil +-- --> leaf1 leaf2 leaf3 +-- for leaf in std.tree.leaves (tr) do +-- io.write (leaf .. "\t") +-- end +Tree = Container { + _type = "Tree", + + --- Deep retrieval. + -- @static + -- @function __index + -- @tparam Tree tr a tree + -- @param i non-table, or list of keys `{i1, ...i_n}` + -- @return `tr[i1]...[i_n]` if *i* is a key list, `tr[i]` otherwise + -- @todo the following doesn't treat list keys correctly + -- e.g. tr[{{1, 2}, {3, 4}}], maybe flatten first? + -- @usage + -- del_other_window = keymap[{"C-x", "4", KEY_DELETE}] + __index = function (tr, i) + if prototype (i) == "table" then + return reduce (operator.get, tr, ielems, i) + else + return rawget (tr, i) + end + end, + + --- Deep insertion. + -- @static + -- @function __newindex + -- @tparam Tree tr a tree + -- @param i non-table, or list of keys `{i1, ...i_n}` + -- @param[opt] v value + -- @usage + -- function bindkey (keylist, fn) keymap[keylist] = fn end + __newindex = function (tr, i, v) + if prototype (i) == "table" then + for n = 1, len (i) - 1 do + if prototype (tr[i[n]]) ~= "Tree" then + rawset (tr, i[n], Tree {}) + end + tr = tr[i[n]] + end + rawset (tr, last (i), v) + else + rawset (tr, i, v) + end + end, + + _functions = { + --- Make a deep copy of a tree, including any metatables. + -- @static + -- @function clone + -- @tparam table t tree or tree-like table + -- @tparam boolean nometa if non-`nil` don't copy metatables + -- @treturn Tree|table a deep copy of *tr* + -- @see std.table.clone + -- @usage + -- tr = {"one", {two=2}, {{"three"}, four=4}} + -- copy = clone (tr) + -- copy[2].two=5 + -- assert (tr[2].two == 2) + clone = X ("clone (table, ?boolean|:nometa)", clone), + + --- Tree iterator which returns just numbered leaves, in order. + -- @static + -- @function ileaves + -- @tparam Tree|table tr tree or tree-like table + -- @treturn function iterator function + -- @treturn Tree|table the tree *tr* + -- @see inodes + -- @see leaves + -- @usage + -- --> t = {"one", "three", "five"} + -- for leaf in ileaves {"one", {two=2}, {{"three"}, four=4}}, foo="bar", "five"} + -- do + -- t[#t + 1] = leaf + -- end + ileaves = X ("ileaves (table)", function (t) return leaves (ipairs, t) end), + + --- Tree iterator over numbered nodes, in order. + -- + -- The iterator function behaves like @{nodes}, but only traverses the + -- array part of the nodes of *tr*, ignoring any others. + -- @static + -- @function inodes + -- @tparam Tree|table tr tree or tree-like table to iterate over + -- @treturn function iterator function + -- @treturn tree|table the tree, *tr* + -- @see nodes + inodes = X ("inodes (table)", function (t) return _nodes (ipairs, t) end), + + --- Tree iterator which returns just leaves. + -- @static + -- @function leaves + -- @tparam table t tree or tree-like table + -- @treturn function iterator function + -- @treturn table *t* + -- @see ileaves + -- @see nodes + -- @usage + -- for leaf in leaves {"one", {two=2}, {{"three"}, four=4}}, foo="bar", "five"} + -- do + -- t[#t + 1] = leaf + -- end + -- --> t = {2, 4, "five", "foo", "one", "three"} + -- table.sort (t, lambda "=tostring(_1) < tostring(_2)") + leaves = X ("leaves (table)", function (t) return leaves (pairs, t) end), + + --- Destructively deep-merge one tree into another. + -- @static + -- @function merge + -- @tparam table t destination tree + -- @tparam table u table with nodes to merge + -- @treturn table *t* with nodes from *u* merged in + -- @see std.table.merge + -- @usage + -- merge (dest, {{exists=1}, {{not = {present = { inside = "dest" }}}}}) + merge = X ("merge (table, table)", merge), + + --- Tree iterator over all nodes. + -- + -- The returned iterator function performs a depth-first traversal of + -- `tr`, and at each node it returns `{node-type, tree-path, tree-node}` + -- where `node-type` is `branch`, `join` or `leaf`; `tree-path` is a + -- list of keys used to reach this node, and `tree-node` is the current + -- node. + -- + -- Note that the `tree-path` reuses the same table on each iteration, so + -- you must `table.clone` a copy if you want to take a snap-shot of the + -- current state of the `tree-path` list before the next iteration + -- changes it. + -- @static + -- @function nodes + -- @tparam Tree|table tr tree or tree-like table to iterate over + -- @treturn function iterator function + -- @treturn Tree|table the tree, *tr* + -- @see inodes + -- @usage + -- -- tree = +-- node1 + -- -- | +-- leaf1 + -- -- | '-- leaf2 + -- -- '-- leaf 3 + -- tree = Tree { Tree { "leaf1", "leaf2"}, "leaf3" } + -- for node_type, path, node in nodes (tree) do + -- print (node_type, path, node) + -- end + -- --> "branch" {} {{"leaf1", "leaf2"}, "leaf3"} + -- --> "branch" {1} {"leaf1", "leaf"2") + -- --> "leaf" {1,1} "leaf1" + -- --> "leaf" {1,2} "leaf2" + -- --> "join" {1} {"leaf1", "leaf2"} + -- --> "leaf" {2} "leaf3" + -- --> "join" {} {{"leaf1", "leaf2"}, "leaf3"} + -- os.exit (0) + nodes = X ("nodes (table)", function (t) return _nodes (pairs, t) end), + }, +} + +return Tree diff --git a/lib/std/version.lua b/lib/std/version.lua new file mode 100644 index 0000000..0dc347a --- /dev/null +++ b/lib/std/version.lua @@ -0,0 +1 @@ +return "General Lua libraries / 41.2.2" diff --git a/local.mk b/local.mk new file mode 100644 index 0000000..e3fdc97 --- /dev/null +++ b/local.mk @@ -0,0 +1,158 @@ +# Local Make rules. +# +# Copyright (C) 2013-2015 Gary V. Vaughan +# Written by Gary V. Vaughan, 2013 +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +## ------------ ## +## Environment. ## +## ------------ ## + +std_path = $(abs_srcdir)/lib/?.lua;$(abs_srcdir)/lib/?/init.lua +LUA_ENV = LUA_PATH="$(std_path);$(LUA_PATH)" + + +## ---------- ## +## Bootstrap. ## +## ---------- ## + +old_NEWS_hash = d41d8cd98f00b204e9800998ecf8427e + +update_copyright_env = \ + UPDATE_COPYRIGHT_HOLDER='(Gary V. Vaughan|Reuben Thomas)' \ + UPDATE_COPYRIGHT_USE_INTERVALS=1 \ + UPDATE_COPYRIGHT_FORCE=1 + + +## ------------- ## +## Declarations. ## +## ------------- ## + +classesdir = $(docdir)/classes +modulesdir = $(docdir)/modules + +dist_doc_DATA = +dist_classes_DATA = +dist_modules_DATA = + +include specs/specs.mk + + +## ------ ## +## Build. ## +## ------ ## + +dist_lua_DATA += \ + lib/std.lua \ + $(NOTHING_ELSE) + +luastddir = $(luadir)/std + +dist_luastd_DATA = \ + lib/std/base.lua \ + lib/std/container.lua \ + lib/std/debug.lua \ + lib/std/functional.lua \ + lib/std/io.lua \ + lib/std/list.lua \ + lib/std/math.lua \ + lib/std/object.lua \ + lib/std/operator.lua \ + lib/std/optparse.lua \ + lib/std/package.lua \ + lib/std/set.lua \ + lib/std/strbuf.lua \ + lib/std/strict.lua \ + lib/std/string.lua \ + lib/std/table.lua \ + lib/std/tree.lua \ + $(NOTHING_ELSE) + +# For bugwards compatibility with LuaRocks 2.1, while ensuring that +# `require "std.debug_init"` continues to work, we have to install +# the former `$(luadir)/std/debug_init.lua` to `debug_init/init.lua`. +# When LuaRocks works again, move this file back to dist_luastd_DATA +# above and rename to debug_init.lua. + +luastddebugdir = $(luastddir)/debug_init + +dist_luastddebug_DATA = \ + lib/std/debug_init/init.lua \ + $(NOTHING_ELSE) + +# In order to avoid regenerating std.lua at configure time, which +# causes the documentation to be rebuilt and hence requires users to +# have ldoc installed, put std/std.lua in as a Makefile dependency. +# (Strictly speaking, distributing an AC_CONFIG_FILE would be wrong.) +lib/std.lua: lib/std.lua.in + ./config.status --file=$@ + + +## Use a builtin rockspec build with root at $(srcdir)/lib, and note +## the github repository doesn't have the same name as the rockspec! +mkrockspecs_args = --module-dir $(srcdir)/lib --repository lua-stdlib + + +## ------------- ## +## Distribution. ## +## ------------- ## + +EXTRA_DIST += \ + build-aux/config.ld.in \ + lib/std.lua.in \ + $(NOTHING_ELSE) + + +## -------------- ## +## Documentation. ## +## -------------- ## + + +dist_doc_DATA += \ + $(srcdir)/doc/index.html \ + $(srcdir)/doc/ldoc.css + +dist_classes_DATA += \ + $(srcdir)/doc/classes/std.container.html \ + $(srcdir)/doc/classes/std.list.html \ + $(srcdir)/doc/classes/std.object.html \ + $(srcdir)/doc/classes/std.optparse.html \ + $(srcdir)/doc/classes/std.set.html \ + $(srcdir)/doc/classes/std.strbuf.html \ + $(srcdir)/doc/classes/std.tree.html \ + $(NOTHING_ELSE) + +dist_modules_DATA += \ + $(srcdir)/doc/modules/std.html \ + $(srcdir)/doc/modules/std.debug.html \ + $(srcdir)/doc/modules/std.functional.html \ + $(srcdir)/doc/modules/std.io.html \ + $(srcdir)/doc/modules/std.math.html \ + $(srcdir)/doc/modules/std.operator.html \ + $(srcdir)/doc/modules/std.package.html \ + $(srcdir)/doc/modules/std.strict.html \ + $(srcdir)/doc/modules/std.string.html \ + $(srcdir)/doc/modules/std.table.html \ + $(NOTHING_ELSE) + +## Parallel make gets confused when one command ($(LDOC)) produces +## multiple targets (all the html files above), so use the presence +## of the doc directory as a sentinel file. +$(dist_doc_DATA) $(dist_classes_DATA) $(dist_modules_DATA): $(srcdir)/doc + +$(srcdir)/doc: $(dist_lua_DATA) $(dist_luastd_DATA) + test -d $@ || mkdir $@ + $(LDOC) -c build-aux/config.ld -d $(abs_srcdir)/doc . diff --git a/luarocks-config.lua.in b/luarocks-config.lua.in deleted file mode 100644 index 412b3ed..0000000 --- a/luarocks-config.lua.in +++ /dev/null @@ -1,3 +0,0 @@ -rocks_trees = { - "@abs_srcdir@/luarocks" -} diff --git a/m4/ax_compare_version.m4 b/m4/ax_compare_version.m4 deleted file mode 100644 index 74dc0fd..0000000 --- a/m4/ax_compare_version.m4 +++ /dev/null @@ -1,177 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_compare_version.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) -# -# DESCRIPTION -# -# This macro compares two version strings. Due to the various number of -# minor-version numbers that can exist, and the fact that string -# comparisons are not compatible with numeric comparisons, this is not -# necessarily trivial to do in a autoconf script. This macro makes doing -# these comparisons easy. -# -# The six basic comparisons are available, as well as checking equality -# limited to a certain number of minor-version levels. -# -# The operator OP determines what type of comparison to do, and can be one -# of: -# -# eq - equal (test A == B) -# ne - not equal (test A != B) -# le - less than or equal (test A <= B) -# ge - greater than or equal (test A >= B) -# lt - less than (test A < B) -# gt - greater than (test A > B) -# -# Additionally, the eq and ne operator can have a number after it to limit -# the test to that number of minor versions. -# -# eq0 - equal up to the length of the shorter version -# ne0 - not equal up to the length of the shorter version -# eqN - equal up to N sub-version levels -# neN - not equal up to N sub-version levels -# -# When the condition is true, shell commands ACTION-IF-TRUE are run, -# otherwise shell commands ACTION-IF-FALSE are run. The environment -# variable 'ax_compare_version' is always set to either 'true' or 'false' -# as well. -# -# Examples: -# -# AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8]) -# AX_COMPARE_VERSION([3.15],[lt],[3.15.8]) -# -# would both be true. -# -# AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8]) -# AX_COMPARE_VERSION([3.15],[gt],[3.15.8]) -# -# would both be false. -# -# AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8]) -# -# would be true because it is only comparing two minor versions. -# -# AX_COMPARE_VERSION([3.15.7],[eq0],[3.15]) -# -# would be true because it is only comparing the lesser number of minor -# versions of the two values. -# -# Note: The characters that separate the version numbers do not matter. An -# empty string is the same as version 0. OP is evaluated by autoconf, not -# configure, so must be a string, not a variable. -# -# The author would like to acknowledge Guido Draheim whose advice about -# the m4_case and m4_ifvaln functions make this macro only include the -# portions necessary to perform the specific comparison specified by the -# OP argument in the final configure script. -# -# LICENSE -# -# Copyright (c) 2008 Tim Toolan -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 11 - -dnl ######################################################################### -AC_DEFUN([AX_COMPARE_VERSION], [ - AC_REQUIRE([AC_PROG_AWK]) - - # Used to indicate true or false condition - ax_compare_version=false - - # Convert the two version strings to be compared into a format that - # allows a simple string comparison. The end result is that a version - # string of the form 1.12.5-r617 will be converted to the form - # 0001001200050617. In other words, each number is zero padded to four - # digits, and non digits are removed. - AS_VAR_PUSHDEF([A],[ax_compare_version_A]) - A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ - -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ - -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ - -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ - -e 's/[[^0-9]]//g'` - - AS_VAR_PUSHDEF([B],[ax_compare_version_B]) - B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ - -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ - -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ - -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ - -e 's/[[^0-9]]//g'` - - dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary - dnl # then the first line is used to determine if the condition is true. - dnl # The sed right after the echo is to remove any indented white space. - m4_case(m4_tolower($2), - [lt],[ - ax_compare_version=`echo "x$A -x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"` - ], - [gt],[ - ax_compare_version=`echo "x$A -x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"` - ], - [le],[ - ax_compare_version=`echo "x$A -x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"` - ], - [ge],[ - ax_compare_version=`echo "x$A -x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"` - ],[ - dnl Split the operator from the subversion count if present. - m4_bmatch(m4_substr($2,2), - [0],[ - # A count of zero means use the length of the shorter version. - # Determine the number of characters in A and B. - ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'` - ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'` - - # Set A to no more than B's length and B to no more than A's length. - A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"` - B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"` - ], - [[0-9]+],[ - # A count greater than zero means use only that many subversions - A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` - B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` - ], - [.+],[ - AC_WARNING( - [illegal OP numeric parameter: $2]) - ],[]) - - # Pad zeros at end of numbers to make same length. - ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`" - B="$B`echo $A | sed 's/./0/g'`" - A="$ax_compare_version_tmp_A" - - # Check for equality or inequality as necessary. - m4_case(m4_tolower(m4_substr($2,0,2)), - [eq],[ - test "x$A" = "x$B" && ax_compare_version=true - ], - [ne],[ - test "x$A" != "x$B" && ax_compare_version=true - ],[ - AC_WARNING([illegal OP parameter: $2]) - ]) - ]) - - AS_VAR_POPDEF([A])dnl - AS_VAR_POPDEF([B])dnl - - dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE. - if test "$ax_compare_version" = "true" ; then - m4_ifvaln([$4],[$4],[:])dnl - m4_ifvaln([$5],[else $5])dnl - fi -]) dnl AX_COMPARE_VERSION diff --git a/m4/ax_lua.m4 b/m4/ax_lua.m4 index c5b54f6..8da0a07 100644 --- a/m4/ax_lua.m4 +++ b/m4/ax_lua.m4 @@ -1,11 +1,10 @@ # =========================================================================== -# ax_lua.m4 +# http://www.gnu.org/software/autoconf-archive/ax_lua.html # =========================================================================== # # SYNOPSIS # -# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], -# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] # AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] # AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] # AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] @@ -18,15 +17,14 @@ # # Also detect Lua headers and libraries. The Lua version contained in the # header is checked to match the Lua interpreter version exactly. When -# searching for Lua libraries, the version number is used as a suffix. This -# is done with the goal of supporting multiple Lua installs (5.1 and 5.2 -# side-by-side). +# searching for Lua libraries, the version number is used as a suffix. +# This is done with the goal of supporting multiple Lua installs (5.1 and +# 5.2 side-by-side). # -# -# *** A note on compatibility with previous versions: This file has been +# A note on compatibility with previous versions: This file has been # mostly rewritten for serial 18. Most developers should be able to use # these macros without needing to modify configure.ac. Care has been taken -# to preserve each macro's behaviour, but there are some differences: +# to preserve each macro's behavior, but there are some differences: # # 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as # AX_PROG_LUA with no arguments. @@ -39,27 +37,23 @@ # should instead specify the LUA precious variable on the command line. # See the AX_PROG_LUA description for details. # -# Please read the macro descriptions for more information. *** -# +# Please read the macro descriptions below for more information. # +# This file was inspired by Andrew Dalke's and James Henstridge's +# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4 +# (serial 17). Basically, this file is a mash-up of those two files. I +# like to think it combines the best of the two! # -# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], -# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] -# ------------------------------------------------------- -# -# Search for the Lua interpreter, and set up important Lua paths. -# Adds precious variable LUA, which may contain the path of the Lua +# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua +# paths. Adds precious variable LUA, which may contain the path of the Lua # interpreter. If LUA is blank, the user's path is searched for an # suitable interpreter. # -# If MINIMUM-VERSION is supplied, then only Lua interpreters with a version -# number greater or equal to MINIMUM-VERSION will be accepted. If TOO-BIG- -# VERSION is also supplied, then only Lua interpreters with a version -# number greater or equal to MINIMUM-VERSION and less than TOO-BIG-VERSION -# will be accepted. -# -# Version comparisions require the AX_COMPARE_VERSION macro, which is -# provided by ax_compare_version.m4 from the Autoconf Archive. +# If MINIMUM-VERSION is supplied, then only Lua interpreters with a +# version number greater or equal to MINIMUM-VERSION will be accepted. If +# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a +# version number greater or equal to MINIMUM-VERSION and less than +# TOO-BIG-VERSION will be accepted. # # The Lua version number, LUA_VERSION, is found from the interpreter, and # substituted. LUA_PLATFORM is also found, but not currently supported (no @@ -72,18 +66,19 @@ # luaexecdir Directory to install Lua modules. # pkgluaexecdir $luaexecdir/$PACKAGE # -# These paths a found based on $prefix, $exec_prefix, Lua's package.path, -# and package.cpath. The first path of package.path beginning with $prefix -# is selected as luadir. The first path of package.cpath beginning with -# $exec_prefix is used as luaexecdir. This should work on all reasonable -# Lua installations. If a path cannot be determined, a default path is -# used. Of course, the user can override these later when invoking make. +# These paths are found based on $prefix, $exec_prefix, Lua's +# package.path, and package.cpath. The first path of package.path +# beginning with $prefix is selected as luadir. The first path of +# package.cpath beginning with $exec_prefix is used as luaexecdir. This +# should work on all reasonable Lua installations. If a path cannot be +# determined, a default path is used. Of course, the user can override +# these later when invoking make. # # luadir Default: $prefix/share/lua/$LUA_VERSION # luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION # -# These directories can be used by Automake as install destinations. -# The variable name minus 'dir' needs to be used as a prefix to the +# These directories can be used by Automake as install destinations. The +# variable name minus 'dir' needs to be used as a prefix to the # appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES. # # If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is @@ -91,23 +86,20 @@ # FOUND is blank, then it will default to printing an error. To prevent # the default behavior, give ':' as an action. # +# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be +# expanded before this macro. Adds precious variable LUA_INCLUDE, which +# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If +# LUA_INCLUDE is blank, then this macro will attempt to find suitable +# flags. # -# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] -# ---------------------------------------------------------- -# -# Search for Lua headers. Requires that AX_PROG_LUA be expanded before this -# macro. Adds precious variable LUA_INCLUDE, which may contain Lua specific -# include flags, e.g. -I/usr/include/lua5.1. If LUA_INCLUDE is blank, then -# this macro will attempt to find suitable flags. -# -# LUA_INCLUDE can be used by Automake to compile Lua modules or executables -# with embedded interpreters. The *_CPPFLAGS variables should be used for -# this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE). +# LUA_INCLUDE can be used by Automake to compile Lua modules or +# executables with embedded interpreters. The *_CPPFLAGS variables should +# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE). # # This macro searches for the header lua.h (and others). The search is # performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE. -# If the search is unsuccessful, then some common directories are tried. If -# the headers are then found, then LUA_INCLUDE is set accordingly. +# If the search is unsuccessful, then some common directories are tried. +# If the headers are then found, then LUA_INCLUDE is set accordingly. # # The paths automatically searched are: # @@ -115,6 +107,7 @@ # * /usr/include/lua/X.Y # * /usr/include/luaXY # * /usr/local/include/luaX.Y +# * /usr/local/include/lua-X.Y # * /usr/local/include/lua/X.Y # * /usr/local/include/luaXY # @@ -125,18 +118,14 @@ # version numbers are not accepted. # # If headers are found, then ACTION-IF-FOUND is performed, otherwise -# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, -# then it will default to printing an error. To prevent the default -# behavior, set the action to ':'. +# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then +# it will default to printing an error. To prevent the default behavior, +# set the action to ':'. # -# -# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] -# ------------------------------------------------------- -# -# Search for Lua libraries. Requires that AX_PROG_LUA be expanded before -# this macro. Adds precious variable LUA_LIB, which may contain Lua specific -# linker flags, e.g. -llua5.1. If LUA_LIB is blank, then this macro will -# attempt to find suitable flags. +# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be +# expanded before this macro. Adds precious variable LUA_LIB, which may +# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank, +# then this macro will attempt to find suitable flags. # # LUA_LIB can be used by Automake to link Lua modules or executables with # embedded interpreters. The *_LIBADD and *_LDADD variables should be used @@ -150,25 +139,21 @@ # flags will be added to LUA_LIB. # # If libraries are found, then ACTION-IF-FOUND is performed, otherwise -# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, -# then it will default to printing an error. To prevent the default -# behavior, set the action to ':'. -# -# -# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] -# ----------------------------------------------------------- +# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then +# it will default to printing an error. To prevent the default behavior, +# set the action to ':'. # -# Search for readline headers and libraries. Requires the AX_LIB_READLINE -# macro, which is provided by ax_lib_readline.m4 from the Autoconf Archive. +# AX_LUA_READLINE: Search for readline headers and libraries. Requires the +# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the +# Autoconf Archive. # # If a readline compatible library is found, then ACTION-IF-FOUND is # performed, otherwise ACTION-IF-NOT-FOUND is performed. # -# # LICENSE # -# Copyright (C) 2013 Tim Perkins -# Copyright (C) 2013 Reuben Thomas +# Copyright (c) 2015 Reuben Thomas +# Copyright (c) 2014 Tim Perkins # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the @@ -195,16 +180,8 @@ # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -# -# THANKS -# -# This file was inspired by Andrew Dalke's and James Henstridge's python.m4 -# and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4 (serial -# 17). Basically, this file is a mashup of those two files. I like to think -# it combines the best of the two! - -#serial 18 +#serial 38 dnl ========================================================================= dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION], @@ -212,12 +189,16 @@ dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ========================================================================= AC_DEFUN([AX_PROG_LUA], [ + dnl Check for required tools. + AC_REQUIRE([AC_PROG_GREP]) + AC_REQUIRE([AC_PROG_SED]) + dnl Make LUA a precious variable. AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1]) dnl Find a Lua interpreter. m4_define_default([_AX_LUA_INTERPRETER_LIST], - [lua lua5.2 lua5.1 lua50]) + [lua lua5.2 lua52 lua5.1 lua51 lua50]) m4_if([$1], [], [ dnl No version check is needed. Find any Lua interpreter. @@ -225,18 +206,20 @@ AC_DEFUN([AX_PROG_LUA], [AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])]) ax_display_LUA='lua' - dnl At least check if this is a Lua interpreter. - AC_MSG_CHECKING([if $LUA is a Lua interprester]) - _AX_LUA_CHK_IS_INTRP([$LUA], - [AC_MSG_RESULT([yes])], - [ AC_MSG_RESULT([no]) - AC_MSG_ERROR([not a Lua interpreter]) + AS_IF([test "x$LUA" != 'x:'], + [ dnl At least check if this is a Lua interpreter. + AC_MSG_CHECKING([if $LUA is a Lua interpreter]) + _AX_LUA_CHK_IS_INTRP([$LUA], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([not a Lua interpreter]) + ]) ]) ], [ dnl A version check is needed. AS_IF([test "x$LUA" != 'x'], [ dnl Check if this is a Lua interpreter. - AC_MSG_CHECKING([if $LUA is a Lua interprester]) + AC_MSG_CHECKING([if $LUA is a Lua interpreter]) _AX_LUA_CHK_IS_INTRP([$LUA], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) @@ -278,21 +261,26 @@ AC_DEFUN([AX_PROG_LUA], m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])]) ], [ dnl Query Lua for its version number. - AC_CACHE_CHECK([for $ax_display_LUA version], [ax_cv_lua_version], - [ ax_cv_lua_version=`$LUA -e "print(_VERSION)" | \ - sed "s|^Lua \(.*\)|\1|" | \ - grep -o "^@<:@0-9@:>@\+\\.@<:@0-9@:>@\+"` + AC_CACHE_CHECK([for $ax_display_LUA version], + [ax_cv_lua_version], + [ dnl Get the interpreter version in X.Y format. This should work for + dnl interpreters version 5.0 and beyond. + ax_cv_lua_version=[`$LUA -e ' + -- return a version number in X.Y format + local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)") + print(ver)'`] ]) AS_IF([test "x$ax_cv_lua_version" = 'x'], [AC_MSG_ERROR([invalid Lua version number])]) AC_SUBST([LUA_VERSION], [$ax_cv_lua_version]) - AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | sed 's|\.||'`]) + AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`]) dnl The following check is not supported: dnl At times (like when building shared libraries) you may want to know dnl which OS platform Lua thinks this is. - AC_CACHE_CHECK([for $ax_display_LUA platform], [ax_cv_lua_platform], - [ax_cv_lua_platform=`$LUA -e "print('unknown')"`]) + AC_CACHE_CHECK([for $ax_display_LUA platform], + [ax_cv_lua_platform], + [ax_cv_lua_platform=[`$LUA -e 'print("unknown")'`]]) AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform]) dnl Use the values of $prefix and $exec_prefix for the corresponding @@ -317,12 +305,12 @@ AC_DEFUN([AX_PROG_LUA], ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION" dnl Try to find a path with the prefix. - _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [package.path]) + _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [script]) AS_IF([test "x$ax_lua_prefixed_path" != 'x'], [ dnl Fix the prefix. - _ax_strip_prefix=`echo "$ax_lua_prefix" | sed 's|.|.|g'` + _ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'` ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \ - sed "s,^$_ax_strip_prefix,$LUA_PREFIX,"` + $SED "s|^$_ax_strip_prefix|$LUA_PREFIX|"` ]) ]) AC_SUBST([luadir], [$ax_cv_lua_luadir]) @@ -344,12 +332,12 @@ AC_DEFUN([AX_PROG_LUA], dnl Try to find a path with the prefix. _AX_LUA_FND_PRFX_PTH([$LUA], - [$ax_lua_exec_prefix], [package.cpathd]) + [$ax_lua_exec_prefix], [module]) AS_IF([test "x$ax_lua_prefixed_path" != 'x'], [ dnl Fix the prefix. - _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | sed 's|.|.|g'` + _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'` ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \ - sed "s,^$_ax_strip_prefix,$LUA_EXEC_PREFIX,"` + $SED "s|^$_ax_strip_prefix|$LUA_EXEC_PREFIX|"` ]) ]) AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir]) @@ -363,7 +351,7 @@ AC_DEFUN([AX_PROG_LUA], dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA. AC_DEFUN([AX_WITH_LUA], [ - AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA]]) + AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA instead]]) AX_PROG_LUA ]) @@ -373,8 +361,19 @@ dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) dnl ========================================================================= AC_DEFUN([_AX_LUA_CHK_IS_INTRP], [ - dnl Just print _VERSION because all Lua interpreters have this global. - AS_IF([$1 -e "print('Hello ' .. _VERSION .. '!')" &>/dev/null], + dnl A minimal Lua factorial to prove this is an interpreter. This should work + dnl for Lua interpreters version 5.0 and beyond. + _ax_lua_factorial=[`$1 2>/dev/null -e ' + -- a simple factorial + function fact (n) + if n == 0 then + return 1 + else + return n * fact(n-1) + end + end + print("fact(5) is " .. fact(5))'`] + AS_IF([test "$_ax_lua_factorial" = 'fact(5) is 120'], [$2], [$3]) ]) @@ -385,48 +384,70 @@ dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE]) dnl ========================================================================= AC_DEFUN([_AX_LUA_CHK_VER], [ - _ax_test_ver=`$1 -e "print(_VERSION)" 2>/dev/null | \ - sed "s|^Lua \(.*\)|\1|" | grep -o "^@<:@0-9@:>@\+\\.@<:@0-9@:>@\+"` - AS_IF([test "x$_ax_test_ver" = 'x'], - [_ax_test_ver='0']) - AX_COMPARE_VERSION([$_ax_test_ver], [ge], [$2]) - m4_if([$3], [], [], - [ AS_IF([$ax_compare_version], - [AX_COMPARE_VERSION([$_ax_test_ver], [lt], [$3])]) - ]) - AS_IF([$ax_compare_version], [$4], [$5]) + dnl Check that the Lua version is within the bounds. Only the major and minor + dnl version numbers are considered. This should work for Lua interpreters + dnl version 5.0 and beyond. + _ax_lua_good_version=[`$1 -e ' + -- a script to compare versions + function verstr2num(verstr) + local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)") + if majorver and minorver then + return tonumber(majorver) * 100 + tonumber(minorver) + end + end + local minver = verstr2num("$2") + local _, _, trimver = string.find(_VERSION, "^Lua (.*)") + local ver = verstr2num(trimver) + local maxver = verstr2num("$3") or 1e9 + if minver <= ver and ver < maxver then + print("yes") + else + print("no") + end'`] + AS_IF([test "x$_ax_lua_good_version" = "xyes"], + [$4], [$5]) ]) dnl ========================================================================= -dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, LUA-PATH-VARIABLE) +dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, SCRIPT-OR-MODULE-DIR) dnl ========================================================================= AC_DEFUN([_AX_LUA_FND_PRFX_PTH], [ - dnl Invokes the Lua interpreter PROG to print the path variable - dnl LUA-PATH-VARIABLE, usually package.path or package.cpath. Paths are - dnl then matched against PREFIX. The first path to begin with PREFIX is set - dnl to ax_lua_prefixed_path. - - ax_lua_prefixed_path='' - _ax_package_paths=`$1 -e 'print($3)' 2>/dev/null | sed 's|;|\n|g'` - dnl Try the paths in order, looking for the prefix. - for _ax_package_path in $_ax_package_paths; do - dnl Copy the path, up to the use of a Lua wildcard. - _ax_path_parts=`echo "$_ax_package_path" | sed 's|/|\n|g'` - _ax_reassembled='' - for _ax_path_part in $_ax_path_parts; do - echo "$_ax_path_part" | grep '\?' >/dev/null && break - _ax_reassembled="$_ax_reassembled/$_ax_path_part" - done - dnl Check the path against the prefix. - _ax_package_path=$_ax_reassembled - if echo "$_ax_package_path" | grep "^$2" >/dev/null; then - dnl Found it. - ax_lua_prefixed_path=$_ax_package_path - break - fi - done + dnl Get the script or module directory by querying the Lua interpreter, + dnl filtering on the given prefix, and selecting the shallowest path. If no + dnl path is found matching the prefix, the result will be an empty string. + dnl The third argument determines the type of search, it can be 'script' or + dnl 'module'. Supplying 'script' will perform the search with package.path + dnl and LUA_PATH, and supplying 'module' will search with package.cpath and + dnl LUA_CPATH. This is done for compatibility with Lua 5.0. + + ax_lua_prefixed_path=[`$1 -e ' + -- get the path based on search type + local searchtype = "$3" + local paths = "" + if searchtype == "script" then + paths = (package and package.path) or LUA_PATH + elseif searchtype == "module" then + paths = (package and package.cpath) or LUA_CPATH + end + -- search for the prefix + local prefix = "'$2'" + local minpath = "" + local mindepth = 1e9 + string.gsub(paths, "(@<:@^;@:>@+)", + function (path) + path = string.gsub(path, "%?.*$", "") + path = string.gsub(path, "/@<:@^/@:>@*$", "") + if string.find(path, prefix) then + local depth = string.len(string.gsub(path, "@<:@^/@:>@", "")) + if depth < mindepth then + minpath = path + mindepth = depth + end + end + end) + print(minpath)'`] ]) @@ -447,12 +468,14 @@ AC_DEFUN([AX_LUA_HEADERS], AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1]) dnl Some default directories to search. - LUA_SHORT_VERSION=`echo "$LUA_VERSION" | sed 's|\.||'` + LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'` m4_define_default([_AX_LUA_INCLUDE_LIST], [ /usr/include/lua$LUA_VERSION \ + /usr/include/lua-$LUA_VERSION \ /usr/include/lua/$LUA_VERSION \ /usr/include/lua$LUA_SHORT_VERSION \ /usr/local/include/lua$LUA_VERSION \ + /usr/local/include/lua-$LUA_VERSION \ /usr/local/include/lua/$LUA_VERSION \ /usr/local/include/lua$LUA_SHORT_VERSION \ ]) @@ -492,14 +515,17 @@ AC_DEFUN([AX_LUA_HEADERS], AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], [ dnl Make a program to print LUA_VERSION defined in the header. - dnl TODO This probably shouldn't be a runtime test. - - AC_CACHE_CHECK([for Lua header version], - [ax_cv_lua_header_version], - [ _ax_lua_saved_cppflags=$CPPFLAGS - CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" - AC_RUN_IFELSE( - [ AC_LANG_SOURCE([[ + dnl TODO It would be really nice if we could do this without compiling a + dnl program, then it would work when cross compiling. But I'm not sure how + dnl to do this reliably. For now, assume versions match when cross compiling. + + AS_IF([test "x$cross_compiling" != 'xyes'], + [ AC_CACHE_CHECK([for Lua header version], + [ax_cv_lua_header_version], + [ _ax_lua_saved_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" + AC_RUN_IFELSE( + [ AC_LANG_SOURCE([[ #include #include #include @@ -509,23 +535,26 @@ int main(int argc, char ** argv) exit(EXIT_SUCCESS); } ]]) + ], + [ ax_cv_lua_header_version=`./conftest$EXEEXT p | \ + $SED -n "s|^Lua \(@<:@0-9@:>@\{1,\}\.@<:@0-9@:>@\{1,\}\).\{0,\}|\1|p"` + ], + [ax_cv_lua_header_version='unknown']) + CPPFLAGS=$_ax_lua_saved_cppflags + ]) + + dnl Compare this to the previously found LUA_VERSION. + AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION]) + AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"], + [ AC_MSG_RESULT([yes]) + ax_header_version_match='yes' ], - [ ax_cv_lua_header_version=`./conftest$EXEEXT p | \ - sed "s|^Lua \(.*\)|\1|" | \ - grep -o "^@<:@0-9@:>@\+\\.@<:@0-9@:>@\+"` - ], - [ax_cv_lua_header_version='unknown']) - CPPFLAGS=$_ax_lua_saved_cppflags - ]) - - dnl Compare this to the previously found LUA_VERSION. - AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION]) - AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"], - [ AC_MSG_RESULT([yes]) - ax_header_version_match='yes' + [ AC_MSG_RESULT([no]) + ax_header_version_match='no' + ]) ], - [ AC_MSG_RESULT([no]) - ax_header_version_match='no' + [ AC_MSG_WARN([cross compiling so assuming header version number matches]) + ax_header_version_match='yes' ]) ]) @@ -542,7 +571,7 @@ int main(int argc, char ** argv) dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS. AC_DEFUN([AX_LUA_HEADERS_VERSION], [ - AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS]]) + AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS instead]]) ]) @@ -597,7 +626,13 @@ AC_DEFUN([AX_LUA_LIBS], dnl Try to find the Lua libs. _ax_lua_saved_libs=$LIBS LIBS="$LIBS $LUA_LIB" - AC_SEARCH_LIBS([lua_load], [lua$LUA_VERSION lua$LUA_SHORT_VERSION lua], + AC_SEARCH_LIBS([lua_load], + [ lua$LUA_VERSION \ + lua$LUA_SHORT_VERSION \ + lua-$LUA_VERSION \ + lua-$LUA_SHORT_VERSION \ + lua \ + ], [_ax_found_lua_libs='yes'], [_ax_found_lua_libs='no'], [$_ax_lua_extra_libs]) diff --git a/m4/ax_with_prog.m4 b/m4/ax_with_prog.m4 deleted file mode 100644 index f337c05..0000000 --- a/m4/ax_with_prog.m4 +++ /dev/null @@ -1,70 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_with_prog.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_WITH_PROG([VARIABLE],[program],[VALUE-IF-NOT-FOUND],[PATH]) -# -# DESCRIPTION -# -# Locates an installed program binary, placing the result in the precious -# variable VARIABLE. Accepts a present VARIABLE, then --with-program, and -# failing that searches for program in the given path (which defaults to -# the system path). If program is found, VARIABLE is set to the full path -# of the binary; if it is not found VARIABLE is set to VALUE-IF-NOT-FOUND -# if provided, unchanged otherwise. -# -# A typical example could be the following one: -# -# AX_WITH_PROG(PERL,perl) -# -# NOTE: This macro is based upon the original AX_WITH_PYTHON macro from -# Dustin J. Mitchell . -# -# LICENSE -# -# Copyright (c) 2008 Francesco Salvestrini -# Copyright (c) 2008 Dustin J. Mitchell -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 16 - -AC_DEFUN([AX_WITH_PROG],[ - AC_PREREQ([2.61]) - - pushdef([VARIABLE],$1) - pushdef([EXECUTABLE],$2) - pushdef([VALUE_IF_NOT_FOUND],$3) - pushdef([PATH_PROG],$4) - - AC_ARG_VAR(VARIABLE,Absolute path to EXECUTABLE executable) - - AS_IF(test -z "$VARIABLE",[ - AC_MSG_CHECKING(whether EXECUTABLE executable path has been provided) - AC_ARG_WITH(EXECUTABLE,AS_HELP_STRING([--with-EXECUTABLE=[[[PATH]]]],absolute path to EXECUTABLE executable), [ - AS_IF([test "$withval" != yes && test "$withval" != no],[ - VARIABLE="$withval" - AC_MSG_RESULT($VARIABLE) - ],[ - VARIABLE="" - AC_MSG_RESULT([no]) - AS_IF([test "$withval" != no], [ - AC_PATH_PROG([]VARIABLE[],[]EXECUTABLE[],[]VALUE_IF_NOT_FOUND[],[]PATH_PROG[]) - ]) - ]) - ],[ - AC_MSG_RESULT([no]) - AC_PATH_PROG([]VARIABLE[],[]EXECUTABLE[],[]VALUE_IF_NOT_FOUND[],[]PATH_PROG[]) - ]) - ]) - - popdef([PATH_PROG]) - popdef([VALUE_IF_NOT_FOUND]) - popdef([EXECUTABLE]) - popdef([VARIABLE]) -]) diff --git a/mkrockspecs.lua b/mkrockspecs.lua deleted file mode 100644 index ee3e80b..0000000 --- a/mkrockspecs.lua +++ /dev/null @@ -1,49 +0,0 @@ --- Generate rockspecs from a prototype with variants - -require "std" - -if select ("#", ...) < 2 then - io.stderr:write "Usage: mkrockspecs PACKAGE VERSION\n" - os.exit () -end - -package_name = select (1, ...) -version = select (2, ...) - -function format (x, indent) - indent = indent or "" - if type (x) == "table" then - local s = "{\n" - for i, v in pairs (x) do - if type (i) ~= "number" then - s = s..indent..i.." = "..format (v, indent.." ")..",\n" - end - end - for i, v in ipairs (x) do - s = s..indent..format (v, indent.." ")..",\n" - end - return s..indent:sub (1, -3).."}" - elseif type (x) == "string" then - return string.format ("%q", x) - else - return tostring (x) - end -end - -for f, spec in pairs (loadfile ("rockspecs.lua") ()) do - if f ~= "default" then - local specfile = package_name.."-"..(f ~= "" and f:lower ().."-" or "")..version.."-1.rockspec" - h = io.open (specfile, "w") - assert (h) - flavour = f -- a global, visible in loadfile - local specs = loadfile ("rockspecs.lua") () -- reload to get current flavour interpolated - local spec = tree.merge (tree.new (specs.default), tree.new (specs[f])) - local s = "" - for i, v in pairs (spec) do - s = s..i.." = "..format (v, " ").."\n" - end - h:write (s) - h:close () - os.execute ("luarocks lint " .. specfile) - end -end diff --git a/rockspec.conf b/rockspec.conf new file mode 100644 index 0000000..397ad26 --- /dev/null +++ b/rockspec.conf @@ -0,0 +1,16 @@ +# stdlib rockspec configuration. + +description: + homepage: http://lua-stdlib.github.io/lua-stdlib + license: MIT/X11 + summary: General Lua Libraries + detailed: + stdlib is a library of modules for common programming tasks, + including list, table and functional operations, objects, + pickling, pretty-printing and command-line option parsing. + +dependencies: +- lua >= 5.1, < 5.4 + +source: + url: git://github.com/lua-stdlib/lua-stdlib.git diff --git a/rockspecs.lua b/rockspecs.lua deleted file mode 100644 index 002dbf5..0000000 --- a/rockspecs.lua +++ /dev/null @@ -1,44 +0,0 @@ --- Rockspec data - --- Variables to be interpolated: --- --- flavour: regex library --- package --- version - -local version_dashed = version:gsub ("%.", "-") - -local default = { - package = package_name, - version = version.."-1", - source = { - url = "git://github.com/rrthomas/lua-stdlib.git", - }, - description = { - summary = "General Lua libraries", - detailed = [[ - stdlib is a library of modules for common programming tasks, - including list, table and functional operations, regexps, objects, - pickling, pretty-printing and getopt. - ]], - homepage = "http://github.com/rrthomas/lua-stdlib/", - license = "MIT/X11", - }, - dependencies = { - "lua >= 5.1", - }, - build = { - type = "command", - build_command = "LUA=$(LUA) CPPFLAGS=-I$(LUA_INCDIR) ./configure --prefix=$(PREFIX) --libdir=$(LIBDIR) --datadir=$(LUADIR) && make clean && make", - install_command = "make install", - copy_directories = {}, - }, -} - -if version ~= "git" then - default.source.branch = "release-v"..version_dashed -else - default.build.build_command = "autoreconf -i && " .. default.build.build_command -end - -return {default=default, [""]={}} diff --git a/specs/container_spec.yaml b/specs/container_spec.yaml new file mode 100644 index 0000000..8f1c333 --- /dev/null +++ b/specs/container_spec.yaml @@ -0,0 +1,120 @@ +before: + Container = require "std.container" {} + prototype = require "std.object".prototype + +specify std.container: +- context when required: + - context by name: + - it does not touch the global table: + expect (show_apis {added_to="_G", by="std.container"}). + to_equal {} + +- describe construction: + - context with table _init: + - it diagnoses missing arguments: | + expect (Container ()). + to_raise "bad argument #1 to 'Container' (table expected, got no value)" + - it diagnoses too many arguments: | + expect (Container ({}, false)). + to_raise "bad argument #2 to 'Container' (no more than 1 argument expected, got 2)" + - context with function _init: + - before: + Thing = Container { _type = "Thing", _init = function (obj) return obj end } + - it doesn't diagnose missing arguments: + expect (Thing ()).not_to_raise "any error" + - it doesn't diagnose too many args: + expect (Thing ({}, false)).not_to_raise "any error" + + - context from Container prototype: + - before: + things = Container {"foo", "bar", baz="quux"} + - it constructs a new container: + expect (things).not_to_be (Container) + expect (type (things)).to_be "table" + expect (prototype (things)).to_be "Container" + - it reuses the container metatable: + o, p = things {"o"}, things {"p"} + expect (getmetatable (o)).to_be (getmetatable (p)) + - it sets container fields from arguments: + o = Container {"foo", "bar", baz="quux"} + expect (o).to_equal (things) + - it serves as a prototype for new instances: + o = things {} + expect (prototype (o)).to_be "Container" + expect (o).to_copy (things) + expect (getmetatable (o)).to_be (getmetatable (things)) + - it separates '_' prefixed fields: + expect (Container {foo="bar", _baz="quux"}). + to_equal (Container {foo="bar"}) + - it puts '_' prefixed fields in a new metatable: + things = Container {foo="bar", _baz="quux"} + expect (getmetatable (things)).not_to_be (getmetatable (Container)) + expect (getmetatable (things)._baz).to_be "quux" + - context with module functions: + - before: + reduce = require "std.functional".reduce + elems = require "std".elems + functions = { + count = function (bag) + return reduce (function (r, k) return r + k end, 0, elems, bag) + end, + } + Bag = Container { + _type = "Bag", _functions = functions, count = functions.count, + } + - it does not propagate _functions: + things = Bag {} + expect (things.count).to_be (nil) + - it does not provide object methods: | + things = Bag {} + expect (things:count ()).to_raise.any_of { + "attempt to call method 'count'", + "attempt to call a nil value (method 'count'" + } + - it does retain module functions: + things = Bag { apples = 1, oranges = 3 } + expect (Bag.count (things)).to_be (4) + - it does allow elements named after module functions: + things = Bag { count = 1337 } + expect (Bag.count (things)).to_be (1337) + + +- describe field access: + - before: + things = Container {"foo", "bar", baz="quux"} + - context with bracket notation: + - it provides access to existing contents: + expect (things[1]).to_be "foo" + expect (things["baz"]).to_be "quux" + - it assigns new contents: + things["new"] = "value" + expect (things["new"]).to_be "value" + - context with dot notation: + - it provides access to existing contents: + expect (things.baz).to_be "quux" + - it assigns new contents: + things.new = "value" + expect (things.new).to_be "value" + + +- describe stringification: + - before: + things = Container {_type = "Derived", "one", "two", "three"} + - it returns a string: + expect (type (tostring (things))).to_be "string" + - it contains the type: + expect (tostring (Container {})).to_contain "Container" + expect (tostring (things)).to_contain (prototype (things)) + - it contains the ordered array part elements: + expect (tostring (things)).to_contain "one, two, three" + - it contains the ordered dictionary part elements: + expect (tostring (Container {one = true, two = true, three = true})). + to_contain "one=true, three=true, two=true" + expect (tostring (things {one = true, two = true, three = true})). + to_contain "one=true, three=true, two=true" + - it contains a ';' separator only when container has array and dictionary parts: + expect (tostring (things)).not_to_contain ";" + expect (tostring (Container {one = true, two = true, three = true})). + not_to_contain ";" + expect (tostring (things {one = true, two = true, three = true})). + to_contain ";" diff --git a/specs/debug_spec.yaml b/specs/debug_spec.yaml new file mode 100644 index 0000000..9a8757e --- /dev/null +++ b/specs/debug_spec.yaml @@ -0,0 +1,1224 @@ +before: | + base_module = "debug" + this_module = "std.debug" + global_table = "_G" + + extend_base = { "DEPRECATED", "DEPRECATIONMSG", "argcheck", "argerror", + "argscheck", "extramsg_mismatch", "extramsg_toomany", + "getfenv", "parsetypes", "resulterror", "setfenv", "say", + "toomanyargmsg", "typesplit", "trace", "_setdebug" } + + 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 DEPRECATED: + - before: | + function runscript (body, name, args) + return luaproc ( + "require '" .. this_module .. "'.DEPRECATED ('0', '" .. + (name or "runscript") .. "', function (...)" .. + " " .. body .. + " end) " .. + "('" .. table.concat (args or {}, "', '") .. "')" + ) + end + + f, badarg = init (M, this_module, "DEPRECATED") + + - it returns a function: + expect (type (f ("0", "deprecated", nop))).to_be "function" + expect (f ("0", "deprecated", nop)).not_to_be (nop) + - context with deprecated function: + - it executes the deprecated function: + expect (runscript 'error "oh noes!"').to_contain_error "oh noes!" + - it passes arguments to the deprecated function: + expect (runscript ("print (table.concat ({...}, ', '))", nil, + {"foo", "bar", "baz"})).to_output "foo, bar, baz\n" + - it returns deprecated function results: | + script = [[ + DEPRECATED = require "std.debug".DEPRECATED + fn = DEPRECATED ("0", "fn", function () return "foo", "bar", "baz" end) + print (fn ()) + ]] + expect (luaproc (script)).to_output "foo\tbar\tbaz\n" + - it writes a warning to stderr: + expect (runscript 'error "oh noes!"'). + to_match_error "deprecated.*, and will be removed" + - it writes the version string to stderr: + expect (runscript 'error "oh noes!"'). + to_contain_error "in release 0" + - it writes the call location to stderr: | + expect (runscript 'error "oh noes!"'). + to_match_error "^%S+:1: " + - context with _DEBUG: + - before: | + script = [[ + DEPRECATED = require "]] .. this_module .. [[".DEPRECATED + fn = DEPRECATED ("0", "fn", function () io.stderr:write "oh noes!\n" end) + fn () -- line 3 + fn () -- line 4 + ]] + - it warns every call by default: + expect (luaproc (script)).to_match_error "^%S+:3:.*deprecated" + expect (luaproc (script)).to_match_error "\n%S+:4:.*deprecated" + - it does not warn at all with _DEBUG set to false: + script = "_DEBUG = false " .. script + expect (luaproc (script)).not_to_match_error "%d:.*deprecated" + - it does not define the function with _DEBUG set to true: | + script = "_DEBUG = true " .. script + expect (luaproc (script)).to_contain_error.any_of { + ":3: attempt to call global 'fn'", + ":3: attempt to call a nil value (global 'fn')", + } + - it warns on every call with _DEBUG.deprecate unset: + script = "_DEBUG = {} " .. script + expect (luaproc (script)).to_match_error "^%S+:3:.*deprecated" + expect (luaproc (script)).to_match_error "\n%S+:4:.*deprecated" + - it does not warn at all with _DEBUG.deprecate set to false: + script = "_DEBUG = { deprecate = false } " .. script + expect (luaproc (script)).not_to_match_error "%d:.*deprecated" + - it warns on every call with _DEBUG.deprecate set to true: | + script = "_DEBUG = { deprecate = true } " .. script + expect (luaproc (script)).to_contain_error.any_of { + ":3: attempt to call global 'fn'", + ":3: attempt to call a nil value (global 'fn')", + } + + +- describe DEPRECATIONMSG: + - before: | + function mkscript (lvl) + return [[ + DEPRECATIONMSG = require "]] .. this_module .. [[".DEPRECATIONMSG + function fn () + io.stderr:write (DEPRECATIONMSG ("42", "spec file", ]] .. lvl .. [[)) + end + fn () -- line 5 + fn () -- line 6 + ]] + end + + f = M.DEPRECATIONMSG + + - it contains deprecating the release version: + expect (f ("41", "foo", 2)).to_contain "41" + - it contains the deprecation function name: + expect (f ("41", "some.module.fname", 2)).to_contain "some.module.fname" + - it appends an optional extra message: + expect (f ("41", "wuh", "ah boo", 2)).to_contain ", ah boo." + - it blames the given stack level: + expect (luaproc (mkscript (1))).to_match_error "^%S+:3:.*deprecated" + expect (luaproc (mkscript (2))).to_match_error "^%S+:5:.*deprecated" + + +- describe resulterror: + - before: | + function mkstack (level) + return string.format ([[ + _DEBUG = true -- line 1 + local debug = require "std.debug" -- line 2 + function ohnoes () -- line 3 + debug.resulterror ("ohnoes", 1, nil, %s) -- line 4 + end -- line 5 + function caller () -- line 6 + local r = ohnoes () -- line 7 + return "not a tail call" -- line 8 + end -- line 9 + caller () -- line 10 + ]], tostring (level)) + end + + f = M.resulterror + + - it blames the call site by default: | + expect (luaproc (mkstack ())).to_contain_error ":4: bad result" + - it honors optional call stack level reporting: | + expect (luaproc (mkstack (1))).to_contain_error ":4: bad result" + expect (luaproc (mkstack (2))).to_contain_error ":7: bad result" + - it reports the calling function name: + expect (f ('expect', 1)).to_raise "'expect'" + - it reports the argument number: | + expect (f ('expect', 12345)).to_raise "#12345" + - it reports extra message in parentheses: + expect (f ('expect', 1, "extramsg")).to_raise " (extramsg)" + + +- describe argerror: + - before: | + function mkstack (level) + return string.format ([[ + _DEBUG = true -- line 1 + local debug = require "std.debug" -- line 2 + function ohnoes () -- line 3 + debug.argerror ("ohnoes", 1, nil, %s) -- line 4 + end -- line 5 + function caller () -- line 6 + local r = ohnoes () -- line 7 + return "not a tail call" -- line 8 + end -- line 9 + caller () -- line 10 + ]], tostring (level)) + end + + f, badarg = init (M, this_module, "argerror") + + - it diagnoses missing arguments: + pending "Lua 5.1 support is dropped" + expect (f ()).to_raise (badarg (1, "string")) + expect (f "foo").to_raise (badarg (2, "int")) + - it diagnoses wrong argument types: + pending "Lua 5.1 support is dropped" + expect (f (false)).to_raise (badarg (1, "string", "boolean")) + expect (f ("foo", false)).to_raise (badarg (2, "int", "boolean")) + expect (f ("foo", 1, false)). + to_raise (badarg (3, "string or nil", "boolean")) + expect (f ("foo", 1, "bar", false)). + to_raise (badarg (4, "int or nil", "boolean")) + - it diagnoses too many arguments: + pending "Lua 5.1 support is dropped" + expect (f ("foo", 1, "bar", 2, false)).to_raise (badarg (5)) + + - it blames the call site by default: | + expect (luaproc (mkstack ())).to_contain_error ":4: bad argument" + - it honors optional call stack level reporting: | + expect (luaproc (mkstack (1))).to_contain_error ":4: bad argument" + expect (luaproc (mkstack (2))).to_contain_error ":7: bad argument" + - it reports the calling function name: + expect (f ('expect', 1)).to_raise "'expect'" + - it reports the argument number: | + expect (f ('expect', 12345)).to_raise "#12345" + - it reports extra message in parentheses: + expect (f ('expect', 1, "extramsg")).to_raise " (extramsg)" + + +- describe argcheck: + - before: | + Object = require 'std.object' + List = Object { _type = "List" } + Foo = Object { _type = "Foo" } + + function fn (...) return M.argcheck ('expect', 1, ...) end + + function mkstack (level, debugp) + return string.format ([[ + _DEBUG = %s -- line 1 + local debug = require "std.debug" -- line 2 + function ohnoes (t) -- line 3 + debug.argcheck ("ohnoes", 1, "table", t, %s) -- line 4 + end -- line 5 + function caller () -- line 6 + local r = ohnoes "not a table" -- line 7 + return "not a tail call" -- line 8 + end -- line 9 + caller () -- line 10 + ]], tostring (debugp), tostring (level)) + end + + f, badarg = init (M, this_module, "argcheck") + + - it diagnoses missing arguments: + pending "Lua 5.1 support is dropped" + expect (f ()).to_raise (badarg (1, "string")) + expect (f "foo").to_raise (badarg (2, "int")) + expect (f ("foo", 1)).to_raise (badarg (3, "string")) + - it diagnoses wrong argument types: + pending "Lua 5.1 support is dropped" + expect (f (false)).to_raise (badarg (1, "string", "boolean")) + expect (f ("foo", false)).to_raise (badarg (2, "int", "boolean")) + expect (f ("foo", 1, false)).to_raise (badarg (3, "string", "boolean")) + expect (f ("foo", 1, "bar", 2, false)). + to_raise (badarg (5, "int or nil", "boolean")) + - it diagnoses too many arguments: + pending "Lua 5.1 support is dropped" + expect (f ("foo", 1, "bar", 2, 3, false)).to_raise (badarg (6)) + + - it blames the calling function by default: | + expect (luaproc (mkstack ())).to_contain_error ":7: bad argument" + - it honors optional call stack level reporting: | + expect (luaproc (mkstack (1))).to_contain_error ":4: bad argument" + expect (luaproc (mkstack (2))).to_contain_error ":7: bad argument" + expect (luaproc (mkstack (3))).to_contain_error ":10: bad argument" + - it can be disabled by setting _DEBUG to false: + expect (luaproc (mkstack (nil, false))). + not_to_contain_error "bad argument" + - it can be disabled by setting _DEBUG.argcheck to false: + expect (luaproc (mkstack (nil, "{ argcheck = false }"))). + not_to_contain_error "bad argument" + - it is not disabled by setting _DEBUG.argcheck to true: + expect (luaproc (mkstack (nil, "{ argcheck = true }"))). + to_contain_error "bad argument" + - it is not disabled by leaving _DEBUG.argcheck unset: + expect (luaproc (mkstack (nil, "{}"))). + to_contain_error "bad argument" + + - context with primitives: + - it diagnoses missing types: + expect (fn ("bool", nil)).to_raise "boolean expected, got no value" + expect (fn ("boolean", nil)).to_raise "boolean expected, got no value" + expect (fn ("file", nil)).to_raise "FILE* expected, got no value" + expect (fn ("number", nil)).to_raise "number expected, got no value" + expect (fn ("string", nil)).to_raise "string expected, got no value" + expect (fn ("table", nil)).to_raise "table expected, got no value" + - it diagnoses mismatched types: + expect (fn ("bool", {0})).to_raise "boolean expected, got table" + expect (fn ("boolean", {0})).to_raise "boolean expected, got table" + expect (fn ("file", {0})).to_raise "FILE* expected, got table" + expect (fn ("number", {0})).to_raise "number expected, got table" + expect (fn ("string", {0})).to_raise "string expected, got table" + expect (fn ("table", false)).to_raise "table expected, got boolean" + - it matches types: + expect (fn ("bool", true)).not_to_raise "any error" + expect (fn ("boolean", true)).not_to_raise "any error" + expect (fn ("file", io.stderr)).not_to_raise "any error" + expect (fn ("number", 1)).not_to_raise "any error" + expect (fn ("string", "s")).not_to_raise "any error" + expect (fn ("table", {})).not_to_raise "any error" + expect (fn ("table", require "std.object")).not_to_raise "any error" + + - context with int: + - it diagnoses missing types: + expect (fn ("int", nil)).to_raise "int expected, got no value" + - it diagnoses mismatched types: + expect (fn ("int", false)).to_raise "int expected, got boolean" + expect (fn ("int", 1.234)).to_raise "int expected, got number" + expect (fn ("int", 1234e-3)).to_raise "int expected, got number" + - it matches types: + expect (fn ("int", 1)).not_to_raise "any error" + expect (fn ("int", 1.0)).not_to_raise "any error" + expect (fn ("int", 0x1234)).not_to_raise "any error" + expect (fn ("int", 1.234e3)).not_to_raise "any error" + - context with constant string: + - it diagnoses missing types: + expect (fn (":foo", nil)).to_raise ":foo expected, got no value" + - it diagnoses mismatched types: + expect (fn (":foo", false)).to_raise ":foo expected, got boolean" + expect (fn (":foo", ":bar")).to_raise ":foo expected, got :bar" + expect (fn (":foo", "foo")).to_raise ":foo expected, got string" + - it matches types: + expect (fn (":foo", ":foo")).not_to_raise "any error" + - context with callable types: + - it diagnoses missing types: + expect (fn ("func", nil)).to_raise "function expected, got no value" + expect (fn ("function", nil)).to_raise "function expected, got no value" + - it diagnoses mismatched types: + expect (fn ("func", {0})).to_raise "function expected, got table" + expect (fn ("function", {0})).to_raise "function expected, got table" + - it matches types: + expect (fn ("func", function () end)).not_to_raise "any error" + expect (fn ("func", setmetatable ({}, {__call = function () end}))). + not_to_raise "any error" + expect (fn ("function", function () end)).not_to_raise "any error" + expect (fn ("function", setmetatable ({}, {__call = function () end}))). + not_to_raise "any error" + - context with table of homogenous elements: + - it diagnoses missing types: + expect (fn ("table of boolean", nil)). + to_raise "table expected, got no value" + expect (fn ("table of booleans", nil)). + to_raise "table expected, got no value" + - it diagnoses mismatched types: + expect (fn ("table of file", io.stderr)). + to_raise "table expected, got file" + expect (fn ("table of files", io.stderr)). + to_raise "table expected, got file" + - it diagnoses mismatched element types: + expect (fn ("table of number", {false})). + to_raise "table of numbers expected, got boolean at index 1" + expect (fn ("table of numbers", {1, 2, "3"})). + to_raise "table of numbers expected, got string at index 3" + expect (fn ("table of numbers", {a=1, b=2, c="3"})). + to_raise "table of numbers expected, got string at index c" + - it matches types: + expect (fn ("table of string", {})).not_to_raise "any error" + expect (fn ("table of string", {"foo"})).not_to_raise "any error" + expect (fn ("table of string", {"f", "o", "o"})).not_to_raise "any error" + expect (fn ("table of string", {b="b", a="a", r="r"})).not_to_raise "any error" + - context with non-empty table types: + - it diagnoses missing types: + expect (fn ("#table", nil)). + to_raise "non-empty table expected, got no value" + - it diagnoses mismatched types: + expect (fn ("#table", false)). + to_raise "non-empty table expected, got boolean" + expect (fn ("#table", {})). + to_raise "non-empty table expected, got empty table" + - it matches types: + expect (fn ("#table", {0})).not_to_raise "any error" + - context with non-empty table of homogenous elements: + - it diagnoses missing types: + expect (fn ("#table of boolean", nil)). + to_raise "non-empty table expected, got no value" + expect (fn ("#table of booleans", nil)). + to_raise "non-empty table expected, got no value" + - it diagnoses mismatched types: + expect (fn ("#table of file", {})). + to_raise "non-empty table expected, got empty table" + expect (fn ("#table of file", io.stderr)). + to_raise "non-empty table expected, got file" + - it diagnoses mismatched element types: + expect (fn ("#table of number", {false})). + to_raise "non-empty table of numbers expected, got boolean at index 1" + expect (fn ("#table of numbers", {1, 2, "3"})). + to_raise "non-empty table of numbers expected, got string at index 3" + expect (fn ("#table of numbers", {a=1, b=2, c="3"})). + to_raise "non-empty table of numbers expected, got string at index c" + - it matches types: + expect (fn ("#table of string", {"foo"})).not_to_raise "any error" + expect (fn ("#table of string", {"f", "o", "o"})).not_to_raise "any error" + expect (fn ("#table of string", {b="b", a="a", r="r"})).not_to_raise "any error" + - context with list: + - it diagnonses missing types: + expect (fn ("list", nil)). + to_raise "list expected, got no value" + - it diagnoses mismatched types: + expect (fn ("list", false)). + to_raise "list expected, got boolean" + expect (fn ("list", {foo=1})). + to_raise "list expected, got table" + expect (fn ("list", Object)). + to_raise "list expected, got Object" + - it matches types: + expect (fn ("list", {})).not_to_raise "any error" + expect (fn ("list", {1})).not_to_raise "any error" + - context with list of homogenous elements: + - it diagnoses missing types: + expect (fn ("list of boolean", nil)). + to_raise "list expected, got no value" + expect (fn ("list of booleans", nil)). + to_raise "list expected, got no value" + - it diagnoses mismatched types: + expect (fn ("list of file", io.stderr)). + to_raise "list expected, got file" + expect (fn ("list of files", io.stderr)). + to_raise "list expected, got file" + expect (fn ("list of files", {file=io.stderr})). + to_raise "list expected, got table" + - it diagnoses mismatched element types: + expect (fn ("list of number", {false})). + to_raise "list of numbers expected, got boolean at index 1" + expect (fn ("list of numbers", {1, 2, "3"})). + to_raise "list of numbers expected, got string at index 3" + - it matches types: + expect (fn ("list of string", {})).not_to_raise "any error" + expect (fn ("list of string", {"foo"})).not_to_raise "any error" + expect (fn ("list of string", {"f", "o", "o"})).not_to_raise "any error" + - context with non-empty list: + - it diagnonses missing types: + expect (fn ("#list", nil)). + to_raise "non-empty list expected, got no value" + - it diagnoses mismatched types: + expect (fn ("#list", false)). + to_raise "non-empty list expected, got boolean" + expect (fn ("#list", {})). + to_raise "non-empty list expected, got empty list" + expect (fn ("#list", {foo=1})). + to_raise "non-empty list expected, got table" + expect (fn ("#list", Object)). + to_raise "non-empty list expected, got empty Object" + expect (fn ("#list", List {})). + to_raise "non-empty list expected, got empty List" + - it matches types: + expect (fn ("#list", {1})).not_to_raise "any error" + - context with non-empty list of homogenous elements: + - it diagnoses missing types: + expect (fn ("#list of boolean", nil)). + to_raise "non-empty list expected, got no value" + expect (fn ("#list of booleans", nil)). + to_raise "non-empty list expected, got no value" + - it diagnoses mismatched types: + expect (fn ("#list of file", {})). + to_raise "non-empty list expected, got empty table" + expect (fn ("#list of file", io.stderr)). + to_raise "non-empty list expected, got file" + expect (fn ("#list of files", {file=io.stderr})). + to_raise "non-empty list expected, got table" + - it diagnoses mismatched element types: + expect (fn ("#list of number", {false})). + to_raise "non-empty list of numbers expected, got boolean at index 1" + expect (fn ("#list of numbers", {1, 2, "3"})). + to_raise "non-empty list of numbers expected, got string at index 3" + - it matches types: + expect (fn ("#list of string", {"foo"})).not_to_raise "any error" + expect (fn ("#list of string", {"f", "o", "o"})).not_to_raise "any error" + - context with container: + - it diagnoses missing types: + expect (fn ("List of boolean", nil)). + to_raise "List expected, got no value" + expect (fn ("List of booleans", nil)). + to_raise "List expected, got no value" + - it diagnoses mismatched types: + expect (fn ("List of file", io.stderr)). + to_raise "List expected, got file" + expect (fn ("List of files", io.stderr)). + to_raise "List expected, got file" + expect (fn ("List of files", {file=io.stderr})). + to_raise "List expected, got table" + - it diagnoses mismatched element types: + expect (fn ("List of number", List {false})). + to_raise "List of numbers expected, got boolean at index 1" + expect (fn ("List of numbers", List {1, 2, "3"})). + to_raise "List of numbers expected, got string at index 3" + - it matches types: + expect (fn ("list of string", List {})).not_to_raise "any error" + expect (fn ("list of string", List {"foo"})).not_to_raise "any error" + expect (fn ("list of string", List {"f", "o", "o"})).not_to_raise "any error" + - context with object: + - it diagnoses missing types: + expect (fn ("object", nil)).to_raise "object expected, got no value" + expect (fn ("Object", nil)).to_raise "Object expected, got no value" + expect (fn ("Foo", nil)).to_raise "Foo expected, got no value" + expect (fn ("any", nil)).to_raise "any value expected, got no value" + - it diagnoses mismatched types: + expect (fn ("object", {0})).to_raise "object expected, got table" + expect (fn ("Object", {0})).to_raise "Object expected, got table" + expect (fn ("object", {_type="Object"})).to_raise "object expected, got table" + expect (fn ("Object", {_type="Object"})).to_raise "Object expected, got table" + expect (fn ("Object", Foo)).to_raise "Object expected, got Foo" + expect (fn ("Foo", {0})).to_raise "Foo expected, got table" + expect (fn ("Foo", Object)).to_raise "Foo expected, got Object" + - it matches types: + expect (fn ("object", Object)).not_to_raise "any error" + expect (fn ("object", Object {})).not_to_raise "any error" + expect (fn ("object", Foo)).not_to_raise "any error" + expect (fn ("object", Foo {})).not_to_raise "any error" + - it matches anything: + expect (fn ("any", true)).not_to_raise "any error" + expect (fn ("any", {})).not_to_raise "any error" + expect (fn ("any", Object)).not_to_raise "any error" + expect (fn ("any", Foo {})).not_to_raise "any error" + - context with a list of valid types: + - it diagnoses missing elements: + expect (fn ("string|table", nil)). + to_raise "string or table expected, got no value" + expect (fn ("string|list|#table", nil)). + to_raise "string, list or non-empty table expected, got no value" + expect (fn ("string|number|list|object", nil)). + to_raise "string, number, list or object expected, got no value" + - it diagnoses mismatched elements: + expect (fn ("string|table", false)). + to_raise "string or table expected, got boolean" + expect (fn ("string|#table", {})). + to_raise "string or non-empty table expected, got empty table" + expect (fn ("string|number|#list|object", {})). + to_raise "string, number, non-empty list or object expected, got empty table" + - it matches any type from a list: + expect (fn ("string|table", "foo")).not_to_raise "any error" + expect (fn ("string|table", {})).not_to_raise "any error" + expect (fn ("string|table", {0})).not_to_raise "any error" + expect (fn ("table|table", {})).not_to_raise "any error" + expect (fn ("#table|table", {})).not_to_raise "any error" + - context with an optional type element: + - it diagnoses mismatched elements: + expect (fn ("?boolean", "string")). + to_raise "boolean or nil expected, got string" + expect (fn ("?boolean|:symbol", {})). + to_raise "boolean, :symbol or nil expected, got empty table" + - it matches nil against a single type: + expect (fn ("?any", nil)).not_to_raise "any error" + expect (fn ("?boolean", nil)).not_to_raise "any error" + expect (fn ("?string", nil)).not_to_raise "any error" + - it matches nil against a list of types: + expect (fn ("?boolean|table", nil)).not_to_raise "any error" + expect (fn ("?string|table", nil)).not_to_raise "any error" + expect (fn ("?table|#table", nil)).not_to_raise "any error" + expect (fn ("?#table|table", nil)).not_to_raise "any error" + - it matches nil against a list of optional types: + expect (fn ("?boolean|?table", nil)).not_to_raise "any error" + expect (fn ("?string|?table", nil)).not_to_raise "any error" + expect (fn ("?table|?#table", nil)).not_to_raise "any error" + expect (fn ("?#table|?table", nil)).not_to_raise "any error" + - it matches any named type: + expect (fn ("?any", false)).not_to_raise "any error" + expect (fn ("?boolean", false)).not_to_raise "any error" + expect (fn ("?string", "string")).not_to_raise "any error" + - it matches any type from a list: + expect (fn ("?boolean|table", {})).not_to_raise "any error" + expect (fn ("?string|table", {0})).not_to_raise "any error" + expect (fn ("?table|#table", {})).not_to_raise "any error" + expect (fn ("?#table|table", {})).not_to_raise "any error" + - it matches any type from a list with several optional specifiers: + expect (fn ("?boolean|?table", {})).not_to_raise "any error" + expect (fn ("?string|?table", {0})).not_to_raise "any error" + expect (fn ("?table|?table", {})).not_to_raise "any error" + expect (fn ("?#table|?table", {})).not_to_raise "any error" + + +- describe debug: + - before: | + function mkwrap (k, v) + local fmt = "%s" + if type (v) == "string" then fmt = "%q" end + return k, string.format (fmt, require "std".tostring (v)) + end + + function mkdebug (debugp, ...) + return string.format ([[ + _DEBUG = %s + require "std.debug" (%s) + ]], + require "std".tostring (debugp), + table.concat (require "std.functional".map (mkwrap, {...}), ", ")) + end + + - it does nothing when _DEBUG is disabled: + expect (luaproc (mkdebug (false, "nothing to see here"))). + not_to_contain_error "nothing to see here" + - it writes to stderr when _DEBUG is not set: + expect (luaproc (mkdebug (nil, "debugging"))). + to_contain_error "debugging" + - it writes to stderr when _DEBUG is enabled: + expect (luaproc (mkdebug (true, "debugging"))). + to_contain_error "debugging" + - it writes to stderr when _DEBUG.level is not set: + expect (luaproc (mkdebug ({}, "debugging"))). + to_contain_error "debugging" + - it writes to stderr when _DEBUG.level is specified: + expect (luaproc (mkdebug ({level = 0}, "debugging"))). + to_contain_error "debugging" + expect (luaproc (mkdebug ({level = 1}, "debugging"))). + to_contain_error "debugging" + expect (luaproc (mkdebug ({level = 2}, "debugging"))). + to_contain_error "debugging" + + +- describe argscheck: + - before: | + function mkstack (name, spec) + return string.format ([[ + local argscheck = require "std.debug".argscheck -- line 1 + local function caller () -- line 2 + argscheck ("%s", function () end) -- line 3 + end -- line 4 + caller () -- line 5 + ]], tostring (name), tostring (spec)) + end + + f = M.argscheck + + mkmagic = function () return "MAGIC" end + wrapped = f ("inner ()", mkmagic) + + _, badarg, badresult = init (M, "", "inner") + id = function (...) return unpack {...} end + + - it returns the wrapped function: + expect (wrapped).not_to_be (inner) + expect (wrapped ()).to_be "MAGIC" + - it does not wrap the function when _ARGCHECK is disabled: | + script = [[ + _DEBUG = false + local debug = require "std.debug_init" + local argscheck = require "std.debug".argscheck + local function inner () return "MAGIC" end + local wrapped = argscheck ("inner (?any)", inner) + os.exit (wrapped == inner and 0 or 1) + ]] + expect (luaproc (script)).to_succeed () + + - context when checking zero argument function: + - it diagnoses too many arguments: + expect (wrapped (false)).to_raise (badarg (1)) + - it accepts correct argument types: + expect (wrapped ()).to_be "MAGIC" + + - context when checking single argument function: + - before: + wrapped = f ("inner (#table)", mkmagic) + - it diagnoses missing arguments: + expect (wrapped ()).to_raise (badarg (1, "non-empty table")) + - it diagnoses wrong argument types: + expect (wrapped {}).to_raise (badarg (1, "non-empty table", "empty table")) + - it diagnoses too many arguments: + expect (wrapped ({1}, 2, nop, "", false)).to_raise (badarg (1, 5)) + - it accepts correct argument types: + expect (wrapped ({1})).to_be "MAGIC" + + - context when checking multi-argument function: + - before: + wrapped = f ("inner (table, function)", mkmagic) + - it diagnoses missing arguments: + expect (wrapped ()).to_raise (badarg (1, "table")) + expect (wrapped ({})).to_raise (badarg (2, "function")) + - it diagnoses wrong argument types: + expect (wrapped (false)).to_raise (badarg (1, "table", "boolean")) + expect (wrapped ({}, false)).to_raise (badarg (2, "function", "boolean")) + - it diagnoses too many arguments: + expect (wrapped ({}, nop, false)).to_raise (badarg (3)) + - it accepts correct argument types: + expect (wrapped ({}, nop)).to_be "MAGIC" + + - context when checking nil argument function: + - before: + wrapped = f ("inner (?int, string)", mkmagic) + - it diagnoses wrong argument types: + expect (wrapped (false)).to_raise (badarg (1, "int or nil", "boolean")) + expect (wrapped (1, false)).to_raise (badarg (2, "string", "boolean")) + expect (wrapped (nil, false)).to_raise (badarg (2, "string", "boolean")) + - it diagnoses too many arguments: + expect (wrapped (1, "foo", nop)).to_raise (badarg (3)) + expect (wrapped (nil, "foo", nop)).to_raise (badarg (3)) + - it accepts correct argument types: + expect (wrapped (1, "foo")).to_be "MAGIC" + expect (wrapped (nil, "foo")).to_be "MAGIC" + + - context when checking optional multi-argument function: + - before: + wrapped = f ("inner ([int], string)", mkmagic) + - it diagnoses missing arguments: + expect (wrapped ()).to_raise (badarg (1, "int or string")) + expect (wrapped (1)).to_raise (badarg (2, "string")) + - it diagnoses wrong argument types: + expect (wrapped (false)).to_raise (badarg (1, "int or string", "boolean")) + - it diagnoses too many arguments: + expect (wrapped (1, "two", nop)).to_raise (badarg (3)) + - it accepts correct argument types: + expect (wrapped ("two")).to_be "MAGIC" + expect (wrapped (1, "two")).to_be "MAGIC" + + - context when checking final optional multi-argument function: + - before: + wrapped = f ("inner (?any, ?string, [any])", mkmagic) + - it diagnoses wrong argument types: + expect (wrapped (1, false)).to_raise (badarg (2, "string or nil", "boolean")) + expect (wrapped (nil, false)).to_raise (badarg (2, "string or nil", "boolean")) + - it diagnoses too many arguments: + expect (wrapped (1, "two", 3, false)).to_raise (badarg (4)) + expect (wrapped (nil, "two", 3, false)).to_raise (badarg (4)) + expect (wrapped (1, nil, 3, false)).to_raise (badarg (4)) + expect (wrapped (nil, nil, 3, false)).to_raise (badarg (4)) + - it accepts correct argument types: + expect (wrapped ()).to_be "MAGIC" + expect (wrapped (1)).to_be "MAGIC" + expect (wrapped (nil, "two")).to_be "MAGIC" + expect (wrapped (1, "two")).to_be "MAGIC" + expect (wrapped (nil, nil, 3)).to_be "MAGIC" + expect (wrapped (1, nil, 3)).to_be "MAGIC" + expect (wrapped (nil, "two", 3)).to_be "MAGIC" + expect (wrapped ("one", "two", 3)).to_be "MAGIC" + + - context when checking final ellipsis function: + - before: + wrapped = f ("inner (string, int...)", mkmagic) + - it diagnoses missing arguments: + expect (wrapped ()).to_raise (badarg (1, "string")) + expect (wrapped ("foo")).to_raise (badarg (2, "int")) + - it diagnoses wrong argument types: + expect (wrapped (false)).to_raise (badarg (1, "string", "boolean")) + expect (wrapped ("foo", false)).to_raise (badarg (2, "int", "boolean")) + expect (wrapped ("foo", 1, false)).to_raise (badarg (3, "int", "boolean")) + expect (wrapped ("foo", 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, false)). + to_raise (badarg (12, "int", "boolean")) + - it accepts correct argument types: + expect (wrapped ("foo", 1)).to_be "MAGIC" + expect (wrapped ("foo", 1, 2)).to_be "MAGIC" + expect (wrapped ("foo", 1, 2, 5)).to_be "MAGIC" + + - context when checking optional final parameter: + - context with single argument: + - before: + wrapped = f ("inner ([int])", mkmagic) + - it diagnoses wrong argument types: + expect (wrapped (false)).to_raise (badarg (1, "int", "boolean")) + - it diagnoses too many arguments: + expect (wrapped (1, nop)).to_raise (badarg (2)) + - it accepts correct argument types: + expect (wrapped ()).to_be "MAGIC" + expect (wrapped (1)).to_be "MAGIC" + - context with trailing ellipsis: + - before: + wrapped = f ("inner (string, [int]...)", mkmagic) + - it diagnoses missing arguments: + expect (wrapped ()).to_raise (badarg (1, "string")) + - it diagnoses wrong argument types: + expect (wrapped (false)).to_raise (badarg (1, "string", "boolean")) + expect (wrapped ("foo", false)).to_raise (badarg (2, "int", "boolean")) + expect (wrapped ("foo", 1, false)).to_raise (badarg (3, "int", "boolean")) + expect (wrapped ("foo", 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, false)). + to_raise (badarg (12, "int", "boolean")) + - it accepts correct argument types: + expect (wrapped ("foo")).to_be "MAGIC" + expect (wrapped ("foo", 1)).to_be "MAGIC" + expect (wrapped ("foo", 1, 2)).to_be "MAGIC" + expect (wrapped ("foo", 1, 2, 5)).to_be "MAGIC" + - context with inner ellipsis: + - before: + wrapped = f ("inner (string, [int...])", mkmagic) + - it diagnoses missing arguments: + expect (wrapped ()).to_raise (badarg (1, "string")) + - it diagnoses wrong argument types: + expect (wrapped (false)).to_raise (badarg (1, "string", "boolean")) + expect (wrapped ("foo", false)).to_raise (badarg (2, "int", "boolean")) + expect (wrapped ("foo", 1, false)).to_raise (badarg (3, "int", "boolean")) + expect (wrapped ("foo", 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, false)). + to_raise (badarg (12, "int", "boolean")) + - it accepts correct argument types: + expect (wrapped ("foo")).to_be "MAGIC" + expect (wrapped ("foo", 1)).to_be "MAGIC" + expect (wrapped ("foo", 1, 2)).to_be "MAGIC" + expect (wrapped ("foo", 1, 2, 5)).to_be "MAGIC" + + - context when omitting self type: + - before: + me = { + wrapped = f ("me:inner (string)", mkmagic) + } + _, badarg, badresult = init (M, "", "me:inner") + - it diagnoses missing arguments: + expect (me:wrapped ()).to_raise (badarg (1, "string")) + - it diagnoses wrong argument types: + expect (me:wrapped (false)).to_raise (badarg (1, "string", "boolean")) + - it diagnoses too many arguments: + expect (me:wrapped ("foo", false)).to_raise (badarg (2)) + - it accepts correct argument types: + expect (me:wrapped ("foo")).to_be "MAGIC" + + - context with too many args: + - before: + wrapped = f ("inner ([string], int)", mkmagic) + - it diagnoses missing arguments: + expect (wrapped ()).to_raise (badarg (1, "string or int")) + expect (wrapped ("one")).to_raise (badarg (2, "int")) + - it diagnoses wrong argument types: + expect (wrapped (false)).to_raise (badarg (1, "string or int", "boolean")) + expect (wrapped ("one", false)).to_raise (badarg (2, "int", "boolean")) + - it diagnoses too many arguments: + expect (wrapped ("one", 2, false)).to_raise (badarg (3)) + expect (wrapped (1, false)).to_raise (badarg (2)) + - it accepts correct argument types: + expect (wrapped (1)).to_be "MAGIC" + expect (wrapped ("one", 2)).to_be "MAGIC" + + - context when checking single return value function: + - before: | + wrapped = f ("inner (?any...) => #table", id) + - it diagnoses missing results: + expect (wrapped ()).to_raise (badresult (1, "non-empty table")) + - it diagnoses wrong result types: + expect (wrapped {}). + to_raise (badresult (1, "non-empty table", "empty table")) + - it diagnoses too many results: + expect (wrapped ({1}, 2, nop, "", false)).to_raise (badresult (1, 5)) + - it accepts correct results: + expect ({wrapped {1}}).to_equal {{1}} + + - context with variant single return value function: + - before: + wrapped = f ("inner (?any...) => int or nil", id) + - it diagnoses wrong result types: + expect (wrapped (false)).to_raise (badresult (1, "int or nil", "boolean")) + - it diagnoses too many results: + expect (wrapped (1, nop)).to_raise (badresult (2)) + - it accepts correct result types: + expect ({wrapped ()}).to_equal {} + expect ({wrapped (1)}).to_equal {1} + + - context when checking multi-return value function: + - before: + wrapped = f ("inner (?any...) => int, string", id) + - it diagnoses missing results: + expect (wrapped ()).to_raise (badresult (1, "int")) + expect (wrapped (1)).to_raise (badresult (2, "string")) + - it diagnoses wrong result types: + expect (wrapped (false)).to_raise (badresult (1, "int", "boolean")) + expect (wrapped (1, false)).to_raise (badresult (2, "string", "boolean")) + - it diagnoses too many results: + expect (wrapped (1, "two", false)).to_raise (badresult (3)) + - it accepts correct argument types: + expect ({wrapped (1, "two")}).to_equal {1, "two"} + + - context when checking nil return specifier: + - before: + wrapped = f ("inner (?any...) => ?int, string", id) + - it diagnoses wrong result types: + expect (wrapped (false)).to_raise (badresult (1, "int or nil", "boolean")) + expect (wrapped (1, false)).to_raise (badresult (2, "string", "boolean")) + expect (wrapped (nil, false)).to_raise (badresult (2, "string", "boolean")) + - it diagnoses too many results: + expect (wrapped (1, "foo", nop)).to_raise (badresult (3)) + expect (wrapped (nil, "foo", nop)).to_raise (badresult (3)) + - it accepts correct result types: + expect ({wrapped (1, "foo")}).to_equal {1, "foo"} + expect ({wrapped (nil, "foo")}).to_equal {[2] = "foo"} + + - context when checking variant multi-return value function: + - before: + wrapped = f ("inner (?any...) => int, string or string", id) + - it diagnoses missing results: + expect (wrapped ()).to_raise (badresult (1, "int or string")) + expect (wrapped (1)).to_raise (badresult (2, "string")) + - it diagnoses wrong result types: + expect (wrapped (false)).to_raise (badresult (1, "int or string", "boolean")) + - it diagnoses too many results: + expect (wrapped (1, "two", nop)).to_raise (badresult (3)) + - it accepts correct result types: + expect ({wrapped ("two")}).to_equal {"two"} + expect ({wrapped (1, "two")}).to_equal {1, "two"} + + - context when checking variant nil,errmsg pattern function: + - before: + wrapped = f ("inner (?any...) => int, string or nil, string", id) + - it diagnoses missing results: + expect (wrapped ()).to_raise (badresult (2, "string")) + expect (wrapped (1)).to_raise (badresult (2, "string")) + - it diagnoses wrong result types: + expect (wrapped (false)).to_raise (badresult (1, "int or nil", "boolean")) + expect (wrapped (1, false)).to_raise (badresult (2, "string", "boolean")) + - it diagnoses too many results: + expect (wrapped (1, "two", nop)).to_raise (badresult (3)) + expect (wrapped (nil, "errmsg", nop)).to_raise (badresult (3)) + - it accepts correct result types: + expect ({wrapped (1, "two")}).to_equal {1, "two"} + expect ({wrapped (nil, "errmsg")}).to_equal {[2] = "errmsg"} + + - context when checking optional multi-return value function: + - before: + wrapped = f ("inner (?any...) => [int], string", id) + - it diagnoses missing results: + expect (wrapped ()).to_raise (badresult (1, "int or string")) + expect (wrapped (1)).to_raise (badresult (2, "string")) + - it diagnoses wrong result types: + expect (wrapped (false)).to_raise (badresult (1, "int or string", "boolean")) + - it diagnoses too many results: + expect (wrapped (1, "two", nop)).to_raise (badresult (3)) + - it accepts correct result types: + expect ({wrapped ("two")}).to_equal {"two"} + expect ({wrapped (1, "two")}).to_equal {1, "two"} + + - context when checking final optional multi-return value function: + - before: + wrapped = f ("inner (?any...) => ?any, ?string, [any]", id) + - it diagnoses wrong result types: + expect (wrapped (1, false)).to_raise (badresult (2, "string or nil", "boolean")) + expect (wrapped (nil, false)).to_raise (badresult (2, "string or nil", "boolean")) + - it diagnoses too many results: + expect (wrapped (1, "two", 3, false)).to_raise (badresult (4)) + expect (wrapped (nil, "two", 3, false)).to_raise (badresult (4)) + expect (wrapped (1, nil, 3, false)).to_raise (badresult (4)) + expect (wrapped (nil, nil, 3, false)).to_raise (badresult (4)) + - it accepts correct result types: + expect ({wrapped ()}).to_equal {} + expect ({wrapped (1)}).to_equal {1} + expect ({wrapped (nil, "two")}).to_equal {[2]="two"} + expect ({wrapped (1, "two")}).to_equal {1, "two"} + expect ({wrapped (nil, nil, 3)}).to_equal {[3]=3} + expect ({wrapped (1, nil, 3)}).to_equal {1, [3]=3} + expect ({wrapped (nil, "two", 3)}).to_equal {[2]="two", [3]=3} + expect ({wrapped ("one", "two", 3)}).to_equal {"one", "two", 3} + + - context when checking optional final result: + - context with single result: + - before: + wrapped = f ("inner (?any...) => [int]", id) + - it diagnoses wrong result types: + expect (wrapped (false)).to_raise (badresult (1, "int", "boolean")) + - it diagnoses too many results: + expect (wrapped (1, nop)).to_raise (badresult (2)) + - it accepts correct result types: + expect ({wrapped ()}).to_equal {} + expect ({wrapped (1)}).to_equal {1} + - context with trailing ellipsis: + - before: + wrapped = f ("inner (?any...) => string, [int]...", id) + - it diagnoses missing results: + expect (wrapped ()).to_raise (badresult (1, "string")) + - it diagnoses wrong result types: + expect (wrapped (false)).to_raise (badresult (1, "string", "boolean")) + expect (wrapped ("foo", false)).to_raise (badresult (2, "int", "boolean")) + expect (wrapped ("foo", 1, false)).to_raise (badresult (3, "int", "boolean")) + expect (wrapped ("foo", 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, false)). + to_raise (badresult (12, "int", "boolean")) + - it accepts correct result types: + expect ({wrapped ("foo")}).to_equal {"foo"} + expect ({wrapped ("foo", 1)}).to_equal {"foo", 1} + expect ({wrapped ("foo", 1, 2)}).to_equal {"foo", 1, 2} + expect ({wrapped ("foo", 1, 2, 5)}).to_equal {"foo", 1, 2, 5} + - context with inner ellipsis: + - before: + wrapped = f ("inner (?any...) => string, [int...]", id) + - it diagnoses missing results: + expect (wrapped ()).to_raise (badresult (1, "string")) + - it diagnoses wrong result types: + expect (wrapped (false)).to_raise (badresult (1, "string", "boolean")) + expect (wrapped ("foo", false)).to_raise (badresult (2, "int", "boolean")) + expect (wrapped ("foo", 1, false)).to_raise (badresult (3, "int", "boolean")) + expect (wrapped ("foo", 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, false)). + to_raise (badresult (12, "int", "boolean")) + - it accepts correct result types: + expect ({wrapped ("foo")}).to_equal {"foo"} + expect ({wrapped ("foo", 1)}).to_equal {"foo", 1} + expect ({wrapped ("foo", 1, 2)}).to_equal {"foo", 1, 2} + expect ({wrapped ("foo", 1, 2, 5)}).to_equal {"foo", 1, 2, 5} + + - context with too many results: + - before: + wrapped = f ("inner (?any...) => [string], int", id) + - it diagnoses missing results: + expect (wrapped ()).to_raise (badresult (1, "string or int")) + expect (wrapped "one").to_raise (badresult (2, "int")) + - it diagnoses wrong result types: + expect (wrapped (false)). + to_raise (badresult (1, "string or int", "boolean")) + expect (wrapped ("one", false)). + to_raise (badresult (2, "int", "boolean")) + - it diagnoses too many results: + expect (wrapped ("one", 2, false)).to_raise (badresult (3)) + expect (wrapped (1, false)).to_raise (badresult (2)) + - it accepts correct argument types: + expect ({wrapped (1)}).to_equal {1} + expect ({wrapped ("one", 2)}).to_equal {"one", 2} + + +- describe extramsg_mismatch: + - before: + f = M.extramsg_mismatch + + - it returns the expected types: + expect (f "nil").to_contain "nil expected, " + expect (f "bool").to_contain "boolean expected, " + expect (f "?bool").to_contain "boolean or nil expected, " + expect (f "string|table").to_contain "string or table expected, " + - it returns expected container types: + expect (f ("table of int", nil, 1)).to_contain "table of ints expected, " + expect (f ("table of int|bool", nil, 1)). + to_contain "table of ints or booleans expected, " + expect (f ("table of int|bool|string", nil, 1)). + to_contain "table of ints, booleans or strings expected, " + expect (f ("table of int|bool|string|table", nil, 1)). + to_contain "table of ints, booleans, strings or tables expected, " + - it returns the actual type: + expect (f ("int")).to_contain ", got no value" + expect (f ("int", false)).to_contain ", got boolean" + expect (f ("int", {})).to_contain ", got empty table" + - it returns table field type: + expect (f ("table of int", nil, 1)).to_contain ", got no value at index 1" + expect (f ("table of int", "two", 2)).to_contain ", got string at index 2" + expect (f ("table of int|bool", "five", 3)).to_contain ", got string at index 3" + + +- describe extramsg_toomany: + - before: + f = M.extramsg_toomany + + - it returns the expected thing: + expect (f ("mojo", 1, 2)).to_contain "no more than 1 mojo" + - it uses singular thing when 1 is expected: + expect (f ("argument", 1, 2)).to_contain "no more than 1 argument" + - it uses plural thing otherwise: + expect (f ("thing", 0, 3)).to_contain "no more than 0 things" + expect (f ("result", 2, 3)).to_contain "no more than 2 results" + - it returns the actual count: + expect (f ("bad", 0, 1)).to_contain ", got 1" + expect (f ("bad", 99, 999)).to_contain ", got 999" + + +- context function environments: + - before: + env = { + tostring = function (x) return '"' .. tostring (x) .. '"' end, + } + fn = function (x) return tostring (x) end + ft = setmetatable ({}, { __call = function (_, ...) return fn (...) end }) + + - describe getfenv: + - before: + f = M.getfenv + - it returns a table: + expect (type (f (fn))).to_be "table" + - it gets a function execution environment: + M.setfenv (fn, env) + expect (f (fn)).to_be (env) + - it understands functables: + M.setfenv (ft, env) + expect (f (ft)).to_be (env) + + - describe setfenv: + - before: + f = M.setfenv + - it returns the passed function: + expect (f (fn, env)).to_be (fn) + - it sets a function execution environment: + f (fn, env) + expect (fn (42)).to_be '"42"' + - it understands functables: + f (ft, env) + expect (fn (5)).to_be '"5"' + + +- describe say: + - before: | + function mkwrap (k, v) + local fmt = "%s" + if type (v) == "string" then fmt = "%q" end + return k, string.format (fmt, require "std".tostring (v)) + end + + function mksay (debugp, ...) + return string.format ([[ + _DEBUG = %s + require "std.debug".say (%s) + ]], + require "std".tostring (debugp), + table.concat (require "std.functional".map (mkwrap, {...}), ", ")) + end + + f = M.say + + - it uses stdlib tostring: + expect (luaproc [[require "std.debug".say {"debugging"}]]). + to_contain_error (require "std".tostring {"debugging"}) + - context when _DEBUG is disabled: + - it does nothing when message level is not set: + expect (luaproc (mksay (false, "nothing to see here"))). + not_to_contain_error "nothing to see here" + - it does nothing when message is set: + expect (luaproc (mksay (false, -999, "nothing to see here"))). + not_to_contain_error "nothing to see here" + expect (luaproc (mksay (false, 0, "nothing to see here"))). + not_to_contain_error "nothing to see here" + expect (luaproc (mksay (false, 1, "nothing to see here"))). + not_to_contain_error "nothing to see here" + expect (luaproc (mksay (false, 2, "nothing to see here"))). + not_to_contain_error "nothing to see here" + expect (luaproc (mksay (false, 999, "nothing to see here"))). + not_to_contain_error "nothing to see here" + - context when _DEBUG is not set: + - it writes to stderr when message level is not set: + expect (luaproc (mksay (nil, "debugging"))). + to_contain_error "debugging" + - it writes to stderr when message level is 1 or lower: + expect (luaproc (mksay (nil, -999, "debugging"))). + to_contain_error "debugging" + expect (luaproc (mksay (nil, 0, "debugging"))). + to_contain_error "debugging" + expect (luaproc (mksay (nil, 1, "debugging"))). + to_contain_error "debugging" + - it does nothing when message level is 2 or higher: + expect (luaproc (mksay (nil, 2, "nothing to see here"))). + not_to_contain_error "nothing to see here" + expect (luaproc (mksay (nil, 999, "nothing to see here"))). + not_to_contain_error "nothing to see here" + - context when _DEBUG is enabled: + - it writes to stderr when message level is not set: + expect (luaproc (mksay (true, "debugging"))). + to_contain_error "debugging" + - it writes to stderr when message level is 1 or lower: + expect (luaproc (mksay (true, -999, "debugging"))). + to_contain_error "debugging" + expect (luaproc (mksay (true, 0, "debugging"))). + to_contain_error "debugging" + expect (luaproc (mksay (true, 1, "debugging"))). + to_contain_error "debugging" + - it does nothing when message level is 2 or higher: + expect (luaproc (mksay (true, 2, "nothing to see here"))). + not_to_contain_error "nothing to see here" + expect (luaproc (mksay (true, 999, "nothing to see here"))). + not_to_contain_error "nothing to see here" + - context when _DEBUG.level is not set: + - it writes to stderr when message level is not set: + expect (luaproc (mksay ({}, "debugging"))). + to_contain_error "debugging" + - it writes to stderr when message level is 1 or lower: + expect (luaproc (mksay ({}, -999, "debugging"))). + to_contain_error "debugging" + expect (luaproc (mksay ({}, 0, "debugging"))). + to_contain_error "debugging" + expect (luaproc (mksay ({}, 1, "debugging"))). + to_contain_error "debugging" + - it does nothing when message level is 2 or higher: + expect (luaproc (mksay ({}, 2, "nothing to see here"))). + not_to_contain_error "nothing to see here" + expect (luaproc (mksay ({}, 999, "nothing to see here"))). + not_to_contain_error "nothing to see here" + - context when _DEBUG.level is specified: + - it writes to stderr when message level is 1 or lower: + expect (luaproc (mksay ({level = 0}, "debugging"))). + to_contain_error "debugging" + expect (luaproc (mksay ({level = 1}, "debugging"))). + to_contain_error "debugging" + expect (luaproc (mksay ({level = 2}, "debugging"))). + to_contain_error "debugging" + - it does nothing when message level is higher than debug level: + expect (luaproc (mksay ({level = 2}, 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 (mksay ({level = 2}, 2, "debugging"))). + to_contain_error "debugging" + - it writes to stderr when message level is lower than debug level: + expect (luaproc (mksay ({level = 2}, 1, "debugging"))). + to_contain_error "debugging" + + +- describe trace: + - before: + f = init (M, this_module, "trace") + + - it does nothing when _DEBUG is disabled: + expect (luaproc [[ + _DEBUG = false + require "std.debug" + os.exit (0) + ]]).to_succeed_with "" + - it does nothing when _DEBUG is not set: + expect (luaproc [[ + require "std.debug" + os.exit (0) + ]]).to_succeed_with "" + - it does nothing when _DEBUG is enabled: + expect (luaproc [[ + _DEBUG = true + require "std.debug" + os.exit (0) + ]]).to_succeed_with "" + - it enables automatically when _DEBUG.call is set: | + expect (luaproc [[ + _DEBUG = {call = true} + local debug = 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/specs/functional_spec.yaml b/specs/functional_spec.yaml new file mode 100644 index 0000000..619c251 --- /dev/null +++ b/specs/functional_spec.yaml @@ -0,0 +1,764 @@ +before: + base = require "std.base" + + this_module = "std.functional" + global_table = "_G" + + exported_apis = { "bind", "callable", "case", "collect", "compose", + "cond", "curry", "eval", "filter", "fold", "foldl", + "foldr", "id", "lambda", "map", "map_with", + "memoize", "nop", "op", "reduce", "zip", "zip_with" } + + M = require (this_module) + + +specify std.functional: +- 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 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 via the std module: + - it does not touch the global table: + expect (show_apis {added_to=global_table, by="std"}). + to_equal {} + + +- describe bind: + - before: + op = require "std.operator" + + f = M.bind + + - it writes an argument passing deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {nop, M, "bind"})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {nop, M, "bind"})). + not_to_contain_error "was deprecated" + + - context with bad arguments: + badargs.diagnose (f, "std.functional.bind (function, ?any*)") + + - it does not affect normal operation if no arguments are bound: + expect (f (math.min, {}) (2, 3, 4)).to_be (2) + - it takes the extra arguments into account: + expect (f (math.min, {1, 0}) (2, 3, 4)).to_be (0) + - it appends final call arguments: + expect (f (math.max, {2, 3}) (4, 5, 1)).to_be (5) + - it does not require all arguments in final call: + div = function (a, b) return a / b end + expect (f (div, {100}) (25)).to_be (4) + - it supports out of order extra arguments: + expect (f (op.pow, {[2] = 3}) (2)).to_be (8) + - it propagates nil arguments correctly: + expect ({f (M.id, {[2]="b", [4]="d"}) (nil, 3, 5, 6, nil)}). + to_equal {nil, "b", 3, "d", 5, 6, nil} + - it supports the legacy api: + expect (f (math.min) (2, 3, 4)).to_be (2) + expect (f (math.min, 1, 0) (2, 3, 4)).to_be (0) + expect (f (op.pow, nil, 3) (2)).to_be (8) + + +- describe callable: + - before: + f = M.callable + + - context with bad arguments: + badargs.diagnose (f, "std.functional.callable (?any)") + + - it returns the function associated with a callable: + Container = require "std.container" { __call = M.nop } + for _, v in ipairs { + true, + 42, + "str", + io.stderr, + {}, + M.nop, + setmetatable ({}, {__call = M.nop}), + Container, + } do + expect (f (v)).to_be (pcall (v, {}) and M.nop or nil) + end + - it returns 'nil' for uncallable arguments: + expect (f ()).to_be (nil) + expect (f {}).to_be (nil) + expect (f "").to_be (nil) + +- describe case: + - before: + yes = function () return true end + no = function () return false end + default = function (s) return s end + branches = { yes = yes, no = no, default } + + f = M.case + + - context with bad arguments: | + badargs.diagnose (f, "std.functional.case (?any, #table)") + + - it matches against branch keys: + expect (f ("yes", branches)).to_be (true) + expect (f ("no", branches)).to_be (false) + - it has a default for unmatched keys: + expect (f ("none", branches)).to_be "none" + - it returns nil for unmatched keys with no default: + expect (f ("none", { yes = yes, no = no })).to_be (nil) + - it returns non-function matches: + expect (f ("t", {t = true})).to_be (true) + - it evaluates returned functions: + expect (f ("fn", {fn = function () return true end})). + to_be (true) + - it passes 'with' to function matches: + expect (f ("with", {function (s) return s end})).to_be "with" + - it evaluates returned functables: + functable = setmetatable ({}, {__call = function (t, with) return with end}) + expect (f ("functable", {functable})).to_be "functable" + - it evaluates 'with' exactly once: + s = "prince" + function acc () s = s .. "s"; return s end + expect (f (acc (), { + prince = function () return "one" end, + princes = function () return "many" end, + princess = function () return "one" end, + function () return "gibberish" end, + })).to_be "many" + + +- describe collect: + - before: + f = M.collect + + - context with bad arguments: + badargs.diagnose (f, "std.functional.collect ([func], any*)") + + - it collects a list of single return value iterator results: + expect (f (base.ielems, {"a", "b", "c"})).to_equal {"a", "b", "c"} + - it collects a table of key:value iterator results: + t = {"first", second="two", last=3} + expect (f (pairs, t)).to_equal (t) + - it propagates nil arguments correctly: + expect (f {"a", nil, nil, "d", "e"}).to_equal {"a", [4]="d", [5]="e"} + - it defaults to npairs iteration: + expect (f {1, 2, [5]=5, a="b", c="d"}).to_equal {1, 2, [5]=5} + + +- describe compose: + - before: + f = M.compose + + - context with bad arguments: + badargs.diagnose (f, "std.functional.compose (func*)") + + - it composes a single function correctly: + expect (f (M.id) (1)).to_be (1) + - it propagates nil arguments correctly: + expect ({f (M.id) (1, nil, nil, 4)}).to_equal {1, nil, nil, 4} + expect ({f (M.id, M.id) (1, nil, nil, 4)}).to_equal {1, nil, nil, 4} + - it composes functions in the correct order: + expect (f (math.sin, math.cos) (1)). + to_be (math.cos (math.sin (1))) + + +- describe cond: + - before: + yes = function () return true end + no = function () return false end + default = function (s) return s end + branches = { yes = yes, no = no, default } + + f = M.cond + + - it returns nil for no arguments: + expect (f ()).to_be (nil) + - it evaluates a single function argument: + expect (f (function () return true end)).to_be (true) + - it evaluates a single functable argument: + functable = setmetatable ({}, {__call = function () return true end}) + expect (f (functable)).to_be (true) + - it returns a non-callable single argument directly: + expect (f "foo").to_be "foo" + - it evaluates a branch function if expr is truthy: + expect (f ("truthy", function (s) return s end)).to_be "truthy" + - it returns nil if the last expr is falsey: + expect (f (nil, function (s) return "falsey" end)).to_be (nil) + expect (f (false, true, false, true)).to_be (nil) + - it recurses with remaining arguments if first argument is falsey: + expect (f (nil, true, 42, M.id)).to_be (42) + expect (f (nil, true, false, false, 42, M.id)).to_be (42) + + +- describe curry: + - before: + op = require "std.operator" + + f = M.curry + + - context with bad arguments: + badargs.diagnose (f, "std.functional.curry (func, int)") + + - it returns a zero argument function uncurried: + expect (f (f, 0)).to_be (f) + - it returns a one argument function uncurried: + expect (f (f, 1)).to_be (f) + - it curries a two argument function: + expect (f (f, 2)).not_to_be (f) + - it evaluates intermediate arguments one at a time: + expect (f (math.min, 3) (2) (3) (4)).to_equal (2) + - it returns a curried function that can be partially applied: + bin = f (op.pow, 2) (2) + expect (bin (2)).to_be (op.pow (2, 2)) + expect (bin (10)).to_be (op.pow (2, 10)) + + +- describe eval: + - before: + f = M.eval + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {"42"})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {"42"})).not_to_contain_error "was deprecated" + + - 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 filter: + - before: + elements = {"a", "b", "c", "d", "e"} + inverse = {a=1, b=2, c=3, d=4, e=5} + + f = M.filter + + - context with bad arguments: + badargs.diagnose (f, "std.functional.filter (func, [func], any*)") + + - it works with an empty table: + expect (f (M.id, pairs, {})).to_equal {} + - it iterates through element keys: + expect (f (M.id, base.ielems, elements)).to_equal {"a", "b", "c", "d", "e"} + expect (f (M.id, base.elems, inverse)).to_contain.a_permutation_of {1, 2, 3, 4, 5} + - it propagates nil arguments correctly: + t = {"a", nil, nil, "d", "e"} + expect (f (M.id, base.npairs, t)).to_equal (t) + - it passes all iteration result values to filter predicate: + t = {} + f (function (k, v) t[k] = v end, pairs, elements) + expect (t).to_equal (elements) + - it returns a list of filtered single return value iterator results: + expect (f (function (e) return e:match "[aeiou]" end, base.ielems, elements)). + to_equal {"a", "e"} + - it returns a table of filtered key:value iterator results: + t = {"first", second=2, last="three"} + expect (f (function (k, v) return type (v) == "string" end, pairs, t)). + to_equal {"first", last="three"} + expect (f (function (k, v) return k % 2 == 0 end, ipairs, elements)). + to_equal {[2]="b", [4]="d"} + - it defaults to pairs iteration: + t = {"first", second=2, last="three"} + expect (f (function (k, v) return type (v) == "string" end, t)). + to_equal {"first", last="three"} + + +- describe fold: + - before: + op = require "std.operator" + f = M.fold + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {M.id, 1, ipairs, {}})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {M.id, 1, ipairs, {}})). + not_to_contain_error "was deprecated" + + - it works with an empty table: + expect (f (op.sum, 2, ipairs, {})).to_be (2) + - it calls a binary function over single return value iterator results: + expect (f (op.sum, 2, base.ielems, {3})). + to_be (2 + 3) + expect (f (op.prod, 2, base.ielems, {3, 4})). + to_be (2 * 3 * 4) + - it calls a binary function over key:value iterator results: + expect (f (op.sum, 2, ipairs, {3})).to_be (2 + 3) + expect (f (op.prod, 2, ipairs, {3, 4})).to_be (2 * 3 * 4) + - it folds elements from left to right: + expect (f (op.pow, 2, ipairs, {3, 4})).to_be ((2 ^ 3) ^ 4) + + +- describe foldl: + - before: + op = require "std.operator" + f = M.foldl + + - context with bad arguments: + badargs.diagnose (f, "std.functional.foldl (func, [any], table)") + + - it works with an empty table: + expect (f (op.sum, 10000, {})).to_be (10000) + - it folds a binary function through a table: + expect (f (op.sum, 10000, {1, 10, 100})).to_be (10111) + - it folds from left to right: + expect (f (op.pow, 2, {3, 4})).to_be ((2 ^ 3) ^ 4) + - it supports eliding init argument: + expect (f (op.pow, {2, 3, 4})).to_be ((2 ^ 3) ^ 4) + + +- describe foldr: + - before: + op = require "std.operator" + f = M.foldr + + - context with bad arguments: + badargs.diagnose (f, "std.functional.foldr (func, [any], table)") + + - it works with an empty table: + expect (f (op.sum, 1, {})).to_be (1) + - it folds a binary function through a table: + expect (f (op.sum, {10000, 100, 10, 1})).to_be (10111) + - it folds from right to left: + expect (f (op.quot, 10, {10000, 100})).to_be (10000 / (100 / 10)) + - it supports eliding init argument: + expect (f (op.quot, {10000, 100, 10})).to_be (10000 / (100 / 10)) + + +- describe id: + - before: + f = M.id + - it returns argument unchanged: + expect (f (true)).to_be (true) + expect (f {1, 1, 2, 3}).to_equal {1, 1, 2, 3} + - it returns multiple arguments unchanged: + expect ({f (1, "two", false)}).to_equal {1, "two", false} + + +- describe lambda: + - before: + f = M.lambda + + - context with bad arguments: + badargs.diagnose (f, "std.functional.lambda (string)") + + examples {["it diagnoses bad lambda string"] = function () + expect (select (2, f "foo")).to_be "invalid lambda string 'foo'" + end} + examples {["it diagnoses an uncompilable expression"] = function () + expect (select (2, f "||+")).to_be "invalid lambda string '||+'" + expect (select (2, f "=")).to_be "invalid lambda string '='" + end} + + - context with argument format: + - it returns a function: + expect (prototype (f "|x| 1+x")).to_be "function" + - it compiles to a working Lua function: + fn = f "||42" + expect (fn ()).to_be (42) + - it propagates argument values: + fn = f "|...| {...}" + expect (fn (1,2,3)).to_equal {1,2,3} + - context with expression format: + - it returns a function: + expect (prototype (f "_")).to_be "function" + - it compiles to a working Lua function: + fn = f "=42" + expect (fn ()).to_be (42) + - it sets auto-argument values: + fn = f "_*_" + expect (fn (42)).to_be (1764) + - it sets numeric auto-argument values: + fn = f "_1+_2+_3" + expect (fn (1, 2, 5)).to_be (8) + + +- describe map: + - before: + elements = {"a", "b", "c", "d", "e"} + inverse = {a=1, b=2, c=3, d=4, e=5} + + f = M.map + + - context with bad arguments: + badargs.diagnose (f, "std.functional.map (func, [func], any*)") + + - it works with an empty table: + expect (f (M.id, ipairs, {})).to_equal {} + - it iterates through elements: + expect (f (M.id, ipairs, elements)).to_equal (elements) + expect (f (M.id, pairs, inverse)).to_contain.a_permutation_of (elements) + - it propagates nil arguments correctly: + t = {"a", nil, nil, "d", "e"} + expect (f (M.id, base.npairs, t)).to_equal (t) + t = {nil, nil, 3, 4} + expect (f (M.id, base.npairs, t)).to_equal (t) + - it passes all iteration result values to map function: + t = {} + f (function (k, v) t[k] = v end, pairs, elements) + expect (t).to_equal (elements) + - it returns a list of mapped single return value iterator results: + expect (f (function (e) return e:match "[aeiou]" end, base.ielems, elements)). + to_equal {"a", "e"} + expect (f (function (e) return e .. "x" end, base.elems, elements)). + to_contain.a_permutation_of {"ax", "bx", "cx", "dx", "ex"} + - it returns a table of mapped key:value iterator results: + t = {"first", second=2, last="three"} + expect (f (function (k, v) return type (v) == "string" end, pairs, t)). + to_contain.a_permutation_of {true, false, true} + expect (f (function (k, v) return k % 2 == 0 end, ipairs, elements)). + to_equal {false, true, false, true, false} + - it supports key:value results from mapping function: + expect (f (function (k, v) return v, k end, pairs, elements)). + to_equal (inverse) + - it defaults to pairs iteration: + t = {"first", second=2, last="three"} + expect (f (function (k, v) return type (v) == "string" end, t)). + to_contain.a_permutation_of {true, false, true} + + +- describe map_with: + - before: + t = {{1, 2, 3}, {4, 5}} + fn = function (...) return select ("#", ...) end + + f = M.map_with + + - context with bad arguments: + badargs.diagnose (f, "std.functional.map_with (func, table of tables)") + + - it works for an empty table: + expect (f (fn, {})).to_equal ({}) + - it returns a table: + u = f (fn, t) + expect (type (u)).to_be "table" + - it creates a new table: + old = t + u = f (fn, t) + expect (t).to_equal (old) + expect (u).not_to_equal (old) + expect (t).to_equal {{1, 2, 3}, {4, 5}} + - it maps a function over a list of argument lists: + expect (f (fn, t)).to_equal {3, 2} + - it discards hash-part arguments: + expect (f (fn, {{1,x=2,3}, {4,5,y="z"}})).to_equal {2, 2} + - it maps a function over a table of argument lists: + expect (f (fn, {a={1,2,3}, b={4,5}})).to_equal {a=3, b=2} + + +- describe memoize: + - before: + f = M.memoize + + memfn = f (function (x) + if x then return {x} else return nil, "bzzt" end + end) + + - context with bad arguments: + badargs.diagnose (f, "std.functional.memoize (func, ?func)") + + - it propagates multiple return values: + expect (select (2, memfn (false))).to_be "bzzt" + - it returns the same object for the same arguments: + t = memfn (1) + expect (memfn (1)).to_be (t) + - it returns a different object for different arguments: + expect (memfn (1)).not_to_be (memfn (2)) + - it returns the same object for table valued arguments: + t = memfn {1, 2, 3} + expect (memfn {1, 2, 3}).to_be (t) + t = memfn {foo = "bar", baz = "quux"} + expect (memfn {foo = "bar", baz = "quux"}).to_be (t) + expect (memfn {baz = "quux", foo = "bar"}).to_be (t) + - it returns a different object for different table arguments: + expect (memfn {1, 2, 3}).not_to_be (memfn {1, 2}) + expect (memfn {1, 2, 3}).not_to_be (memfn {3, 1, 2}) + expect (memfn {1, 2, 3}).not_to_be (memfn {1, 2, 3, 4}) + - it accepts alternative normalization function: + normalize = function (...) return select ("#", ...) end + memfn = f (function (x) return {x} end, normalize) + expect (memfn "same").to_be (memfn "not same") + expect (memfn (1, 2)).to_be (memfn (false, "x")) + expect (memfn "one").not_to_be (memfn ("one", "two")) + + +- describe nop: + - before: + f = M.nop + - it accepts any number of arguments: + expect (f ()).to_be (nil) + expect (f (false)).to_be (nil) + expect (f (1, 2, 3, nil, "str", {}, f)).to_be (nil) + - it returns no values: + expect (f (1, "two", false)).to_be (nil) + + +- describe op: + - context with []: + - before: + f = M.op["[]"] + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {{2}, 1})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {{2}, 1})). + not_to_contain_error "was deprecated" + + - it dereferences a table: + expect (f ({}, 1)).to_be (nil) + expect (f ({"foo", "bar"}, 1)).to_be "foo" + expect (f ({foo = "bar"}, "foo")).to_be "bar" + + - context with +: + - before: + f = M.op["+"] + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {2, 1})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {2, 1})). + not_to_contain_error "was deprecated" + + - it returns the sum of its arguments: + expect (f (99, 2)).to_be (99 + 2) + + - context with -: + - before: + f = M.op["-"] + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {2, 1})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {2, 1})). + not_to_contain_error "was deprecated" + + - it returns the difference of its arguments: + expect (f (99, 2)).to_be (99 - 2) + + - context with *: + - before: + f = M.op["*"] + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {2, 1})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {2, 1})). + not_to_contain_error "was deprecated" + + - it returns the product of its arguments: + expect (f (99, 2)).to_be (99 * 2) + + - context with /: + - before: + f = M.op["/"] + + - it writes a deprecation warning on: + setdebug { deprecate = "nil" } + expect (capture (f, {2, 1})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {2, 1})). + not_to_contain_error "was deprecated" + + - it returns the quotient of its arguments: + expect (f (99, 2)).to_be (99 / 2) + + - context with and: + - before: + f = M.op["and"] + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {true, false})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {true, false})). + not_to_contain_error "was deprecated" + + - it returns the logical and of its arguments: + expect (f (false, false)).to_be (false) + expect (f (false, true)).to_be (false) + expect (f (true, false)).to_be (false) + expect (f (true, true)).to_be (true) + - it supports truthy and falsey arguments: + expect (f ()).to_be (nil) + expect (f (0)).to_be (nil) + expect (f (nil, 0)).to_be (nil) + expect (f (0, "false")).to_be ("false") + + - context with or: + - before: + f = M.op["or"] + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {true, false})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {true, false})). + not_to_contain_error "was deprecated" + + - it returns the logical or of its arguments: + expect (f (false, false)).to_be (false) + expect (f (false, true)).to_be (true) + expect (f (true, false)).to_be (true) + expect (f (true, true)).to_be (true) + - it supports truthy and falsey arguments: + expect (f ()).to_be (nil) + expect (f (0)).to_be (0) + expect (f (nil, 0)).to_be (0) + expect (f (0, "false")).to_be (0) + + - context with not: + - before: + f = M.op["not"] + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {true})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {true})). + not_to_contain_error "was deprecated" + + - it returns the logical not of its argument: + expect (f (false)).to_be (true) + expect (f (true)).to_be (false) + - it supports truthy and falsey arguments: + expect (f ()).to_be (true) + expect (f (0)).to_be (false) + + - context with ==: + - before: + f = M.op["=="] + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {2, 1})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {2, 1})). + not_to_contain_error "was deprecated" + + - it returns true if the arguments are equal: + expect (f ()).to_be (true) + expect (f ("foo", "foo")).to_be (true) + - it returns false if the arguments are unequal: + expect (f (1)).to_be (false) + expect (f ("foo", "bar")).to_be (false) + + - context with ~=: + - before: + f = M.op["~="] + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {2, 1})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {2, 1})). + not_to_contain_error "was deprecated" + + - it returns false if the arguments are equal: + expect (f (1, 1)).to_be (false) + expect (f ("foo", "foo")).to_be (false) + - it returns true if the arguments are unequal: + expect (f (1, 2)).to_be (true) + expect (f ("foo", "bar")).to_be (true) + expect (f ({}, {})).to_be (true) + + +- describe reduce: + - before: + op = require "std.operator" + + f = M.reduce + + - context with bad arguments: + badargs.diagnose (f, "std.functional.reduce (func, any, [func], any*)") + + - it works with an empty table: + expect (f (op.sum, 2, ipairs, {})).to_be (2) + - it calls a binary function over single return value iterator results: + expect (f (op.sum, 2, base.ielems, {3})). + to_be (2 + 3) + expect (f (op.prod, 2, base.ielems, {3, 4})). + to_be (2 * 3 * 4) + - it calls a binary function over key:value iterator results: + expect (f (op.sum, 2, base.ielems, {3})).to_be (2 + 3) + expect (f (op.prod, 2, base.ielems, {3, 4})).to_be (2 * 3 * 4) + - it propagates nil arguments correctly: + function set (t, k, v) t[k] = tostring (v) return t end + expect (f (set, {}, base.npairs, {1, nil, nil, "a", false})). + to_equal {"1", "nil", "nil", "a", "false"} + expect (f (set, {}, base.npairs, {nil, nil, "3"})). + to_equal {"nil", "nil", "3"} + - it reduces elements from left to right: + expect (f (op.pow, 2, base.ielems, {3, 4})).to_be ((2 ^ 3) ^ 4) + - it passes all iterator results to accumulator function: + expect (f (rawset, {}, {"one", two=5})).to_equal {"one", two=5} + + +- describe zip: + - before: + tt = {{1, 2}, {3, 4}, {5, 6}} + + f = M.zip + + - context with bad arguments: + badargs.diagnose (f, "std.functional.zip (table)") + + - it works for an empty table: + expect (f {}).to_equal {} + - it is the inverse of itself: + expect (f (f (tt))).to_equal (tt) + - it transposes rows and columns: + expect (f (tt)).to_equal {{1, 3, 5}, {2, 4, 6}} + expect (f {x={a=1, b=2}, y={a=3, b=4}, z={b=5}}). + to_equal {a={x=1, y=3}, b={x=2,y=4,z=5}} + + +- describe zip_with: + - before: + tt = {{1, 2}, {3, 4}, {5}} + fn = function (...) return tonumber (table.concat {...}) end + + f = M.zip_with + + - context with bad arguments: + badargs.diagnose (f, "std.functional.zip_with (function, table of tables)") + + - it works for an empty table: + expect (f (fn, {})).to_equal {} + - it returns a table: + expect (type (f (fn, tt))).to_be "table" + - it returns the result in a new table: + expect (f (fn, tt)).not_to_be (tt) + - it does not perturb the argument list: + m = f (fn, tt) + expect (tt).to_equal {{1, 2}, {3, 4}, {5}} + - it combines column entries with a function: + expect (f (fn, tt)).to_equal {135, 24} + - it discards hash-part arguments: + expect (f (fn, {{1,2}, x={3,4}, {[2]=5}})).to_equal {1, 25} + - it combines matching key entries with a function: + expect (f (fn, {{a=1,b=2}, {a=3,b=4}, {b=5}})). + to_equal {a=13, b=245} diff --git a/specs/io_spec.yaml b/specs/io_spec.yaml new file mode 100644 index 0000000..d35b5db --- /dev/null +++ b/specs/io_spec.yaml @@ -0,0 +1,442 @@ +before: | + base_module = "io" + this_module = "std.io" + global_table = "_G" + + extend_base = { "catdir", "catfile", "die", "dirname", "monkey_patch", + "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 monkey_patch: + - before: + namespace = {} + + f = M.monkey_patch + + - context with bad arguments: + badargs.diagnose (f, "std.io.monkey_patch (?table)") + + # Ideally, `.to_be (M)`, except that M is cloned from a nested context + # by Specl to prevent us from affecting any other examples, thus the + # address is different by now. + - it returns std.io module table: + expect (f {}).to_equal (M) + - it injects std.io apis into the given namespace: + namespace = {} + f (namespace) + for _, api in ipairs (extend_base) do + expect (namespace.io[api]).to_be (M[api]) + end + - it installs file methods: + mt = { "file metatable" } + io = f { + io = { + stdin = setmetatable ({ "stdin" }, mt), + stdout = setmetatable ({ "stdout" }, mt), + stderr = setmetatable ({ "stderr" }, mt) + } + } + expect (mt.readlines).to_be (M.readlines) + expect (mt.writelines).to_be (M.writelines) + + +- describe process_files: + - before: + name = "Makefile" + names = {"Makefile", "config.log", "config.status", "build-aux/config.ld"} + 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\n4\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)") + + 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 + } + + - 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)") + + 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 + } + + - 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': + expect (f (false)).to_raise (badarg (1, "?file|string|number", "boolean")) + - 'it diagnoses argument #2 type not string, number or nil': + expect (f (1, false)).to_raise (badarg (2, "string|number", "boolean")) + - 'it diagnoses argument #3 type not string, number or nil': + expect (f (1, 2, false)).to_raise (badarg (3, "string|number", "boolean")) + - it diagnoses closed file argument: | + closed = io.open (name, "r") closed:close () + expect (f (closed)).to_raise (badarg (1, "?file|string|number", "closed file")) + + - 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/specs/list_spec.yaml b/specs/list_spec.yaml new file mode 100644 index 0000000..7a90aa0 --- /dev/null +++ b/specs/list_spec.yaml @@ -0,0 +1,1239 @@ +before: + this_module = "std.list" + global_table = "_G" + + exported_apis = { "append", "compare", "concat", "cons", "depair", + "elems", "enpair", "filter", "flatten", "foldl", + "foldr", "index_key", "index_value", "map", + "map_with", "project", "relems", "rep", "reverse", + "shape", "sub", "tail", "transpose", "zip_with" } + + M = require (this_module) + + List = M {} + l = List {"foo", "bar", "baz"} + + + +specify std.list: +- context when required: + - context by name: + - it does not touch the global table: + expect (show_apis {added_to="_G", by="std.list"}). + 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 via the std module: + - it does not touch the global table: + expect (show_apis {added_to=global_table, by="std"}). + to_equal {} + +- describe construction: + - context from List clone method: + - it constructs a new list: + l = List:clone {} + expect (l).not_to_be (List) + expect (prototype (l)).to_be "List" + - it reuses the List metatable: + l, m = List:clone {"l"}, List:clone {"m"} + expect (getmetatable (l)).to_be (getmetatable (m)) + - it initialises List with constructor parameters: + m = List:clone {"foo", "bar", "baz"} + expect (m).to_equal (l) + - it serves as a prototype for new instances: + m = l:clone {} + expect (prototype (m)).to_be "List" + expect (m).to_equal (l) + expect (getmetatable (m)).to_be (getmetatable (l)) + + # List {args} is just syntactic sugar for List:clone {args} + - context from List object prototype: + - it constructs a new List: + l = List {} + expect (l).not_to_be (List) + expect (prototype (l)).to_be "List" + - it reuses the List metatable: + l, m = List {"l"}, List {"m"} + expect (getmetatable (l)).to_be (getmetatable (m)) + - it initialises List with constructor parameters: + m = List {"foo", "bar", "baz"} + expect (m).to_equal (l) + - it serves as a prototype for new instances: + m = l {} + expect (prototype (m)).to_be "List" + expect (m).to_equal (l) + expect (getmetatable (m)).to_be (getmetatable (l)) + + +- describe metatable propagation: + - it reuses the metatable for List constructed objects: + m = List {"foo", "bar"} + expect (getmetatable (m)).to_be (getmetatable (l)) + + +- describe append: + - before: + f = M.append + + - context with bad arguments: + badargs.diagnose (f, "std.list.append (List, any)") + + - context as a module function: + - it returns a List object: + expect (prototype (f (l, "quux"))).to_be "List" + - it works for an empty List: + expect (f (List {}, "quux")).to_equal (List {"quux"}) + - it appends an item to a List: + expect (f (l, "quux")). + to_equal (List {"foo", "bar", "baz", "quux"}) + + - context as an object method: + - before: + f = l.append + + - it returns a List object: + expect (prototype (f (l, "quux"))).to_be "List" + - it works for an empty List: + expect (f (List {}, "quux")).to_equal (List {"quux"}) + - it appends an item to a List: + expect (f (l, "quux")). + to_equal (List {"foo", "bar", "baz", "quux"}) + + - context as a List metamethod: + - it returns a List object: + expect (prototype (l + "quux")).to_be "List" + - it works for an empty list: + expect (List {} + "quux").to_equal (List {"quux"}) + - it appends an item to a list: + expect (l + "quux"). + to_equal (List {"foo", "bar", "baz", "quux"}) + + +- describe compare: + - before: + a, b = List {"foo", "bar"}, List {"foo", "baz"} + + f = M.compare + + - context with bad arguments: + badargs.diagnose (f, "std.list.compare (List, List|table)") + + - context as a module function: + - it returns -1 when the first list is less than the second: + expect (f (a, {"foo", "baz"})).to_be (-1) + expect (f (a, List {"foo", "baz"})).to_be (-1) + - it returns -1 when the second list has additional elements: + expect (f (List {"foo"}, {"foo", "bar"})).to_be (-1) + expect (f (List {"foo"}, List {"foo", "bar"})).to_be (-1) + - it returns 0 when two lists are the same: + expect (f (a, {"foo", "bar"})).to_be (0) + expect (f (a, List {"foo", "bar"})).to_be (0) + - it returns +1 when the first list is greater than the second: + expect (f (a, {"baz", "quux"})).to_be (1) + expect (f (a, List {"baz", "quux"})).to_be (1) + - it returns +1 when the first list has additional elements: + expect (f (a, {"foo"})).to_be (1) + expect (f (a, List {"foo"})).to_be (1) + - it compares numerically when both arguments can be coerced: + a, b = List {"1", "2", "3"}, List {"1", "2", "10"} + expect (f (a, b)).to_be (-1) + + - context as an object method: + - before: + f = a.compare + + - it returns -1 when the first list is less than the second: + expect (f (a, {"foo", "baz"})).to_be (-1) + expect (f (a, List {"foo", "baz"})).to_be (-1) + - it returns -1 when the second list has additional elements: | + b = List {"foo"} + expect (f (b, {"foo", "bar"})).to_be (-1) + expect (List {"foo"}:compare (List {"foo", "bar"})).to_be (-1) + - it returns 0 when two lists are the same: + expect (f (a, {"foo", "bar"})).to_be (0) + expect (f (a, List {"foo", "bar"})).to_be (0) + - it returns +1 when the first list is greater than the second: + expect (f (a, {"baz", "quux"})).to_be (1) + expect (f (a, List {"baz", "quux"})).to_be (1) + - it returns +1 when the first list has additional elements: + expect (f (a, {"foo"})).to_be (1) + expect (f (a, List {"foo"})).to_be (1) + - it compares numerically when both arguments can be coerced: + a, b = List {"1", "2", "3"}, List {"1", "2", "10"} + expect (f (a, b)).to_be (-1) + + - context as a '<' List metamethod: + - it succeeds when the first list is less than the second: + expect (a < b).to_be (true) + - it fails when the first list is not less than the second: + expect (a < a).to_be (false) + expect (b < a).to_be (false) + - it compares numerically when both arguments can be coerced: + a, b = List {"1", "2", "3"}, List {"1", "2", "10"} + expect (a < b).to_be (true) + + - context as a '>' List metamethod: + - it succeeds when the first list is greater than the second: + expect (b > a).to_be (true) + - it fails when the first list is not greater than the second: + expect (b > b).to_be (false) + expect (a > b).to_be (false) + - it compares numerically when both arguments can be coerced: + a, b = List {"1", "2", "3"}, List {"1", "2", "10"} + expect (a > b).to_be (false) + + - context as a '<=' List metamethod: + - it succeeds when the first list is less than or equal to the second: + expect (a <= b).to_be (true) + expect (a <= a).to_be (true) + - it fails when the first list is not less than or equal to the second: + expect (b <= a).to_be (false) + - it compares numerically when both arguments can be coerced: + a, b = List {"1", "2", "3"}, List {"1", "2", "10"} + expect (a <= b).to_be (true) + + - context as a '>=' List metamethod: + - it succeeds when the first list is greater than or equal to the second: + expect (b >= a).to_be (true) + expect (b >= b).to_be (true) + - it fails when the first list is not greater than or equal to the second: + expect (a >= b).to_be (false) + - it compares numerically when both arguments can be coerced: + a, b = List {"1", "2", "3"}, List {"1", "2", "10"} + expect (a >= b).to_be (false) + + +- describe concat: + - before: + l = List {"foo", "bar"} + + f = M.concat + + - context with bad arguments: + badargs.diagnose (f, "std.list.concat (List, List|table*)") + + - context as a module function: + - it returns a List object: + expect (prototype (f (l, l))).to_be "List" + - it works for an empty List: + expect (f (List {}, {"baz"})).to_equal (List {"baz"}) + expect (f (List {}, List {"baz"})).to_equal (List {"baz"}) + - it concatenates Lists: + expect (f (l, {"baz", "quux"})). + to_equal (List {"foo", "bar", "baz", "quux"}) + expect (f (l, List {"baz", "quux"})). + to_equal (List {"foo", "bar", "baz", "quux"}) + expect (f (l, {"baz"}, {"quux"})). + to_equal (List {"foo", "bar", "baz", "quux"}) + expect (f (l, List {"baz"}, List {"quux"})). + to_equal (List {"foo", "bar", "baz", "quux"}) + + - context as an object method: + - before: + f = l.concat + + - it returns a List object: + expect (prototype (f (l, l))).to_be "List" + - it works for an empty List: + expect (f (List {}, {"baz"})).to_equal (List {"baz"}) + expect (f (List {}, List {"baz"})).to_equal (List {"baz"}) + - it concatenates Lists: + expect (f (l, {"baz", "quux"})). + to_equal (List {"foo", "bar", "baz", "quux"}) + expect (f (l, List {"baz", "quux"})). + to_equal (List {"foo", "bar", "baz", "quux"}) + expect (f (l, {"baz"}, {"quux"})). + to_equal (List {"foo", "bar", "baz", "quux"}) + expect (f (l, List {"baz"}, List {"quux"})). + to_equal (List {"foo", "bar", "baz", "quux"}) + + # Beware that .. operations are right associative + - context as a List metamethod: + - it returns a List object: + expect (prototype (l .. List {"baz"})).to_be "List" + - it works for an empty List: + expect (List {} .. {"baz"}).to_equal (List {"baz"}) + expect (List {} .. List {"baz"}).to_equal (List {"baz"}) + - it concatenates Lists: + expect (l .. {"baz", "quux"}). + to_equal (List {"foo", "bar", "baz", "quux"}) + expect (l .. List {"baz", "quux"}). + to_equal (List {"foo", "bar", "baz", "quux"}) + expect ({"baz"} .. {"quux"} .. l). + to_equal (List {"baz", "quux", "foo", "bar"}) + expect (l .. List {"baz"} .. List {"quux"}). + to_equal (List {"foo", "bar", "baz", "quux"}) + + +- describe cons: + - before: + f = M.cons + + - context with bad arguments: + badargs.diagnose (f, "std.list.cons (List, any)") + + - context as a module function: + - it returns a List object: + expect (prototype (f (l, "x"))).to_be "List" + - it prepends an item to a List: + expect (f (l, "x")).to_equal (List {"x", "foo", "bar", "baz"}) + - it works for empty Lists: + expect (f (List {}, "x")).to_equal (List {"x"}) + + - context as an object method: + - before: + f = l.cons + + - it returns a List object: + expect (prototype (f (l, "x"))).to_be "List" + - it prepends an item to a List: + expect (f (l, "x")).to_equal (List {"x", "foo", "bar", "baz"}) + - it works for empty Lists: + expect (f (List {}, "x")).to_equal (List {"x"}) + + +- describe depair: + - before: + l = List {List {1, "first"}, List {2, "second"}, List {"third", 4}} + t = {"first", "second", third = 4} + + - context as a module function: + - before: + f = M.depair + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l})).not_to_contain_error "was deprecated" + + - it returns a primitive table: + expect (prototype (f (l))).to_be "table" + - it works with an empty List: + l = List {} + expect (f (l)).to_equal {} + - it is the inverse of enpair: + expect (f (l)).to_equal (t) + + - context as an object method: + - before: + f = l.depair + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l})).not_to_contain_error "was deprecated" + + - it returns a primitive table: + expect (prototype (f (l))).to_be "table" + - it works with an empty List: + expect (f (List {})).to_equal {} + - it is the inverse of enpair: + expect (f (l)).to_equal (t) + + +- describe elems: + - context as a module function: + - before: + f = M.elems + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {{}})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {{}})).not_to_contain_error "was deprecated" + + - it is an iterator over List members: + t = {} + for e in f (l) do table.insert (t, e) end + expect (t).to_equal {"foo", "bar", "baz"} + - it works for an empty List: + t = {} + for e in f (List {}) do table.insert (t, e) end + expect (t).to_equal {} + + - context as an object method: + - before: + f = l.elems + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l})).not_to_contain_error "was deprecated" + + - it is an iterator over List members: + t = {} + for e in l:elems () do table.insert (t, e) end + expect (t).to_equal {"foo", "bar", "baz"} + - it works for an empty List: + t, l = {}, List {} + for e in l:elems () do table.insert (t, e) end + expect (t).to_equal {} + + +- describe enpair: + - before: + t = {"first", "second", third = 4} + f = M.enpair + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {t})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {t})).not_to_contain_error "was deprecated" + + - context as a module function: + - it returns a List object: + expect (prototype (f (t))).to_be "List" + - it works for an empty table: + expect (f {}).to_equal (List {}) + - it turns a table into a List of pairs: + expect (f (t)). + to_equal (List {List {1, "first"}, List {2, "second"}, List {"third", 4}}) + + +- describe filter: + - before: + l = List {"foo", "bar", "baz", "quux"} + p = function (e) return (e:match "a" ~= nil) end + + - context as a module function: + - before: + f = M.filter + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {p, l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {p, l})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (p, l))).to_be "List" + - it works for an empty List: + expect (f (p, List {})).to_equal (List {}) + - it filters a List according to a predicate: + expect (f (p, l)).to_equal (List {"bar", "baz"}) + + - context as an object method: + - before: + f = l.filter + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l, p})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l, p})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (l, p))).to_be "List" + - it works for an empty List: + expect (f (List {}, p)).to_equal (List {}) + - it filters a List according to a predicate: + expect (f (l, p)).to_equal (List {"bar", "baz"}) + + +- describe flatten: + - before: + l = List {List {List {"one"}}, "two", List {List {"three"}, "four"}} + + - context as a module function: + - before: + f = M.flatten + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (l))).to_be "List" + - it works for an empty List: + l = List {} + expect (f (l)).to_equal (List {}) + - it flattens a List: + expect (f (l)). + to_equal (List {"one", "two", "three", "four"}) + + - context as an object method: + - before: + f = l.flatten + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (l))).to_be "List" + - it works for an empty List: + l = List {} + expect (f (l)).to_equal (List {}) + - it flattens a List: + expect (f (l)). + to_equal (List {"one", "two", "three", "four"}) + + +- describe foldl: + - before: + op = require "std.operator" + l = List {3, 4} + + - context as a module function: + - before: + f = M.foldl + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {op.sum, 1, l})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {op.sum, 1, l})). + not_to_contain_error "was deprecated" + + - context with a table: + - it works with an empty table: + expect (f (op.sum, 10000, {})).to_be (10000) + - it folds a binary function through a table: + expect (f (op.sum, 10000, {1, 10, 100})).to_be (10111) + - it folds from left to right: + expect (f (op.pow, 2, {3, 4})).to_be ((2 ^ 3) ^ 4) + + - context with a List: + - it works with an empty List: + expect (f (op.sum, 10000, List {})).to_be (10000) + - it folds a binary function through a List: + expect (f (op.sum, 10000, List {1, 10, 100})). + to_be (10111) + - it folds from left to right: + expect (f (op.pow, 2, List {3, 4})).to_be ((2 ^ 3) ^ 4) + + - context as an object method: + - before: + f = l.foldl + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l, op.sum, 1})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l, op.sum, 1})). + not_to_contain_error "was deprecated" + + - it works with an empty List: + l = List {} + expect (f (l, op.sum, 2)).to_be (2) + - it folds a binary function through a List: + expect (f (l, op.sum, 2)).to_be (9) + - it folds from left to right: + expect (f (l, op.pow, 2)).to_be ((2 ^ 3) ^ 4) + + +- describe foldr: + - before: + op = require "std.operator" + l = List {10000, 100} + + - context as a module function: + - before: + f = M.foldr + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {op.sum, 1, {10}})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {op.sum, 1, {10}})). + not_to_contain_error "was deprecated" + + - context with a table: + - it works with an empty table: + expect (f (op.sum, 10000, {})).to_be (10000) + - it folds a binary function through a table: + expect (f (op.sum, 10000, {1, 10, 100})).to_be (10111) + - it folds from right to left: + expect (f (op.quot, 10, {10000, 100})).to_be (10000 / (100 / 10)) + + - context with a List: + - it works with an empty List: + expect (f (op.sum, 10000, List {})).to_be (10000) + - it folds a binary function through a List: + expect (f (op.sum, 10000, List {1, 10, 100})). + to_be (10111) + - it folds from right to left: + expect (f (op.quot, 10, List {10000, 100})). + to_be (10000 / (100 / 10)) + + - context as an object method: + - before: + f = l.foldr + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l, op.sum, 1})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l, op.sum, 1})). + not_to_contain_error "was deprecated" + + - it works with an empty List: + l = List {} + expect (f (l, op.sum, 10)).to_be (10) + - it folds a binary function through a List: + expect (f (l, op.sum, 10)).to_be (10110) + - it folds from right to left: + expect (f (l, op.quot, 10)).to_be (10000 / (100 / 10)) + + +- describe index_key: + - context as a module function: + - before: + f = M.index_key + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {1, List {{1}}})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {1, List {{1}}})). + not_to_contain_error "was deprecated" + + - it makes a map of matched table field values to table List offsets: + l = List {{a = "b", c = "d"}, {e = "x", f = "g"}, {a = "x"}} + t = f ("a", l) + expect (t).to_equal {b = 1, x = 3} + for k, v in pairs (t) do + expect (k).to_equal (l[v]["a"]) + end + - it captures only the last matching List offset: + l = List {{a = "b"}, {a = "x"}, {a = "b"}} + t = f ("a", l) + expect (t.b).not_to_be (1) + expect (t.x).to_be (2) + expect (t.b).to_be (3) + - it produces incomplete indices when faced with repeated matching table values: + l = List {{1, 2, 3}, {2}, {2, 1, 3, 2, 1}} + expect (f (1, l)).to_equal {1, 3} + expect (f (2, l)).to_equal {3, 1} + expect (f (3, l)).to_equal {nil, nil, 3} + + - context as an object method: + - before: + f = l.index_key + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l, 1})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l, 1})).not_to_contain_error "was deprecated" + + - it makes a map of matched table field values to table List offsets: + l = List {{a = "b", c = "d"}, {e = "x", f = "g"}, {a = "x"}} + t = l:index_key "a" + expect (t).to_equal {b = 1, x = 3} + for k, v in pairs (t) do + expect (k).to_equal (l[v]["a"]) + end + - it captures only the last matching List offset: + l = List {{a = "b"}, {a = "x"}, {a = "b"}} + t = l:index_key "a" + expect (t.b).not_to_be (1) + expect (t.x).to_be (2) + expect (t.b).to_be (3) + - it produces incomplete indices when faced with repeated matching table values: + l = List {{1, 2, 3}, {2}, {2, 1, 3, 2, 1}} + expect (l:index_key (1)).to_equal {1, 3} + expect (l:index_key (2)).to_equal {3, 1} + expect (l:index_key (3)).to_equal {nil, nil, 3} + + +- describe index_value: + - context as a module function: + - before: + f = M.index_value + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {1, List {{1}}})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {1, List {{1}}})). + not_to_contain_error "was deprecated" + + - it makes a table of matched table field values to table List references: + l = List {{a = "b", c = "d"}, {e = "x", f = "g"}, {a = "x"}} + t = f ("a", l) + expect (t).to_equal {b = l[1], x = l[3]} + for k, v in pairs (t) do + expect (k).to_equal (v["a"]) + end + - it captures only the last matching List offset: + l = List {{a = "b"}, {a = "x"}, {a = "b"}} + t = f ("a", l) + expect (t.b).not_to_be (l[1]) + expect (t.x).to_be (l[2]) + expect (t.b).to_be (l[3]) + - it produces incomplete indices when faced with repeated matching table values: + l = List {{1, 2, 3}, {2}, {2, 1, 3, 2, 1}} + expect (f (1, l)).to_equal {l[1], l[3]} + expect (f (2, l)).to_equal {l[3], l[1]} + expect (f (3, l)).to_equal {nil, nil, l[3]} + + - context as an object method: + - before: + l = List {{1}} + + f = l.index_value + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l, 1})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l, 1})).not_to_contain_error "was deprecated" + + - it makes a table of matched table field values to table List references: + l = List {{a = "b", c = "d"}, {e = "x", f = "g"}, {a = "x"}} + t = l:index_value "a" + expect (t).to_equal {b = l[1], x = l[3]} + for k, v in pairs (t) do + expect (k).to_equal (v["a"]) + end + - it captures only the last matching List offset: + l = List {{a = "b"}, {a = "x"}, {a = "b"}} + t = l:index_value "a" + expect (t.b).not_to_be (l[1]) + expect (t.x).to_be (l[2]) + expect (t.b).to_be (l[3]) + - it produces incomplete indices when faced with repeated matching table values: + l = List {{1, 2, 3}, {2}, {2, 1, 3, 2, 1}} + expect (l:index_value (1)).to_equal {l[1], l[3]} + expect (l:index_value (2)).to_equal {l[3], l[1]} + expect (l:index_value (3)).to_equal {nil, nil, l[3]} + + +- describe map: + - before: + l = List {1, 2, 3, 4, 5} + sq = function (n) return n * n end + + - context as a module function: + - before: + f, badarg = init (M, this_module, "map") + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {sq, l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {sq, l})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (sq, l))).to_be "List" + - it works for an empty List: + expect (f (sq, List {})).to_equal (List {}) + - it creates a new List: + o = l + m = f (sq, l) + expect (l).to_equal (o) + expect (m).not_to_equal (o) + expect (l).to_equal (List {1, 2, 3, 4, 5}) + - it maps a function over a List: + expect (f (sq, l)).to_equal (List {1, 4, 9, 16, 25}) + + - context as an object method: + - before: + f = l.map + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l, sq})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l, sq})).not_to_contain_error "was deprecated" + + - it returns a List object: + m = f (l, sq) + expect (prototype (m)).to_be "List" + - it works for an empty List: + expect (f (List {}, sq)).to_equal (List {}) + - it creates a new List: + o = l + m = f (l, sq) + expect (l).to_equal (o) + expect (m).not_to_equal (o) + expect (l).to_equal (List {1, 2, 3, 4, 5}) + - it maps a function over a List: + expect (f (l, sq)).to_equal (List {1, 4, 9, 16, 25}) + + +- describe map_with: + - before: + l = List {List {1, 2, 3}, List {4, 5}} + fn = function (...) return select ("#", ...) end + + - context as a module function: + - before: + f = M.map_with + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {fn, l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {fn, l})).not_to_contain_error "was deprecated" + + - it returns a List object: + m = f (fn, l) + expect (prototype (m)).to_be "List" + - it creates a new List: + o = l + m = f (fn, l) + expect (l).to_equal (o) + expect (m).not_to_equal (o) + expect (l).to_equal (List {List {1, 2, 3}, List {4, 5}}) + - it maps a function over a List: + expect (f (fn, l)).to_equal (List {3, 2}) + - it works for an empty List: + l = List {} + expect (f (fn, l)).to_equal (List {}) + + - context as an object method: + - before: + f = l.map_with + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l, fn})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l, fn})).not_to_contain_error "was deprecated" + + - it returns a List object: + m = f (l, fn) + expect (prototype (m)).to_be "List" + - it creates a new List: + o = l + m = f (l, fn) + expect (l).to_equal (o) + expect (m).not_to_equal (o) + expect (l).to_equal (List {List {1, 2, 3}, List {4, 5}}) + - it maps a function over a List: + expect (f (l, fn)).to_equal (List {3, 2}) + - it works for an empty List: + l = List {} + expect (f (l, fn)).to_equal (List {}) + + +- describe project: + - before: + l = List { + {first = false, second = true, third = true}, + {first = 1, second = 2, third = 3}, + {first = "1st", second = "2nd", third = "3rd"}, + } + + - context as a module function: + - before: + f = M.project + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {"third", l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {"third", l})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f ("third", l))).to_be "List" + - it works with an empty List: + expect (f ("third", List {})).to_equal (List {}) + - it projects a List of fields from a List of tables: + expect (f ("third", l)).to_equal (List {true, 3, "3rd"}) + - it projects fields with a falsey value correctly: + expect (f ("first", l)).to_equal (List {false, 1, "1st"}) + + - context as an object method: + - before: + f = l.project + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l, "third"})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l, "third"})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (l, "third"))).to_be "List" + - it works with an empty List: + expect (f (List {}, "third")).to_equal (List {}) + - it projects a List of fields from a List of tables: + expect (f (l, "third")).to_equal (List {true, 3, "3rd"}) + - it projects fields with a falsey value correctly: + expect (f (l, "first")).to_equal (List {false, 1, "1st"}) + + +- describe relems: + - context as a module function: + - before: + f = M.relems + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l})).not_to_contain_error "was deprecated" + + - it is a reverse iterator over List members: + t = {} + for e in f (l) do table.insert (t, e) end + expect (t).to_equal {"baz", "bar", "foo"} + - it works for an empty List: + t = {} + for e in f (List {}) do table.insert (t, e) end + expect (t).to_equal {} + + - context as an object method: + - before: + f = l.relems + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l})).not_to_contain_error "was deprecated" + + - it is a reverse iterator over List members: + t = {} + for e in l:relems () do table.insert (t, e) end + expect (t).to_equal {"baz", "bar", "foo"} + - it works for an empty List: + t, l = {}, List {} + for e in l:relems () do table.insert (t, e) end + expect (t).to_equal {} + + +- describe rep: + - before: + l = List {"foo", "bar"} + + f = M.rep + + - context with bad arguments: + badargs.diagnose (f, "std.list.rep (List, int)") + + - context as a module function: + - it returns a List object: + expect (prototype (f (l, 3))).to_be "List" + - it works for an empty List: + expect (f (List {}, 99)).to_equal (List {}) + - it repeats the contents of a List: + expect (f (l, 3)). + to_equal (List {"foo", "bar", "foo", "bar", "foo", "bar"}) + + - context as an object method: + - before: + f = l.rep + + - it returns a List object: + expect (prototype (f (l, 3))).to_be "List" + - it works for an empty List: + expect (f (List {}, 99)).to_equal (List {}) + - it repeats the contents of a List: + expect (f (l, 3)). + to_equal (List {"foo", "bar", "foo", "bar", "foo", "bar"}) + + +- describe reverse: + - before: + l = List {"foo", "bar", "baz", "quux"} + + - context as a module function: + - before: + f = M.reverse + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {{}})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {{}})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (l))).to_be "List" + - it works for an empty List: + l = List {} + expect (f (l)).to_equal (List {}) + - it makes a new reversed List: + m = l + expect (f (l)). + to_equal (List {"quux", "baz", "bar", "foo"}) + expect (l).to_equal (List {"foo", "bar", "baz", "quux"}) + expect (l).to_be (m) + + - context as an object method: + - before: + f = l.reverse + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (l))).to_be "List" + - it works for an empty List: + expect (f (List {})).to_equal (List {}) + - it makes a new reversed List: + m = l + expect (f (l)). + to_equal (List {"quux", "baz", "bar", "foo"}) + expect (l).to_equal (List {"foo", "bar", "baz", "quux"}) + expect (l).to_be (m) + + +- describe shape: + - before: + l = List {1, 2, 3, 4, 5, 6} + + - context as a module function: + - before: + f = M.shape + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {{0}, l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {{0}, l})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f ({2, 3}, l))).to_be "List" + - it works for an empty List: + expect (f ({0}, List {})).to_equal (List {}) + - it returns the result in a new List object: + expect (f ({2, 3}, l)).not_to_be (l) + - it does not perturb the argument List: + f ({2, 3}, l) + expect (l).to_equal (List {1, 2, 3, 4, 5, 6}) + - it reshapes a List according to given dimensions: + expect (f ({2, 3}, l)). + to_equal (List {List {1, 2, 3}, List {4, 5, 6}}) + expect (f ({3, 2}, l)). + to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}}) + - it treats 0-valued dimensions as an indefinite number: + expect (f ({2, 0}, l)). + to_equal (List {List {1, 2, 3}, List {4, 5, 6}}) + expect (f ({0, 2}, l)). + to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}}) + + - context as an object method: + - before: + f = l.shape + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l, {0}})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l, {0}})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (l, {2, 3}))).to_be "List" + - it works for an empty List: + expect (f (List {}, {0})).to_equal (List {}) + - it returns the result in a new List object: + expect (f (l, {2, 3})):not_to_be (l) + - it does not perturb the argument List: + f (l, {2, 3}) + expect (l).to_equal (List {1, 2, 3, 4, 5, 6}) + - it reshapes a List according to given dimensions: + expect (f (l, {2, 3})). + to_equal (List {List {1, 2, 3}, List {4, 5, 6}}) + expect (f (l, {3, 2})). + to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}}) + - it treats 0-valued dimensions as an indefinite number: + expect (f (l, {2, 0})). + to_equal (List {List {1, 2, 3}, List {4, 5, 6}}) + expect (f (l, {0, 2})). + to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}}) + + +- describe sub: + - before: + l = List {1, 2, 3, 4, 5, 6, 7} + + f = M.sub + + - context with bad arguments: + badargs.diagnose (f, "std.list.sub (List, ?int, ?int)") + + - context as a module function: + - it returns a List object: + expect (prototype (f (l, 1, 1))).to_be "List" + - it makes a List from a subrange of another List: + expect (f (l, 2, 5)).to_equal (List {2, 3, 4, 5}) + - it truncates the result if 'to' argument is too large: + expect (f (l, 5, 10)).to_equal (List {5, 6, 7}) + - it defaults 'to' to the end of the List: + expect (f (l, 5)).to_equal (List {5, 6, 7}) + - it defaults 'from' to the beginning of the List: + expect (f (l)).to_equal (l) + - it returns an empty List when 'from' is greater than 'to': + expect (f (l, 2, 1)).to_equal (List {}) + - it counts from the end of the List for a negative 'from' argument: + expect (f (l, -3)).to_equal (List {5, 6, 7}) + - it counts from the end of the List for a negative 'to' argument: + expect (f (l, -5, -2)).to_equal (List {3, 4, 5, 6}) + + - context as an object method: + - before: + f = l.sub + + - it returns a List object: + expect (prototype (f (l, 1, 1))).to_be "List" + - it makes a List from a subrange of another List: + expect (f (l, 2, 5)).to_equal (List {2, 3, 4, 5}) + - it truncates the result if 'to' argument is too large: + expect (f (l, 5, 10)).to_equal (List {5, 6, 7}) + - it defaults 'to' to the end of the List: + expect (f (l, 5)).to_equal (List {5, 6, 7}) + - it defaults 'from' to the beginning of the List: + expect (f (l)).to_equal (l) + - it returns an empty List when 'from' is greater than 'to': + expect (f (l, 2, 1)).to_equal (List {}) + - it counts from the end of the List for a negative 'from' argument: + expect (f (l, -3)).to_equal (List {5, 6, 7}) + - it counts from the end of the List for a negative 'to' argument: + expect (f (l, -5, -2)).to_equal (List {3, 4, 5, 6}) + + +- describe tail: + - before: + l = List {1, 2, 3, 4, 5, 6, 7} + + f = M.tail + + - context with bad arguments: + badargs.diagnose (f, "std.list.tail (List)") + + - context as a module function: + - it returns a List object: + expect (prototype (f (l))).to_be "List" + - it makes a new List with the first element removed: + expect (f (l)).to_equal (List {2, 3, 4, 5, 6, 7}) + - it works for an empty List: + expect (f (List {})).to_equal (List {}) + - it returns an empty List when passed a List with one element: + expect (f (List {1})).to_equal (List {}) + + - context as an object method: + - before: + f = l.tail + + - it returns a List object: + expect (prototype (f (l))).to_be "List" + - it makes a new List with the first element removed: + expect (f (l)).to_equal (List {2, 3, 4, 5, 6, 7}) + - it works for an empty List: + expect (f (List {})).to_equal (List {}) + - it returns an empty List when passed a List with one element: + expect (f (List {1})).to_equal (List {}) + + +- describe transpose: + - before: + l = List {List {1, 2}, List {3, 4}, List {5, 6}} + + - context as a module function: + - before: + f = M.transpose + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (l))).to_be "List" + - it works for an empty List: + expect (f (List {})).to_equal (List {}) + - it returns the result in a new List object: + expect (f (l)).not_to_be (l) + - it does not perturb the argument List: + m = f (l) + expect (l).to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}}) + - it transposes rows and columns: + expect (f (l)).to_equal (List {List {1, 3, 5}, List {2, 4, 6}}) + + - context as an object method: + - before: + f = l.transpose + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (l))).to_be "List" + - it works for an empty List: + expect (f (List {})).to_equal (List {}) + - it returns the result in a new List object: + expect (f (l)).not_to_be (l) + - it does not perturb the argument List: + m = f (l) + expect (l).to_equal (List {List {1, 2}, List {3, 4}, List {5, 6}}) + - it transposes rows and columns: + expect (f (l)). + to_equal (List {List {1, 3, 5}, List {2, 4, 6}}) + + +- describe zip_with: + - before: + l = List {List {1, 2}, List {3, 4}, List {5}} + fn = function (...) return tonumber (table.concat {...}) end + + - context as a module function: + - before: + f = M.zip_with + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l, fn})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l, fn})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (l, fn))).to_be "List" + - it works for an empty List: + expect (f (List {}, fn)).to_equal (List {}) + - it returns the result in a new List object: + expect (f (l, fn)):not_to_be (l) + - it does not perturb the argument List: + m = f (l, fn) + expect (l).to_equal (List {List {1, 2}, List {3, 4}, List {5}}) + - it combines column entries with a function: + expect (f (l, fn)).to_equal (List {135, 24}) + + - context as an object method: + - before: + f = l.zip_with + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {l, fn})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {l, fn})).not_to_contain_error "was deprecated" + + - it returns a List object: + expect (prototype (f (l, fn))).to_be "List" + - it works for an empty List: + expect (f (List {}, fn)).to_equal (List {}) + - it returns the result in a new List object: + expect (f (l, fn)):not_to_be (l) + - it does not perturb the argument List: + m = f (l, fn) + expect (l).to_equal (List {List {1, 2}, List {3, 4}, List {5}}) + - it combines column entries with a function: + expect (f (l, fn)).to_equal (List {135, 24}) diff --git a/specs/math_spec.yaml b/specs/math_spec.yaml new file mode 100644 index 0000000..f198877 --- /dev/null +++ b/specs/math_spec.yaml @@ -0,0 +1,101 @@ +before: + base_module = "math" + this_module = "std.math" + global_table = "_G" + + extend_base = { "floor", "monkey_patch", "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 monkey_patch: + - before: + f = M.monkey_patch + + - context with bad arguments: + badargs.diagnose (f, "std.math.monkey_patch (?table)") + + # Ideally, `.to_be (M)`, except that M is cloned from a nested context + # by Specl to prevent us from affecting any other examples, thus the + # address is different by now. + - it returns std.math module table: + expect (f {}).to_equal (M) + - it injects std.math apis into the given namespace: + namespace = {} + f (namespace) + for _, api in ipairs (extend_base) do + expect (namespace.math[api]).to_be (M[api]) + end + + +- 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) diff --git a/specs/object_spec.yaml b/specs/object_spec.yaml new file mode 100644 index 0000000..a4d6603 --- /dev/null +++ b/specs/object_spec.yaml @@ -0,0 +1,303 @@ +before: + Object = require "std.object" + obj = Object {"foo", "bar", baz="quux"} + prototype = Object.prototype + + function copy (t) + local r = {} + for k, v in pairs (t) do r[k] = v end + return r + end + +specify std.object: +- context when required: + - context by name: + - it does not touch the global table: + expect (show_apis {added_to="_G", by="std.object"}). + to_equal {} + +- describe construction: + - context from Object clone method: + - it constructs a new object: + obj = Object:clone {} + expect (obj).not_to_be (Object) + expect (type (obj)).to_be "table" + expect (prototype (obj)).to_be "Object" + - it reuses the Object metatable: + o = obj:clone {"o"} + p = o:clone {"p"} + expect (p).not_to_be (o) + expect (getmetatable (o)).to_be (getmetatable (p)) + - it sets object fields from arguments: + expect (obj:clone {}).to_copy (obj) + - it serves as a prototype for new instances: + o = obj:clone {} + expect (prototype (o)).to_be "Object" + expect (o).to_copy (obj) + expect (getmetatable (o)).to_be (getmetatable (obj)) + - it separates '_' prefixed fields: + expect (Object:clone {foo="bar", _baz="quux"}). + to_equal (Object:clone {foo="bar"}) + - it puts '_' prefixed fields in a new metatable: + obj = Object:clone {foo="bar", _baz="quux"} + expect (getmetatable (obj)).not_to_be (getmetatable (Object)) + expect (getmetatable (obj)._baz).to_be "quux" + +- describe prototype: + - before: o = Object {} + + - context when called from the object module: + - it reports the prototype stored in the object's metatable: + expect (prototype (o)).to_be "Object" + - it reports the type of a cloned object: + expect (prototype (o {})).to_be "Object" + - it reports the type of a derived object: + Example = Object {_type = "Example"} + expect (prototype (Example)).to_be "Example" + - it reports the type of a cloned derived object: + Portal = Object {_type = "Demon"} + p = Portal {} + expect (prototype (p)).to_be "Demon" + expect (prototype (p {})).to_be "Demon" + - it recognizes a file object: + h = io.open (os.tmpname ()) + expect (prototype (h)).to_be "file" + h:close () + expect (prototype (h)).to_be "closed file" + - it recognizes a primitive object: + expect (prototype (nil)).to_be "nil" + expect (prototype (false)).to_be "boolean" + expect (prototype (0.0)).to_be "number" + expect (prototype "0.0").to_be "string" + expect (prototype (function () end)).to_be "function" + expect (prototype {}).to_be "table" + - context when called as an object method: + - it reports the type stored in the object's metatable: + expect (o:prototype ()).to_be "Object" + - it reports the type of a cloned object: + expect ((o {}):prototype ()).to_be "Object" + - it reports the type of a subclassed object: + Example = Object {_type = "Example"} + expect (Example:prototype ()).to_be "Example" + - it reports the type of a cloned subclassed object: + Portal = Object {_type = "Demon"} + p = Portal {} + expect (p:prototype ()).to_be "Demon" + expect ((p {}):prototype ()).to_be "Demon" + - context backwards compatibility: + - it reports the prototype stored in the object's metatable: + expect (Object.type (o)).to_be "Object" + - it reports the type stored in the object's metatable: + expect (o:type ()).to_be "Object" + + +- describe instantiation from a prototype: + - context when _init is nil: + - before: + Array = Object { + _type = "Array", + "foo", "bar", "baz", + } + Array._init = nil + + - it contains user-defined fields: + expect (copy (Array)). + to_equal {"foo", "bar", "baz"} + - it sets array part of instance object from positional parameters: + array = Array {"first", "second", "third"} + expect (copy (array)). + to_equal {"first", "second", "third"} + - it uses prototype values for missing positional parameters: + array = Array {"first", "second"} + expect (copy (array)). + to_equal {"first", "second", "baz"} + - it merges surplas positional parameters: + array = Array {"first", "second", "third", "fourth"} + expect (copy (array)). + to_equal {"first", "second", "third", "fourth"} + + - context when _init is an empty table: + - before: + Prototype = Object { + _type = "Prototype"; + _init = {}, + "first", "second", "third", + } + - it contains user-defined fields: + expect (copy (Prototype)). + to_equal {"first", "second", "third"} + - it ignores positional parameters: | + instance = Prototype {"foo", "bar"} + expect (instance).to_copy (Prototype) + + - context when _init is a table of field names: + - before: + Process = Object { + _type = "Process", + _init = {"status", "output", "errout"}, + status = -1, + output = "empty", + errout = "no errors", + } + - it contains user-defined fields: + expect (copy (Process)). + to_equal {status = -1, output = "empty", errout = "no errors"} + - it sets user-defined fields from positional parameters: + proc = Process {0, "output", "diagnostics"} + expect (copy (proc)). + to_equal {status = 0, output = "output", errout = "diagnostics"} + - it uses prototype values for missing positional parameters: + proc = Process {0, "output"} + expect (copy (proc)). + to_equal {status = 0, output = "output", errout = "no errors"} + - it discards surplus positional parameters: + proc = Process {0, "output", "diagnostics", "garbage"} + expect (copy (proc)). + to_equal { status = 0, output = "output", errout = "diagnostics" } + + - context when _init is a function: + - before: + Prototype = Object { + _type = "Prototype", + f1 = "proto1", f2 = "proto2", + _init = function (self, ...) + self.args = unpack {...} + return self + end, + } + - it passes user defined fields to custom _init function: + instance = Prototype {"param1", "param2"} + expect ({instance.f1, instance.f2, instance.args}). + to_equal {"proto1", "proto2", {"param1", "param2"}} + +- describe field access: + - before: + Prototype = Object { + _type = "Prototype", + _init = { "field", "method"}, + field = "in prototype", + method = function (self, ...) + return prototype (self) .. " class, " .. + table.concat ({...}, ", ") + end, + } + instance = Prototype {"in object", function (self, ...) + return prototype (self) .. " instance, " .. + table.concat ({...}, ", ") + end, + } + + - it provides object field access with dot notation: + expect (instance.field).to_be "in object" + - it provides class field acces with dot notation: + expect (Prototype.field).to_be "in prototype" + - it provides object method acces with colon notation: + expect (instance:method "object method call"). + to_be "Prototype instance, object method call" + - it provides class method access with class dot notation: + expect (Prototype.method (instance, "class method call")). + to_be "Prototype class, class method call" + - it allows new instance fields to be added: + instance.newfield = "new" + expect (instance.newfield).to_be "new" + - it allows new instance methods to be added: + instance.newmethod = function (self) + return prototype (self) .. ", new instance method" + end + expect (instance:newmethod ()).to_be "Prototype, new instance method" + - it allows new class methods to be added: + Prototype.newmethod = function (self) + return prototype (self) .. ", new class method" + end + expect (Prototype.newmethod (instance)). + to_be "Prototype, new class method" + + +- describe object method propagation: + - context with no custom instance methods: + # :prototype is a method defined by the root object + - it inherits prototype object methods: + instance = Object {} + expect (instance:prototype ()).to_be "Object" + - it propagates prototype methods to derived instances: + Derived = Object {_type = "Derived"} + instance = Derived {} + expect (instance:prototype ()).to_be "Derived" + - context with custom object methods: + - before: + bag = Object { + _type = "bag", + __index = { + add = function (self, item) + self[item] = (self[item] or 0) + 1 + return self + end, + }, + } + # :prototype is a method defined by the root object + - it inherits prototype object methods: + expect (bag:prototype ()).to_be "bag" + - it propagates prototype methods to derived instances: + instance = bag {} + expect (instance:prototype ()).to_be "bag" + - it supports method calls: + expect (bag:add "foo").to_be (bag) + expect (bag.foo).to_be (1) + + +# Metatable propagation is an important property of Object cloning, +# because Lua will only call __lt and __le metamethods when both +# arguments share the same metatable - i.e. the previous behaviour +# of making each object its own metatable precluded ever being able +# to use __lt and __le! +- describe object metatable propagation: + - before: root_mt = getmetatable (Object) + + - context with no custom metamethods: + - it inherits prototype object metatable: + instance = Object {} + expect (getmetatable (instance)).to_be (root_mt) + - it propagates prototype metatable to derived instances: + Derived = Object {_type = "Derived"} + instance = Derived {} + expect (getmetatable (Derived)).not_to_be (root_mt) + expect (getmetatable (instance)).to_be (getmetatable (Derived)) + - context with custom metamethods: + - before: + bag = Object { + _type = "bag", + __lt = function (a, b) return a[1] < b[1] end, + } + - it has it's own metatable: + expect (getmetatable (bag)).not_to_be (root_mt) + - it propagates prototype metatable to derived instances: + instance = bag {} + expect (getmetatable (instance)).to_be (getmetatable (bag)) + - it supports __lt calls: | + a, b = bag {"a"}, bag {"b"} + expect (a < b).to_be (true) + expect (a < a).to_be (false) + expect (a > b).to_be (false) + + +- describe __tostring: + - before: + obj = Object {_type = "Derived", "one", "two", "three"} + - it returns a string: + expect (type (tostring (obj))).to_be "string" + - it contains the type: + expect (tostring (Object {})).to_contain "Object" + expect (tostring (obj)).to_contain (prototype (obj)) + - it contains the ordered array part elements: + expect (tostring (obj)).to_contain "one, two, three" + - it contains the ordered dictionary part elements: + expect (tostring (Object {one = true, two = true, three = true})). + to_contain "one=true, three=true, two=true" + expect (tostring (obj {one = true, two = true, three = true})). + to_contain "one=true, three=true, two=true" + - it contains a ';' separator only when object has array and dictionary parts: + expect (tostring (obj)).not_to_contain ";" + expect (tostring (Object {one = true, two = true, three = true})). + not_to_contain ";" + expect (tostring (obj {one = true, two = true, three = true})). + to_contain ";" diff --git a/specs/operator_spec.yaml b/specs/operator_spec.yaml new file mode 100644 index 0000000..1b66084 --- /dev/null +++ b/specs/operator_spec.yaml @@ -0,0 +1,227 @@ +before: | + this_module = "std.operator" + global_table = "_G" + + M = require (this_module) + +specify std.operator: +- 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 {} + + - context via the std module: + - it does not touch the global table: + expect (show_apis {added_to=global_table, by="std"}). + to_equal {} + + +- describe concat: + - before: + f = M.concat + + - it stringifies its arguments: + expect (f (1, "")).to_be "1" + expect (f ("", 2)).to_be "2" + - it concatenates its arguments: + expect (f (1, 2)).to_be "12" + +- describe get: + - before: + f = M.get + + - it dereferences a table: + expect (f ({}, 1)).to_be (nil) + expect (f ({"foo", "bar"}, 1)).to_be "foo" + expect (f ({foo = "bar"}, "foo")).to_be "bar" + +- describe set: + - before: + f = M.set + + - it sets a table entry: + expect (f ({}, 1, 42)).to_equal {42} + expect (f ({}, "foo", 42)).to_equal {foo=42} + - it overwrites an existing entry: + expect (f ({1, 2}, 1, 42)).to_equal {42, 2} + expect (f ({foo="bar", baz="quux"}, "foo", 42)). + to_equal {foo=42, baz="quux"} + +- describe sum: + - before: + f = M.sum + + - it returns the sum of its arguments: + expect (f (99, 2)).to_be (99 + 2) + +- describe diff: + - before: + f = M.diff + + - it returns the difference of its arguments: + expect (f (99, 2)).to_be (99 - 2) + +- describe prod: + - before: + f = M.prod + + - it returns the product of its arguments: + expect (f (99, 2)).to_be (99 * 2) + +- describe quot: + - before: + f = M.quot + + - it returns the quotient of its arguments: + expect (f (99, 2)).to_be (99 / 2) + +- describe mod: + - before: + f = M.mod + + - it returns the modulus of its arguments: + expect (f (99, 2)).to_be (99 % 2) + +- describe pow: + - before: + f = M.pow + + - it returns the power of its arguments: + expect (f (99, 2)).to_be (99 ^ 2) + +- describe conj: + - before: + f = M.conj + + - it returns the logical and of its arguments: + expect (f (false, false)).to_be (false) + expect (f (false, true)).to_be (false) + expect (f (true, false)).to_be (false) + expect (f (true, true)).to_be (true) + - it supports truthy and falsey arguments: + expect (f ()).to_be (nil) + expect (f (0)).to_be (nil) + expect (f (nil, 0)).to_be (nil) + expect (f (0, "false")).to_be ("false") + +- describe disj: + - before: + f = M.disj + + - it returns the logical or of its arguments: + expect (f (false, false)).to_be (false) + expect (f (false, true)).to_be (true) + expect (f (true, false)).to_be (true) + expect (f (true, true)).to_be (true) + - it supports truthy and falsey arguments: + expect (f ()).to_be (nil) + expect (f (0)).to_be (0) + expect (f (nil, 0)).to_be (0) + expect (f (0, "false")).to_be (0) + +- describe neg: + - before: + f = M.neg + + - it returns the logical not of its argument: + expect (f (false)).to_be (true) + expect (f (true)).to_be (false) + - it supports truthy and falsey arguments: + expect (f ()).to_be (true) + expect (f (0)).to_be (false) + +- describe eq: + - before: + f = M.eq + + - it returns true if the arguments are equal: + expect (f ()).to_be (true) + expect (f ("foo", "foo")).to_be (true) + - it returns false if the arguments are unequal: + expect (f (1)).to_be (false) + expect (f ("foo", "bar")).to_be (false) + +- describe neq: + - before: + f = M.neq + + - it returns false if the arguments are equal: + expect (f (1, 1)).to_be (false) + expect (f ("foo", "foo")).to_be (false) + - it returns true if the arguments are unequal: + expect (f (1)).to_be (true) + expect (f ("foo", "bar")).to_be (true) + expect (f ({}, {})).to_be (true) + +- describe lt: + - before: + f = M.lt + + - it returns true if the arguments are in ascending order: + expect (f (1, 2)).to_be (true) + expect (f ("a", "b")).to_be (true) + - it returns false if the arguments are not in ascending order: + expect (f (2, 2)).to_be (false) + expect (f (3, 2)).to_be (false) + expect (f ("b", "b")).to_be (false) + expect (f ("c", "b")).to_be (false) + - it supports __lt metamethods: + List = require "std.list" {} + expect (f (List {1, 2, 3}, List {1, 2, 3, 4})).to_be (true) + expect (f (List {1, 2, 3}, List {1, 2, 3})).to_be (false) + expect (f (List {1, 2, 4}, List {1, 2, 3})).to_be (false) + +- describe lte: + - before: + f = M.lte + + - it returns true if the arguments are not in descending order: + expect (f (1, 2)).to_be (true) + expect (f (2, 2)).to_be (true) + expect (f ("a", "b")).to_be (true) + expect (f ("b", "b")).to_be (true) + - it returns false if the arguments are in descending order: + expect (f (3, 2)).to_be (false) + expect (f ("c", "b")).to_be (false) + - it supports __lte metamethods: + List = require "std.list" {} + expect (f (List {1, 2, 3}, List {1, 2, 3, 4})).to_be (true) + expect (f (List {1, 2, 3}, List {1, 2, 3})).to_be (true) + expect (f (List {1, 2, 4}, List {1, 2, 3})).to_be (false) + +- describe gt: + - before: + f = M.gt + + - it returns true if the arguments are in descending order: + expect (f (2, 1)).to_be (true) + expect (f ("b", "a")).to_be (true) + - it returns false if the arguments are not in descending order: + expect (f (2, 2)).to_be (false) + expect (f (2, 3)).to_be (false) + expect (f ("b", "b")).to_be (false) + expect (f ("b", "c")).to_be (false) + - it supports __lt metamethods: + List = require "std.list" {} + expect (f (List {1, 2, 3, 4}, List {1, 2, 3})).to_be (true) + expect (f (List {1, 2, 3}, List {1, 2, 3})).to_be (false) + expect (f (List {1, 2, 3}, List {1, 2, 4})).to_be (false) + +- describe gte: + - before: + f = M.gte + + - it returns true if the arguments are not in ascending order: + expect (f (2, 1)).to_be (true) + expect (f (2, 2)).to_be (true) + expect (f ("b", "a")).to_be (true) + expect (f ("b", "b")).to_be (true) + - it returns false if the arguments are in ascending order: + expect (f (2, 3)).to_be (false) + expect (f ("b", "c")).to_be (false) + - it supports __lte metamethods: + List = require "std.list" {} + expect (f (List {1, 2, 3, 4}, List {1, 2, 3})).to_be (true) + expect (f (List {1, 2, 3}, List {1, 2, 3})).to_be (true) + expect (f (List {1, 2, 3}, List {1, 2, 4})).to_be (false) diff --git a/specs/optparse_spec.yaml b/specs/optparse_spec.yaml new file mode 100644 index 0000000..70f874e --- /dev/null +++ b/specs/optparse_spec.yaml @@ -0,0 +1,461 @@ +before: + hell = require "specl.shell" + +specify std.optparse: +- before: | + OptionParser = require "std.optparse" + + help = [[ + parseme (stdlib spec) 0α1 + + Copyright © 2015 Gary V. Vaughan + This test program comes with ABSOLUTELY NO WARRANTY. + + Usage: parseme [] ... + + Banner text. + + Long description. + + Options: + + -h, --help display this help, then exit + --version display version information, then exit + -b a short option with no long option + --long a long option with no short option + --another-long a long option with internal hypen + --true a Lua keyword as an option name + -v, --verbose a combined short and long option + -n, --dryrun, --dry-run several spellings of the same option + -u, --name=USER require an argument + -o, --output=[FILE] accept an optional argument + -- end of options + + Footer text. + + Please report bugs at . + ]] + + -- strip off the leading whitespace required for YAML + parser = OptionParser (help:gsub ("^ ", "")) + +- context when required: + - context by name: + - it does not touch the global table: + expect (show_apis {added_to="_G", by="std.optparse"}). + to_equal {} + +- describe OptionParser: + - it recognises the program name: + expect (parser.program).to_be "parseme" + - it recognises the version number: + expect (parser.version).to_be "0α1" + - it recognises the version text: + expect (parser.versiontext). + to_match "^parseme .*Copyright .*NO WARRANTY%." + - it recognises the help text: | + expect (parser.helptext). + to_match ("^Usage: parseme .*Banner .*Long .*Options:.*" .. + "Footer .*/issues>%.") + - it diagnoses incorrect input text: + expect (OptionParser "garbage in").to_raise "argument must match" + +- describe parser: + - before: | + code = [[ + package.path = "]] .. package.path .. [[" + local OptionParser = require 'std.optparse' + local help = [=[]] .. help .. [[]=] + help = help:match ("^[%s\n]*(.-)[%s\n]*$") + + local parser = OptionParser (help) + local arg, opts = parser:parse (_G.arg) + + o = {} + for k, v in pairs (opts) do + table.insert (o, k .. " = " .. tostring (v)) + end + if #o > 0 then + table.sort (o) + print ("opts = { " .. table.concat (o, ", ") .. " }") + end + if #arg > 0 then + print ("args = { " .. table.concat (arg, ", ") .. " }") + end + ]] + parse = bind (luaproc, {code}) + + - it responds to --version with version text: + expect (parse {"--version"}). + to_match_output "^%s*parseme .*Copyright .*NO WARRANTY%.\n$" + - it responds to --help with help text: | + expect (parse {"--help"}). + to_match_output ("^%s*Usage: parseme .*Banner.*Long.*" .. + "Options:.*Footer.*/issues>%.\n$") + - it leaves behind unrecognised short options: + expect (parse {"-x"}).to_output "args = { -x }\n" + - it recognises short options: + expect (parse {"-b"}).to_output "opts = { b = true }\n" + - it leaves behind unrecognised options: + expect (parse {"--not-an-option"}). + to_output "args = { --not-an-option }\n" + - it recognises long options: + expect (parse {"--long"}).to_output "opts = { long = true }\n" + - it recognises long options with hyphens: + expect (parse {"--another-long"}). + to_output "opts = { another_long = true }\n" + - it recognises long options named after Lua keywords: + expect (parse {"--true"}).to_output "opts = { true = true }\n" + - it recognises combined short and long option specs: + expect (parse {"-v"}).to_output "opts = { verbose = true }\n" + expect (parse {"--verbose"}).to_output "opts = { verbose = true }\n" + - it recognises options with several spellings: + expect (parse {"-n"}).to_output "opts = { dry_run = true }\n" + expect (parse {"--dry-run"}).to_output "opts = { dry_run = true }\n" + expect (parse {"--dryrun"}).to_output "opts = { dry_run = true }\n" + - it recognises end of options marker: + expect (parse {"-- -n"}).to_output "args = { -n }\n" + - context given an unhandled long option: + - it leaves behind unmangled argument: + expect (parse {"--not-an-option=with-an-argument"}). + to_output "args = { --not-an-option=with-an-argument }\n" + - context given an option with a required argument: + - it records an argument to a long option following an '=' delimiter: + expect (parse {"--name=Gary"}). + to_output "opts = { name = Gary }\n" + - it records an argument to a short option without a space: + expect (parse {"-uGary"}). + to_output "opts = { name = Gary }\n" + - it records an argument to a long option following a space: + expect (parse {"--name Gary"}). + to_output "opts = { name = Gary }\n" + - it records an argument to a short option following a space: + expect (parse {"-u Gary"}). + to_output "opts = { name = Gary }\n" + - it diagnoses a missing argument: + expect (parse {"--name"}). + to_contain_error "'--name' requires an argument" + expect (parse {"-u"}). + to_contain_error "'-u' requires an argument" + - context given an option with an optional argument: + - it records an argument to a long option following an '=' delimiter: + expect (parse {"--output=filename"}). + to_output "opts = { output = filename }\n" + - it records an argument to a short option without a space: + expect (parse {"-ofilename"}). + to_output "opts = { output = filename }\n" + - it records an argument to a long option following a space: + expect (parse {"--output filename"}). + to_output "opts = { output = filename }\n" + - it records an argument to a short option following a space: + expect (parse {"-o filename"}). + to_output "opts = { output = filename }\n" + - it doesn't consume the following option: + expect (parse {"--output -v"}). + to_output "opts = { output = true, verbose = true }\n" + expect (parse {"-o -v"}). + to_output "opts = { output = true, verbose = true }\n" + - context when splitting combined short options: + - it separates non-argument options: + expect (parse {"-bn"}). + to_output "opts = { b = true, dry_run = true }\n" + expect (parse {"-vbn"}). + to_output "opts = { b = true, dry_run = true, verbose = true }\n" + - it stops separating at a required argument option: + expect (parse {"-vuname"}). + to_output "opts = { name = name, verbose = true }\n" + expect (parse {"-vuob"}). + to_output "opts = { name = ob, verbose = true }\n" + - it stops separating at an optional argument option: + expect (parse {"-vofilename"}). + to_output "opts = { output = filename, verbose = true }\n" + expect (parse {"-vobn"}). + to_output "opts = { output = bn, verbose = true }\n" + - it leaves behind unsplittable short options: + expect (parse {"-xvb"}).to_output "args = { -xvb }\n" + expect (parse {"-vxb"}).to_output "args = { -vxb }\n" + expect (parse {"-vbx"}).to_output "args = { -vbx }\n" + - it separates short options before unsplittable options: + expect (parse {"-vb -xvb"}). + to_output "opts = { b = true, verbose = true }\nargs = { -xvb }\n" + expect (parse {"-vb -vxb"}). + to_output "opts = { b = true, verbose = true }\nargs = { -vxb }\n" + expect (parse {"-vb -vbx"}). + to_output "opts = { b = true, verbose = true }\nargs = { -vbx }\n" + - it separates short options after unsplittable options: + expect (parse {"-xvb -vb"}). + to_output "opts = { b = true, verbose = true }\nargs = { -xvb }\n" + expect (parse {"-vxb -vb"}). + to_output "opts = { b = true, verbose = true }\nargs = { -vxb }\n" + expect (parse {"-vbx -vb"}). + to_output "opts = { b = true, verbose = true }\nargs = { -vbx }\n" + + - context with option defaults: + - before: | + function main (arg) + local OptionParser = require "std.optparse" + local parser = OptionParser ("program 0\nUsage: program\n" .. + " -x set x\n" .. + " -y set y\n" .. + " -z set z\n") + local state = { arg = {}, opts = { x={"t"}, y=false }} + state.arg, state.opts = parser:parse (arg, state.opts) + return state + end + - it prefers supplied argument: + expect (main {"-x", "-y"}). + to_equal { arg = {}, opts = { x=true, y=true }} + expect (main {"-x", "-y", "-z"}). + to_equal { arg = {}, opts = { x=true, y=true, z=true }} + expect (main {"-w", "-x", "-y"}). + to_equal { arg = {"-w"}, opts = { x=true, y=true }} + - it defers to default value: + expect (main {}). + to_equal { arg = {}, opts = { x={"t"}, y=false }} + expect (main {"-z"}). + to_equal { arg = {}, opts = { x={"t"}, y=false, z=true }} + expect (main {"-w"}). + to_equal { arg = {"-w"}, opts = { x={"t"}, y=false }} + + - context with io.die: + - before: | + runscript = function (code) + return luaproc ([[ + local OptionParser = require "std.optparse" + local parser = OptionParser ("program 0\nUsage: program\n") + _G.arg, _G.opts = parser:parse (_G.arg) + ]] .. code .. [[ + require "std.io".die "By 'eck!" + ]]) + end + - it prefers `prog.name` to `opts.program`: | + code = [[prog = { file = "file", name = "name" }]] + expect (runscript (code)).to_fail_while_matching ": name: By 'eck!\n" + - it prefers `prog.file` to `opts.program`: | + code = [[prog = { file = "file" }]] + expect (runscript (code)).to_fail_while_matching ": file: By 'eck!\n" + - it appends `prog.line` if any to `prog.file` over using `opts`: | + code = [[ + prog = { file = "file", line = 125 }; opts.line = 99]] + expect (runscript (code)). + to_fail_while_matching ": file:125: By 'eck!\n" + - it prefixes `opts.program` if any: | + expect (runscript ("")).to_fail_while_matching ": program: By 'eck!\n" + - it appends `opts.line` if any, to `opts.program`: | + code = [[opts.line = 99]] + expect (runscript (code)). + to_fail_while_matching ": program:99: By 'eck!\n" + + - context with io.warn: + - before: | + runscript = function (code) + return luaproc ([[ + local OptionParser = require "std.optparse" + local parser = OptionParser ("program 0\nUsage: program\n") + _G.arg, _G.opts = parser:parse (_G.arg) + ]] .. code .. [[ + require "std.io".warn "Ayup!" + ]]) + end + - it prefers `prog.name` to `opts.program`: | + code = [[prog = { file = "file", name = "name" }]] + expect (runscript (code)).to_output_error "name: Ayup!\n" + - it prefers `prog.file` to `opts.program`: | + code = [[prog = { file = "file" }]] + expect (runscript (code)).to_output_error "file: Ayup!\n" + - it appends `prog.line` if any to `prog.file` over using `opts`: | + code = [[ + prog = { file = "file", line = 125 }; opts.line = 99]] + expect (runscript (code)). + to_output_error "file:125: Ayup!\n" + - it prefixes `opts.program` if any: | + expect (runscript ("")).to_output_error "program: Ayup!\n" + - it appends `opts.line` if any, to `opts.program`: | + code = [[opts.line = 99]] + expect (runscript (code)). + to_output_error "program:99: Ayup!\n" + +- describe parser:on: + - before: | + function parseargs (onargstr, arglist) + code = [[ + package.path = "]] .. package.path .. [[" + local OptionParser = require 'std.optparse' + local help = [=[]] .. help .. [[]=] + help = help:match ("^[%s\n]*(.-)[%s\n]*$") + + local parser = OptionParser (help) + + parser:on (]] .. onargstr .. [[) + + _G.arg, _G.opts = parser:parse (_G.arg) + + o = {} + for k, v in pairs (opts) do + table.insert (o, k .. " = " .. tostring (v)) + end + if #o > 0 then + table.sort (o) + print ("opts = { " .. table.concat (o, ", ") .. " }") + end + if #arg > 0 then + print ("args = { " .. table.concat (arg, ", ") .. " }") + end + ]] + + return luaproc (code, arglist) + end + + - it recognises short options: + expect (parseargs ([["x"]], {"-x"})). + to_output "opts = { x = true }\n" + - it recognises long options: + expect (parseargs([["something"]], {"--something"})). + to_output "opts = { something = true }\n" + - it recognises long options with hyphens: + expect (parseargs([["some-thing"]], {"--some-thing"})). + to_output "opts = { some_thing = true }\n" + - it recognises long options named after Lua keywords: + expect (parseargs ([["if"]], {"--if"})). + to_output "opts = { if = true }\n" + - it recognises combined short and long option specs: + expect (parseargs ([[{"x", "if"}]], {"-x"})). + to_output "opts = { if = true }\n" + expect (parseargs ([[{"x", "if"}]], {"--if"})). + to_output "opts = { if = true }\n" + - it recognises options with several spellings: + expect (parseargs ([[{"x", "blah", "if"}]], {"-x"})). + to_output "opts = { if = true }\n" + expect (parseargs ([[{"x", "blah", "if"}]], {"--blah"})). + to_output "opts = { if = true }\n" + expect (parseargs ([[{"x", "blah", "if"}]], {"--if"})). + to_output "opts = { if = true }\n" + - it recognises end of options marker: + expect (parseargs ([["x"]], {"--", "-x"})). + to_output "args = { -x }\n" + - context given an option with a required argument: + - it records an argument to a short option without a space: + expect (parseargs ([["x", parser.required]], {"-y", "-xarg", "-b"})). + to_contain_output "opts = { b = true, x = arg }" + - it records an argument to a short option following a space: + expect (parseargs ([["x", parser.required]], {"-y", "-x", "arg", "-b"})). + to_contain_output "opts = { b = true, x = arg }\n" + - it records an argument to a long option following a space: + expect (parseargs ([["this", parser.required]], {"--this", "arg"})). + to_output "opts = { this = arg }\n" + - it records an argument to a long option following an '=' delimiter: + expect (parseargs ([["this", parser.required]], {"--this=arg"})). + to_output "opts = { this = arg }\n" + - it diagnoses a missing argument: + expect (parseargs ([[{"x", "this"}, parser.required]], {"-x"})). + to_contain_error "'-x' requires an argument" + expect (parseargs ([[{"x", "this"}, parser.required]], {"--this"})). + to_contain_error "'--this' requires an argument" + - context with a boolean handler function: + - it records a truthy argument: + for _, optarg in ipairs {"1", "TRUE", "true", "yes", "Yes", "y"} + do + expect (parseargs ([["x", parser.required, parser.boolean]], + {"-x", optarg})). + to_output "opts = { x = true }\n" + end + - it records a falsey argument: + for _, optarg in ipairs {"0", "FALSE", "false", "no", "No", "n"} + do + expect (parseargs ([["x", parser.required, parser.boolean]], + {"-x", optarg})). + to_output "opts = { x = false }\n" + end + - context with a file handler function: + - it records an existing file: + expect (parseargs ([["x", parser.required, parser.file]], + {"-x", "/dev/null"})). + to_output "opts = { x = /dev/null }\n" + - it diagnoses a missing file: | + expect (parseargs ([["x", parser.required, parser.file]], + {"-x", "/this/file/does/not/exist"})). + to_contain_error "error: /this/file/does/not/exist: " + - context with a custom handler function: + - it calls the handler: + expect (parseargs ([["x", parser.required, function (p,o,a) + return "custom" + end + ]], {"-x", "ignored"})). + to_output "opts = { x = custom }\n" + - it diagnoses a missing argument: + expect (parseargs ([["x", parser.required, function (p,o,a) + return "custom" + end + ]], {"-x"})). + to_contain_error "option '-x' requires an argument" + - context given an option with an optional argument: + - it records an argument to a short option without a space: + expect (parseargs ([["x", parser.optional]], {"-y", "-xarg", "-b"})). + to_contain_output "opts = { b = true, x = arg }" + - it records an argument to a short option following a space: + expect (parseargs ([["x", parser.optional]], {"-y", "-x", "arg", "-b"})). + to_contain_output "opts = { b = true, x = arg }\n" + - it records an argument to a long option following a space: + expect (parseargs ([["this", parser.optional]], {"--this", "arg"})). + to_output "opts = { this = arg }\n" + - it records an argument to a long option following an '=' delimiter: + expect (parseargs ([["this", parser.optional]], {"--this=arg"})). + to_output "opts = { this = arg }\n" + - it does't consume the following option: + expect (parseargs ([[{"x", "this"}, parser.optional]], {"-x", "-b"})). + to_output "opts = { b = true, this = true }\n" + expect (parseargs ([[{"x", "this"}, parser.optional]], {"--this", "-b"})). + to_output "opts = { b = true, this = true }\n" + - context with a boolean handler function: + - it records a truthy argument: + for _, optarg in ipairs {"1", "TRUE", "true", "yes", "Yes", "y"} + do + expect (parseargs ([["x", parser.optional, parser.boolean]], + {"-x", optarg})). + to_output "opts = { x = true }\n" + end + - it records a falsey argument: + for _, optarg in ipairs {"0", "FALSE", "false", "no", "No", "n"} + do + expect (parseargs ([["x", parser.optional, parser.boolean]], + {"-x", optarg})). + to_output "opts = { x = false }\n" + end + - it defaults to a truthy value: + expect (parseargs ([["x", parser.optional, parser.boolean]], + {"-x", "-b"})). + to_output "opts = { b = true, x = true }\n" + - context with a file handler function: + - it records an existing file: + expect (parseargs ([["x", parser.optional, parser.file]], + {"-x", "/dev/null"})). + to_output "opts = { x = /dev/null }\n" + - it diagnoses a missing file: | + expect (parseargs ([["x", parser.optional, parser.file]], + {"-x", "/this/file/does/not/exist"})). + to_contain_error "error: /this/file/does/not/exist: " + - context with a custom handler function: + - it calls the handler: + expect (parseargs ([["x", parser.optional, function (p,o,a) + return "custom" + end + ]], {"-x", "ignored"})). + to_output "opts = { x = custom }\n" + - it does not consume a following option: + expect (parseargs ([["x", parser.optional, function (p,o,a) + return a or "default" + end + ]], {"-x", "-b"})). + to_output "opts = { b = true, x = default }\n" + - context when splitting combined short options: + - it separates non-argument options: + expect (parseargs ([["x"]], {"-xb"})). + to_output "opts = { b = true, x = true }\n" + expect (parseargs ([["x"]], {"-vxb"})). + to_output "opts = { b = true, verbose = true, x = true }\n" + - it stops separating at a required argument option: + expect (parseargs ([[{"x", "this"}, parser.required]], {"-bxbit"})). + to_output "opts = { b = true, this = bit }\n" + - it stops separating at an optional argument option: + expect (parseargs ([[{"x", "this"}, parser.optional]], {"-bxbit"})). + to_output "opts = { b = true, this = bit }\n" diff --git a/specs/package_spec.yaml b/specs/package_spec.yaml new file mode 100644 index 0000000..170cd5b --- /dev/null +++ b/specs/package_spec.yaml @@ -0,0 +1,193 @@ +before: | + base_module = "package" + this_module = "std.package" + global_table = "_G" + + extend_base = { "dirsep", "execdir", "find", "igmark", "insert", + "mappath", "normalize", "pathsep", "path_mark", + "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 path_mark: + expect (f "?.lua"). + to_be (catfile (".", M.path_mark .. ".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 path_mark: + expect (f ("?.lua", "x")). + to_be (catpath (catfile (".", M.path_mark .. ".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.path_mark, M.execdir, M.igmark) + ).to_contain (package.config) diff --git a/specs/set_spec.yaml b/specs/set_spec.yaml new file mode 100644 index 0000000..ac87faf --- /dev/null +++ b/specs/set_spec.yaml @@ -0,0 +1,292 @@ +before: + Set = require "std.set" + prototype = require "std.object".prototype + s = Set {"foo", "bar", "bar"} + +specify std.set: +- describe require: + - it does not perturb the global namespace: + expect (show_apis {added_to="_G", by="std.set"}). + to_equal {} + + +- describe construction: + - it constructs a new set: + s = Set {} + expect (s).not_to_be (Set) + expect (prototype (s)).to_be "Set" + - it initialises set with constructor parameters: + t = Set {"foo", "bar", "bar"} + expect (t).to_equal (s) + - it serves as a prototype for new instances: + obj = s {} + expect (prototype (obj)).to_be "Set" + expect (obj).to_equal (s) + expect (getmetatable (obj)).to_be (getmetatable (s)) + + +- describe delete: + - context when called as a Set module function: + - before: + s = Set {"foo", "bar", "baz"} + - it returns a set object: + expect (prototype (Set.delete (s, "foo"))).to_be "Set" + - it is destructive: + Set.delete (s, "bar") + expect (s).not_to_have_member "bar" + - it returns the modified set: + expect (Set.delete (s, "baz")).not_to_have_member "baz" + - it ignores removal of non-members: | + clone = s {} + expect (Set.delete (s, "quux")).to_equal (clone) + - it deletes a member from the set: + expect (s).to_have_member "bar" + Set.delete (s, "bar") + expect (s).not_to_have_member "bar" + - it works with an empty set: + expect (Set.delete (Set {}, "quux")).to_equal (Set {}) + + +- describe difference: + - before: + r = Set {"foo", "bar", "baz"} + s = Set {"bar", "baz", "quux"} + + - context when called as a Set module function: + - it returns a set object: + expect (prototype (Set.difference (r, s))).to_be "Set" + - it is non-destructive: + Set.difference (r, s) + expect (r).to_equal (Set {"foo", "bar", "baz"}) + expect (s).to_equal (Set {"bar", "baz", "quux"}) + - it returns a set containing members of the first that are not in the second: + expect (Set.difference (r, s)).to_equal (Set {"foo"}) + - context when called as a set metamethod: + - it returns a set object: + expect (prototype (r - s)).to_be "Set" + - it is non-destructive: + q = r - s + expect (r).to_equal (Set {"foo", "bar", "baz"}) + expect (s).to_equal (Set {"bar", "baz", "quux"}) + - it returns a set containing members of the first that are not in the second: + expect (r - s).to_equal (Set {"foo"}) + + +- describe elems: + - before: + s = Set {"foo", "bar", "baz"} + + - context when called as a Set module function: + - it is an iterator over set members: + t = {} + for e in Set.elems (s) do table.insert (t, e) end + table.sort (t) + expect (t).to_equal {"bar", "baz", "foo"} + - it works for an empty set: + t = {} + for e in Set.elems (Set {}) do table.insert (t, e) end + expect (t).to_equal {} + + +- describe insert: + - context when called as a Set module function: + - before: + s = Set {"foo"} + - it returns a set object: + expect (prototype (Set.insert (s, "bar"))).to_be "Set" + - it is destructive: + Set.insert (s, "bar") + expect (s).to_have_member "bar" + - it returns the modified set: + expect (Set.insert (s, "baz")).to_have_member "baz" + - it ignores insertion of existing members: + expect (Set.insert (s, "foo")).to_equal (Set {"foo"}) + - it inserts a new member into the set: + expect (s).not_to_have_member "bar" + Set.insert (s, "bar") + expect (s).to_have_member "bar" + - it works with an empty set: + expect (Set.insert (Set {}, "foo")).to_equal (s) + - context when called as a set metamethod: + - before: + s = Set {"foo"} + - it returns a set object: + s["bar"] = true + expect (prototype (s)).to_be "Set" + - it is destructive: + s["bar"] = true + expect (s).to_have_member "bar" + - it ignores insertion of existing members: + s["foo"] = true + expect (s).to_equal (Set {"foo"}) + - it inserts a new member into the set: + expect (s).not_to_have_member "bar" + s["bar"] = true + expect (s).to_have_member "bar" + - it works with an empty set: + s = Set {} + s.foo = true + expect (s).to_equal (s) + + +- describe intersection: + - before: + r = Set {"foo", "bar", "baz"} + s = Set {"bar", "baz", "quux"} + + - context when called as a Set module function: + - it returns a set object: + expect (prototype (Set.intersection (r, s))).to_be "Set" + - it is non-destructive: + Set.intersection (r, s) + expect (r).to_equal (Set {"foo", "bar", "baz"}) + expect (s).to_equal (Set {"bar", "baz", "quux"}) + - it returns a set containing members common to both arguments: + expect (Set.intersection (r, s)). + to_equal (Set {"bar", "baz"}) + - context when called as a set metamethod: + - it returns a set object: + q = r * s + expect (prototype (q)).to_be "Set" + - it is non-destructive: + q = r * s + expect (r).to_equal (Set {"foo", "bar", "baz"}) + expect (s).to_equal (Set {"bar", "baz", "quux"}) + - it returns a set containing members common to both arguments: + expect (r * s).to_equal (Set {"bar", "baz"}) + + +- describe member: + - before: s = Set {"foo", "bar"} + + - context when called as a Set module function: + - it succeeds when set contains the given member: + expect (Set.member (s, "foo")).to_be (true) + - it fails when set does not contain the given member: + expect (Set.member (s, "baz")).not_to_be (true) + - it works with the empty set: + s = Set {} + expect (Set.member (s, "foo")).not_to_be (true) + - context when called as a set metamethod: + - it succeeds when set contains the given member: + expect (s["foo"]).to_be (true) + - it fails when set does not contain the given member: + expect (s["baz"]).not_to_be (true) + - it works with the empty set: + s = Set {} + expect (s["foo"]).not_to_be (true) + + +- describe proper_subset: + - before: + r = Set {"foo", "bar", "baz"} + s = Set {"bar", "baz"} + + - context when called as a Set module function: + - it succeeds when set contains all elements of another: + expect (Set.proper_subset (s, r)).to_be (true) + - it fails when two sets are equal: + r = s {} + expect (Set.proper_subset (s, r)).to_be (false) + - it fails when set does not contain all elements of another: + s = s + Set {"quux"} + expect (Set.proper_subset (r, s)).to_be (false) + - context when called as a set metamethod: + - it succeeds when set contains all elements of another: + expect (s < r).to_be (true) + - it fails when two sets are equal: + r = s {} + expect (s < r).to_be (false) + - it fails when set does not contain all elements of another: + s = s + Set {"quux"} + expect (r < s).to_be (false) + + +- describe subset: + - before: + r = Set {"foo", "bar", "baz"} + s = Set {"bar", "baz"} + + - context when called as a Set module function: + - it succeeds when set contains all elements of another: + expect (Set.subset (s, r)).to_be (true) + - it succeeds when two sets are equal: + r = s {} + expect (Set.subset (s, r)).to_be (true) + - it fails when set does not contain all elements of another: + s = s + Set {"quux"} + expect (Set.subset (r, s)).to_be (false) + - context when called as a set metamethod: + - it succeeds when set contains all elements of another: + expect (s <= r).to_be (true) + - it succeeds when two sets are equal: + r = s {} + expect (s <= r).to_be (true) + - it fails when set does not contain all elements of another: + s = s + Set {"quux"} + expect (r <= s).to_be (false) + + +- describe symmetric_difference: + - before: + r = Set {"foo", "bar", "baz"} + s = Set {"bar", "baz", "quux"} + + - context when called as a Set module function: + - it returns a set object: + expect (prototype (Set.symmetric_difference (r, s))). + to_be "Set" + - it is non-destructive: + Set.symmetric_difference (r, s) + expect (r).to_equal (Set {"foo", "bar", "baz"}) + expect (s).to_equal (Set {"bar", "baz", "quux"}) + - it returns a set containing members in only one argument set: + expect (Set.symmetric_difference (r, s)). + to_equal (Set {"foo", "quux"}) + - context when called as a set metamethod: + - it returns a set object: + expect (prototype (r / s)).to_be "Set" + - it is non-destructive: + q = r / s + expect (r).to_equal (Set {"foo", "bar", "baz"}) + expect (s).to_equal (Set {"bar", "baz", "quux"}) + - it returns a set containing members in only one argument set: + expect (r / s).to_equal (Set {"foo", "quux"}) + + +- describe union: + - before: + r = Set {"foo", "bar", "baz"} + s = Set {"bar", "baz", "quux"} + + - context when called as a Set module function: + - it returns a set object: + expect (prototype (Set.union (r, s))).to_be "Set" + - it is non-destructive: + Set.union (r, s) + expect (r).to_equal (Set {"foo", "bar", "baz"}) + expect (s).to_equal (Set {"bar", "baz", "quux"}) + - it returns a set containing members in only one argument set: + expect (Set.union (r, s)). + to_equal (Set {"foo", "bar", "baz", "quux"}) + - context when called as a set metamethod: + - it returns a set object: + expect (prototype (r + s)).to_be "Set" + - it is non-destructive: + q = r + s + expect (r).to_equal (Set {"foo", "bar", "baz"}) + expect (s).to_equal (Set {"bar", "baz", "quux"}) + - it returns a set containing members in only one argument set: + expect (r + s).to_equal (Set {"foo", "bar", "baz", "quux"}) + + +- describe __tostring: + - before: + s = Set {"foo", "bar", "baz"} + + - it returns a string: + expect (type (tostring (s))).to_be "string" + - it shows the type name: + expect (tostring (s)).to_contain "Set" + - it contains the ordered set elements: + expect (tostring (s)).to_contain "bar, baz, foo" diff --git a/specs/spec_helper.lua b/specs/spec_helper.lua new file mode 100644 index 0000000..9c60717 --- /dev/null +++ b/specs/spec_helper.lua @@ -0,0 +1,307 @@ +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" + + +-- Tweak _DEBUG without tripping over Specl nested environments. +setdebug = require "std.debug"._setdebug + + +-- 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 + + +-- 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, i or 1, j or maxn (t)) +end + + +-- In case we're not using a bleeding edge release of Specl... +badargs.result = badargs.result or function (fname, i, want, got) + if want == nil then i, want = i - 1, i end -- numbers only for narg error + + 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:prototype, so that an unloadable base.lua doesn't +-- prevent everything else from working. +function prototype (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 + + +--- 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 from = ]] .. from .. [[ + local 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 from = ]] .. from .. [[ + local 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 before, after = {}, {} + local from = ]] .. from .. [[ + + 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/specs/specs.mk b/specs/specs.mk new file mode 100644 index 0000000..591b61c --- /dev/null +++ b/specs/specs.mk @@ -0,0 +1,38 @@ +# Specl specs make rules. + + +## ------ ## +## Specs. ## +## ------ ## + +SPECL_OPTS = --unicode + +## For compatibility with Specl < 11, std_spec.yaml has to be +## last, so that when `require "std"` leaks symbols into the +## Specl global environment, subsequent example blocks are not +## affected. + +specl_SPECS = \ + $(srcdir)/specs/container_spec.yaml \ + $(srcdir)/specs/debug_spec.yaml \ + $(srcdir)/specs/functional_spec.yaml \ + $(srcdir)/specs/io_spec.yaml \ + $(srcdir)/specs/list_spec.yaml \ + $(srcdir)/specs/math_spec.yaml \ + $(srcdir)/specs/object_spec.yaml \ + $(srcdir)/specs/operator_spec.yaml \ + $(srcdir)/specs/optparse_spec.yaml \ + $(srcdir)/specs/package_spec.yaml \ + $(srcdir)/specs/set_spec.yaml \ + $(srcdir)/specs/strbuf_spec.yaml \ + $(srcdir)/specs/string_spec.yaml \ + $(srcdir)/specs/table_spec.yaml \ + $(srcdir)/specs/tree_spec.yaml \ + $(srcdir)/specs/std_spec.yaml \ + $(NOTHING_ELSE) + +EXTRA_DIST += \ + $(srcdir)/specs/spec_helper.lua \ + $(NOTHING_ELSE) + +include build-aux/specl.mk diff --git a/specs/std_spec.yaml b/specs/std_spec.yaml new file mode 100644 index 0000000..6a88d8d --- /dev/null +++ b/specs/std_spec.yaml @@ -0,0 +1,591 @@ +before: | + this_module = "std" + global_table = "_G" + + exported_apis = { "assert", "barrel", "elems", "eval", "getmetamethod", + "ielems", "ipairs", "ireverse", "monkey_patch", "npairs", + "pairs", "require", "ripairs", "rnpairs", "tostring", + "version" } + + -- 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) + + +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.set + expect (lazy).to_be (require "std.set") + - it loads submodule functions on demand: + expect (M.object.prototype (M.set {"Lazy"})). + to_be "Set" + +- 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 barrel: + - before: + mt = { "file metatable" } + namespace = { + io = { + stdin = setmetatable ({}, mt), + stdout = setmetatable ({}, mt), + stderr = setmetatable ({}, mt), + }, + } + + f = M.barrel + + f (namespace) + + - context with bad arguments: + badargs.diagnose (f, "std.barrel (?table)") + + # Ideally, `.to_be (M)`, except that M is cloned from a nested context + # by Specl to prevent us from affecting any other examples, thus the + # address is different by now. Also, we have to force lazy-loading in + # our copy of M to match apis already loaded in barrel. + - it returns std module table: + t = f(namespace) + for k, v in pairs(t) do + if type(v) ~= 'function' then + _ = M[k] + end + end + expect (f (namespace)).to_equal (M) + - it installs std monkey patches: + for _, api in ipairs (exported_apis) do + if type (M[api]) == "function" and + api ~= "barrel" and api ~= "monkey_patch" + then + expect (namespace[api]).to_be (M[api]) + end + end + - it installs std.io monkey patches: + for _, api in ipairs { "catdir", "catfile", "die", "monkey_patch", + "process_files", "readlines", "shell", "slurp", "splitdir", "warn", + "writelines" } + do + expect (namespace.io[api]).to_be (M.io[api]) + end + expect (mt.readlines).to_be (M.io.readlines) + expect (mt.writelines).to_be (M.io.writelines) + - it installs std.math monkey patches: + for _, api in ipairs { "floor", "monkey_patch", "round" } do + expect (namespace.math[api]).to_be (M.math[api]) + end + - it installs std.string monkey patches: + # FIXME: string metatable monkey-patches leak out! + mt = getmetatable "" + expect (mt.__concat).to_be (M.string.__concat) + expect (mt.__index).to_be (M.string.__index) + + for _, api in ipairs { "__concat", "__index", "caps", "chomp", + "escape_pattern", "escape_shell", "finds", "format", "ltrim", + "monkey_patch", "numbertosi", "ordinal_suffix", "pad", "pickle", + "prettytostring", "render", "rtrim", "split", "tfind", "trim", + "wrap" } + do + expect (namespace.string[api]).to_be (M.string[api]) + end + - it installs std.table monkey patches: + for _, api in ipairs { "clone", "clone_select", "depair", "empty", + "enpair", "flatten", "insert", "invert", "keys", "len", "maxn", + "merge", "merge_select", "monkey_patch", "new", "pack", "project", + "shape", "size", "sort", "values" } + do + expect (namespace.table[api]).to_be (M.table[api]) + end + - it scribbles backwards compatibility apis: + for api, fn in pairs { + bind = M.functional.bind, + collect = M.functional.collect, + compose = M.functional.compose, + curry = M.functional.curry, + die = M.io.die, + filter = M.functional.filter, + fold = M.functional.fold, + id = M.functional.id, + ileaves = M.tree.ileaves, + inodes = M.tree.inodes, + leaves = M.tree.leaves, + map = M.functional.map, + metamethod = M.getmetamethod, + nodes = M.tree.nodes, + op = M.operator, + pack = M.table.pack, + pickle = M.string.pickle, + prettytostring = M.string.prettytostring, + render = M.string.render, + require_version = M.require, + warn = M.io.warn, + } do + expect (namespace[api]).to_be (fn) + end + - 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.strbuf + expect (lazy).to_be (require "std.strbuf") + - it loads submodule functions on demand: + expect (M.object.prototype (M.strbuf {"Lazy"})). + to_be "StrBuf" + + +- 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 () end + t = setmetatable ({}, { _type = "table", _method = method }) + - it returns nil for missing metamethods: + expect (f (t, "not a metamethod on t")).to_be (nil) + - it returns nil for non-function metatable entries: + expect (f (t, "_type")).to_be (nil) + - it returns a method from the metatable: + expect (f (t, "_method")).to_be (method) + + - context with an object: + - before: + Object = require "std.object" + objmethod = function () end + obj = Object { + _type = "DerivedObject", + _method = objmethod, + } + - it returns nil for missing metamethods: + expect (f (obj, "not a metamethod on obj")).to_be (nil) + - it returns nil for non-function metatable entries: + expect (f (obj, "_type")).to_be (nil) + - it returns a method from the metatable: + expect (f (obj, "_method")).to_be (objmethod) + + +- 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 ireverse: + - before: + f = M.ireverse + + - context with bad arguments: + badargs.diagnose (f, "std.ireverse (table)") + + - it returns a new list: + t = {1, 2, 5} + expect (f (t)).not_to_be (t) + - it reverses the elements relative to the original list: + expect (f {1, 2, "five"}).to_equal {"five", 2, 1} + - it ignores the dictionary part of a table: + expect (f {1, 2, "five"; a = "b", c = "d"}).to_equal {"five", 2, 1} + - it respects __len metamethod: + expect (f (__index)).to_equal {"g", "n", "i", "r", "t", "s", " ", "a"} + - it works for an empty list: + expect (f {}).to_equal {} + + +- describe monkey_patch: + - before: + io_mt = {} + t = { + io = { + stdin = setmetatable ({}, io_mt), + stdout = setmetatable ({}, io_mt), + stderr = setmetatable ({}, io_mt), + }, + math = {}, + table = {}, + } + + f = M.monkey_patch + + f (t) + + - context with bad arguments: + badargs.diagnose (f, "std.monkey_patch (?table)") + + # Ideally, `.to_be (M)`, except that M is cloned from a nested context + # by Specl to prevent us from affecting any other examples, thus the + # address is different by now. + - it returns std module table: + expect (f (t)).to_equal (M) + - it installs std module functions: + for _, v in ipairs (exported_apis) do + if type (M[v]) == "function" and v ~= "barrel" and v ~= "monkey_patch" then + expect (t[v]).to_be (M[v]) + end + end + + +- 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 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", "41")).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: + std = require "std" + M._VERSION, M.version = M.version, nil + expect (f ("std", "41", "9999")).to_be (require "std") + M._VERSION, M.version = nil, M._VERSION + - 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 works with the empty list: + t = {} + for k, v in f {} do t[k] = v end + expect (t).to_equal {} + + +- describe tostring: + - before: + f = M.tostring + + - context with bad arguments: + badargs.diagnose (f, "std.tostring (?any)") + + - it renders primitives exactly like system tostring: + expect (f (nil)).to_be (tostring (nil)) + expect (f (false)).to_be (tostring (false)) + expect (f (42)).to_be (tostring (42)) + expect (f (f)).to_be (tostring (f)) + expect (f "a string").to_be "a string" + - it renders empty tables as a pair of braces: + expect (f {}).to_be ("{}") + - it renders table array part compactly: + expect (f {"one", "two", "five"}). + to_be '{1=one,2=two,3=five}' + - it renders a table dictionary part compactly: + expect (f { one = true, two = 2, three = {3}}). + to_be '{one=true,three={1=3},two=2}' + - it renders table keys in table.sort order: + expect (f { one = 3, two = 5, three = 4, four = 2, five = 1 }). + to_be '{five=1,four=2,one=3,three=4,two=5}' + - it renders keys with invalid symbol names compactly: + expect (f { _ = 0, word = 0, ["?"] = 1, ["a-key"] = 1, ["[]"] = 1 }). + to_be '{?=1,[]=1,_=0,a-key=1,word=0}' diff --git a/specs/strbuf_spec.yaml b/specs/strbuf_spec.yaml new file mode 100644 index 0000000..88bbc07 --- /dev/null +++ b/specs/strbuf_spec.yaml @@ -0,0 +1,121 @@ +before: + object = require "std.object" + StrBuf = require "std.strbuf" + b = StrBuf {"foo", "bar"} + +specify std.strbuf: +- describe require: + - it does not perturb the global namespace: + expect (show_apis {added_to="_G", by="std.strbuf"}). + to_equal {} + + +- describe construction: + - context from StrBuf clone method: + - it constructs a new strbuf: + b = StrBuf:clone {} + expect (b).not_to_be (StrBuf) + expect (object.type (b)).to_be "StrBuf" + - it reuses the StrBuf metatable: + a, b = StrBuf:clone {"a"}, StrBuf:clone {"b"} + expect (getmetatable (a)).to_be (getmetatable (b)) + - it initialises strbuf with constructor parameters: + a = StrBuf:clone {"foo", "bar"} + expect (a).to_equal (b) + - it serves as a prototype for new instances: + obj = b:clone {} + expect (object.type (obj)).to_be "StrBuf" + expect (obj).to_equal (b) + expect (getmetatable (obj)).to_be (getmetatable (b)) + + # StrBuf {args} is just syntactic sugar for StrBuf:clone {args} + - context from StrBuf object prototype: + - it constructs a new strbuf: + b = StrBuf {} + expect (b).not_to_be (StrBuf) + expect (object.type (b)).to_be "StrBuf" + - it reuses the StrBuf metatable: + a, b = StrBuf {"a"}, StrBuf {"b"} + expect (getmetatable (a)).to_be (getmetatable (b)) + - it initialises strbuf with constructor parameters: + a = StrBuf:clone {"foo", "bar"} + expect (a).to_equal (b) + - it serves as a prototype for new instances: + obj = b {} + expect (object.type (obj)).to_be "StrBuf" + expect (obj).to_equal (b) + expect (getmetatable (obj)).to_be (getmetatable (b)) + + +- describe tostring: + - context as a module function: + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (StrBuf.tostring, {b})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (StrBuf.tostring, {b})). + not_to_contain_error "was deprecated" + - it returns buffered string: + expect (StrBuf.tostring (b)).to_be "foobar" + + - context as an object method: + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (b.tostring, {b})). + to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (b.tostring, {b})). + not_to_contain_error "was deprecated" + - it returns buffered string: + expect (b:tostring ()).to_be "foobar" + + - context as a metamethod: + - it returns buffered string: + expect (tostring (b)).to_be "foobar" + + +- describe concat: + - before: + a = StrBuf {"foo", "bar"} + b = StrBuf {"baz", "quux"} + + - context as a module function: + - it appends a string: + a = StrBuf.concat (a, "baz") + expect (object.type (a)).to_be "StrBuf" + expect (tostring (a)).to_be "foobarbaz" + - it appends a StrBuf: + a = StrBuf.concat (a, b) + expect (object.type (a)).to_be "StrBuf" + expect (tostring (a)).to_be "foobarbazquux" + - context as an object method: + - it appends a string: + a = a:concat "baz" + expect (object.type (a)).to_be "StrBuf" + expect (tostring(a)).to_be "foobarbaz" + - it appends a StrBuf: + a = a:concat (b) + expect (object.type (a)).to_be "StrBuf" + expect (tostring (a)).to_be "foobarbazquux" + - context as a metamethod: + - it appends a string: + a = a .. "baz" + expect (object.type (a)).to_be "StrBuf" + expect (tostring (a)).to_be "foobarbaz" + - it appends a StrBuf: + a = a .. b + expect (object.type (a)).to_be "StrBuf" + expect (tostring (a)).to_be "foobarbazquux" + - it stringifies lazily: + a = StrBuf {1} + b = StrBuf {a, "five"} + a = a:concat (2) + expect (tostring (b)).to_be "12five" + b = StrBuf {tostring (a), "five"} + a = a:concat (3) + expect (tostring (b)).to_be "12five" + - it can be non-destructive: + a = StrBuf {1} + b = a {} .. 2 + expect (tostring (a)).to_be "1" diff --git a/specs/string_spec.yaml b/specs/string_spec.yaml new file mode 100644 index 0000000..7ac10ba --- /dev/null +++ b/specs/string_spec.yaml @@ -0,0 +1,745 @@ +before: + base_module = "string" + this_module = "std.string" + global_table = "_G" + + extend_base = { "__concat", "__index", + "caps", "chomp", "escape_pattern", "escape_shell", + "finds", "format", "ltrim", "monkey_patch", + "numbertosi", "ordinal_suffix", "pad", "pickle", + "prettytostring", "render", "rtrim", "split", + "tfind", "trim", "wrap" } + deprecations = { "assert", "require_version", "tostring" } + + 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: + apis = require "std.base".copy (extend_base) + for _, v in ipairs (deprecations) do + apis[#apis + 1] = v + end + expect (show_apis {from=base_module, not_in=this_module}). + to_contain.a_permutation_of (apis) + + - 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 (string.format ("%s%s", subject, require "std".tostring (argument))) + - it stringifies nil arguments: + argument = nil + expect (subject .. argument). + to_be (string.format ("%s%s", subject, require "std".tostring (argument))) + - it does not perturb the original subject: + original = subject + newstring = subject .. " concatenate something" + expect (subject).to_be (original) + + +- describe assert: + - before: + f = M.assert + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {"std.string"})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {"std.string"})).not_to_contain_error "was deprecated" + + - 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 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": + expect (f ()).to_raise ("string expected") + expect (f {"a table"}).to_raise ("string expected") + + +- 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 monkey_patch: + - before: + f = M.monkey_patch + + - context with bad arguments: + badargs.diagnose (f, "std.string.monkey_patch (?table)") + + # Ideally, `.to_be (M)`, except that M is cloned from a nested context + # by Specl to prevent us from affecting any other examples, thus the + # address is different by now. + - it returns std.string module table: + expect (f {}).to_equal (M) + - it injects std.string apis into given namespace: + namespace = {} + f (namespace) + for _, api in ipairs (extend_base) do + expect (namespace.string[api]).to_be (M[api]) + end + - it installs string metamethods: + # FIXME: string metatable monkey-patches leak out! + mt = getmetatable "" + expect (mt.__concat).to_be (M.__concat) + expect (mt.__index).to_be (M.__index) + + +- 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 pickle: + - before: + loadstring = loadstring or load + function unpickle (s) return loadstring ("return " .. s) () end + t = {1, {{2, 3}, 4, {5}}} + f = M.pickle + - it converts a primitive to a representative string: + expect (f (nil)).to_be "nil" + expect (f (false)).to_be "false" + expect (f (42)).to_be "42" + expect (f "string").to_be '"string"' + - it returns a loadable string that results in the original value: + expect (unpickle (f (nil))).to_be (nil) + expect (unpickle (f (false))).to_be (false) + expect (unpickle (f (42))).to_be (42) + expect (unpickle (f "string")).to_be "string" + - it converts a table to a representative string: + expect (f {"table", 42}).to_be '{[1]="table",[2]=42}' + - it returns a loadable string that results in the original table: + expect (unpickle (f {"table", 42})).to_equal {"table", 42} + - it converts a nested table to a representative string: + expect (f (t)). + to_be "{[1]=1,[2]={[1]={[1]=2,[2]=3},[2]=4,[3]={[1]=5}}}" + - it returns a loadable string that results in the original nested table: + expect (unpickle (f (t))).to_equal (t) + + +- 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 render: + - before: + term = function (s) return function () return s end end + pair = function (_, _, _, i, v) return i .. "=" .. v end + sep = function (_, i, _, j) return (i and j) and "," or "" end + r = function (x) + return M.render (x, term "{", term "}", tostring, pair, sep) + end + t = {1, {{2, 3}, 4, {5}}} + + f = M.render + + - context with bad arguments: + badargs.diagnose (f, "std.string.render (?any, func, func, func, func, func, ?table)") + + - it converts a primitive to a representative string: + expect (r (nil)).to_be "nil" + expect (r (false)).to_be "false" + expect (r (42)).to_be "42" + expect (r ("string")).to_be "string" + - it converts a table to a representative string: + expect (r ({"table", 42})).to_be '{1=table,2=42}' + - it converts a nested table to a representative string: + expect (r (t)). + to_be "{1=1,2={1={1=2,2=3},2=4,3={1=5}}}" + - it converts a recursive table to a representative string: + t[1] = t + expect (r (t)). + to_be ("{1="..tostring (t)..",2={1={1=2,2=3},2=4,3={1=5}}}") + + +- describe require_version: + - before: + f = M.require_version + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {"std.string"})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {"std.string"})).not_to_contain_error "was deprecated" + + - 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 () + - it diagnoses module too new: + expect (f ("std", "0", "0")).to_raise () + - 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", "41")).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: + std = require "std" + std._VERSION, std.version = std.version, nil + expect (f ("std", "41", "9999")).to_be (require "std") + std._VERSION, std.version = nil, std._VERSION + - 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 () + expect (f ("std", "1.3")).to_raise () + expect (f ("std", "2.1.2")).to_raise () + expect (f ("std", "2")).to_raise () + expect (f ("std", "1.2.10")).to_raise () + - it diagnoses module too new: + expect (f ("std", nil, "1.2.2")).to_raise () + expect (f ("std", nil, "1.1")).to_raise () + expect (f ("std", nil, "1.1.2")).to_raise () + expect (f ("std", nil, "1")).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", "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) + + +- 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 tostring: + - before: + f = M.tostring + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {"std.string"})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {"std.string"})).not_to_contain_error "was deprecated" + + - it renders primitives exactly like system tostring: + expect (f (nil)).to_be (tostring (nil)) + expect (f (false)).to_be (tostring (false)) + expect (f (42)).to_be (tostring (42)) + expect (f (f)).to_be (tostring (f)) + expect (f "a string").to_be "a string" + - it renders empty tables as a pair of braces: + expect (f {}).to_be ("{}") + - it renders table array part compactly: + expect (f {"one", "two", "five"}). + to_be '{1=one,2=two,3=five}' + - it renders a table dictionary part compactly: + expect (f { one = true, two = 2, three = {3}}). + to_be '{one=true,three={1=3},two=2}' + - it renders table keys in table.sort order: + expect (f { one = 3, two = 5, three = 4, four = 2, five = 1 }). + to_be '{five=1,four=2,one=3,three=4,two=5}' + - it renders keys with invalid symbol names compactly: + expect (f { _ = 0, word = 0, ["?"] = 1, ["a-key"] = 1, ["[]"] = 1 }). + to_be '{?=1,[]=1,_=0,a-key=1,word=0}' + + +- 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: + expect (f ()).to_raise ("string expected") + expect (f {"a table"}).to_raise ("string expected") diff --git a/specs/table_spec.yaml b/specs/table_spec.yaml new file mode 100644 index 0000000..6ff2cad --- /dev/null +++ b/specs/table_spec.yaml @@ -0,0 +1,772 @@ +before: | + base_module = "table" + this_module = "std.table" + global_table = "_G" + + extend_base = { "clone", "clone_select", "depair", "empty", + "enpair", "flatten", "insert", "invert", "keys", + "len", "maxn", "merge", "merge_select", + "monkey_patch", "new", "okeys", "pack", "project", + "remove", "shape", "size", "sort", "unpack", + "values" } + deprecations = { "clone_rename", "metamethod", "ripairs", "totable" } + + M = require "std.table" + + +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: + apis = require "std.base".copy (extend_base) + for _, v in ipairs (deprecations) do + apis[#apis + 1] = v + end + expect (show_apis {from=base_module, not_in=this_module}). + to_contain.a_permutation_of (apis) + + - 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_rename: + - before: + subject = { k1 = {"v1"}, k2 = {"v2"}, k3 = {"v3"} } + + fname = "clone_rename" + f = M[fname] + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {{}, subject})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {{}, subject})).not_to_contain_error "was deprecated" + + - it copies the subject: + expect (f ({}, subject)).to_copy (subject) + - it only makes a shallow copy: + expect (f ({}, subject).k2).to_be (subject.k2) + + - context when renaming some keys: + - before: + target = { newkey = subject.k1, k2 = subject.k2, k3 = subject.k3 } + - it renames during cloning: + expect (f ({k1 = "newkey"}, subject)).to_equal (target) + - it does not perturb the value in the renamed key field: + expect (f ({k1 = "newkey"}, subject).newkey).to_be (subject.k1) + + - it diagnoses non-table arguments: + expect (f {}).to_raise ("table expected") + expect (f ({}, "foo")).to_raise ("table expected") + + +- 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 (prototype (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 (prototype (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 flatten: + - before: + t = {{{"one"}}, "two", {{"three"}, "four"}} + + f = M.flatten + + - context with bad arguments: + badargs.diagnose (f, "std.table.flatten (table)") + + - it returns a table: + expect (type (f (t))).to_be "table" + - it works for an empty table: + expect (f {}).to_equal {} + - it flattens a nested table: + expect (f (t)).to_equal {"one", "two", "three", "four"} + + +- 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 len: + - before: + f = M.len + + - context with bad arguments: + badargs.diagnose (f, "std.table.len (table)") + + - it returns the length 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 elements after a hole: + expect (f {1, 2, [5]=3}).to_be (2) + - it respects __len metamethod: + t = setmetatable ({1, 2, [5]=3}, {__len = function () return 42 end}) + expect (f (t)).to_be (42) + + +- 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 metamethod: + - before: + Object = require "std.object" + objmethod = function () end + obj = Object { + _type = "DerivedObject", + _method = objmethod, + } + + f = M.metamethod + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {{}, subject})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {{}, subject})).not_to_contain_error "was deprecated" + + - it returns nil for missing metamethods: + expect (f (obj, "not a method on obj")).to_be (nil) + - it returns nil for non-function metatable entries: + expect (f (obj, "_type")).to_be (nil) + - it returns a method from the metatable: + expect (f (obj, "_method")).to_be (objmethod) + + +- describe monkey_patch: + - before: + f = M.monkey_patch + + - context with bad arguments: + badargs.diagnose (f, "std.table.monkey_patch (?table)") + + # Ideally, `.to_be (M)`, except that M is cloned from a nested context + # by Specl to prevent us from affecting any other examples, thus the + # address is different by now. + - it returns std.table module table: + expect (f {}).to_equal (M) + - it injects std.table apis into given namespace: + namespace = {} + f (namespace) + for _, api in ipairs (extend_base) do + expect (namespace.table[api]).to_be (M[api]) + end + + +- 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 okeys: + - before: + subject = { "v1", k1 = 1, "v2", k2 = 2, "v3", k3 = 3, [10]="v10" } + + f = M.okeys + + - context with bad arguments: + badargs.diagnose (f, "std.table.okeys (table)") + + - it returns an empty list when subject is empty: + expect (f {}).to_equal {} + - it makes an ordered list of table keys: + expect (f (subject)). + to_equal {1, 2, 3, 10, "k1", "k2", "k3"} + + +- describe pack: + - before: + unpack = unpack or table.unpack + t = {"one", "two", "five"} + f = M.pack + - it creates an empty table with no arguments: + expect (f ()).to_equal {} + - 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) + + +- 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 (prototype (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 ripairs: + - before: + f = M.ripairs + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {{}, subject})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {{}, subject})).not_to_contain_error "was deprecated" + + - 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 a 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"} + + +- describe shape: + - before: + l = {1, 2, 3, 4, 5, 6} + + f = M.shape + + - context with bad arguments: + badargs.diagnose (f, "std.table.shape (table, table)") + + - it returns a table: + expect (prototype (f ({2, 3}, l))).to_be "table" + - it works for an empty table: + expect (f ({0}, {})).to_equal ({}) + - it returns the result in a new table: + expect (f ({2, 3}, l)).not_to_be (l) + - it does not perturb the argument table: + f ({2, 3}, l) + expect (l).to_equal {1, 2, 3, 4, 5, 6} + - it reshapes a table according to given dimensions: + expect (f ({2, 3}, l)). + to_equal ({{1, 2, 3}, {4, 5, 6}}) + expect (f ({3, 2}, l)). + to_equal ({{1, 2}, {3, 4}, {5, 6}}) + - it treats 0-valued dimensions as an indefinite number: + expect (f ({2, 0}, l)). + to_equal ({{1, 2, 3}, {4, 5, 6}}) + expect (f ({0, 2}, l)). + to_equal ({{1, 2}, {3, 4}, {5, 6}}) + + +- 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 totable: + - before: + t = {"one", "two", "five"} + mt = { _type = "MockObject", + __totable = function (self) return self.content end } + + f = M.totable + + - it writes a deprecation warning: + setdebug { deprecate = "nil" } + expect (capture (f, {{}})).to_contain_error "was deprecated" + setdebug { deprecate = false } + expect (capture (f, {{}})).not_to_contain_error "was deprecated" + + - it calls object's __totable metamethod: + object = setmetatable ({content = t}, mt) + expect (f (object)).to_be (t) + - it returns a table with no __totable metamethod unchanged: + t = {content = t} + object = setmetatable (t, { _type = "Thing" }) + expect (f (object)).to_be (t) + + +- 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 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/specs/tree_spec.yaml b/specs/tree_spec.yaml new file mode 100644 index 0000000..025d7fb --- /dev/null +++ b/specs/tree_spec.yaml @@ -0,0 +1,409 @@ +before: | + global_table = "_G" + this_module = "std.tree" + + Tree = require "std.tree" + +specify std.tree: +- before: + prototype = (require "std.object").prototype + t = {foo="foo", fnord={branch={bar="bar", baz="baz"}}, quux="quux"} + tr = Tree (t) + +- 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 {} + + - context via the std module: + - it does not touch the global table: + expect (show_apis {added_to=global_table, by="std"}). + to_equal {} + +- describe construction: + - it constructs a new tree: + tr = Tree {} + expect (tr).not_to_be (Tree) + expect (prototype (tr)).to_be "Tree" + - it turns a table argument into a tree: + expect (prototype (Tree (t))).to_be "Tree" + - it does not turn table argument values into sub-Trees: + expect (prototype (tr["fnord"])).to_be "table" + - it understands branched nodes: + expect (tr).to_equal (Tree (t)) + expect (tr[{"fnord"}]).to_equal (t.fnord) + expect (tr[{"fnord", "branch", "bar"}]).to_equal (t.fnord.branch.bar) + - it serves as a prototype for new instances: + obj = tr {} + expect (prototype (obj)).to_be "Tree" + expect (obj).to_equal (tr) + expect (getmetatable (obj)).to_be (getmetatable (tr)) + + +- describe clone: + - before: + subject = { k1 = {"v1"}, k2 = {"v2"}, k3 = {"v3"} } + f = Tree.clone + - 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 makes a deep copy: + expect (f (subject).k1).not_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) + - it diagnoses non-table arguments: + expect (f ()).to_raise ("table expected") + expect (f "foo").to_raise ("table expected") + + +- describe ileaves: + - before: + f = Tree.ileaves + l = {} + - it iterates over array part of a table argument: + for v in f {"first", "second", "3rd"} do l[1+#l]=v end + expect (l).to_equal {"first", "second", "3rd"} + - it iterates over array parts of nested table argument: + for v in f {{"one", {"two"}, {{"three"}, "four"}}, "five"} do + l[1+#l]=v + end + expect (l).to_equal {"one", "two", "three", "four", "five"} + - it skips hash part of a table argument: + for v in f {"first", "second"; third = "2rd"} do l[1+#l]=v end + expect (l).to_equal {"first", "second"} + - it skips hash parts of nested table argument: + for v in f {{"one", {two=2}, {{"three"}, four=4}}, foo="bar", "five"} do + l[1+#l]=v + end + expect (l).to_equal {"one", "three", "five"} + - it works on trees too: + for v in f (Tree {Tree {"one", + Tree {two=2}, + Tree {Tree {"three"}, four=4} + }, + foo="bar", "five"}) + do + l[1+#l]=v + end + expect (l).to_equal {"one", "three", "five"} + - it diagnoses non-table arguments: + expect (f ()).to_raise ("table expected") + expect (f "string").to_raise ("table expected") + + +- describe inodes: + - before: | + f = Tree.inodes + local tostring = (require "std.string").tostring + + function traverse (subject) + l = {} + for ty, p, n in f (subject) do + l[1+#l]={ty, Tree.clone (p), n} + end + return l + end + - it iterates over array part of a table argument: | + subject = {"first", "second", "3rd"} + expect (traverse (subject)). + to_equal {{"branch", {}, subject}, -- { + {"leaf", {1}, subject[1]}, -- first, + {"leaf", {2}, subject[2]}, -- second, + {"leaf", {3}, subject[3]}, -- 3rd, + {"join", {}, subject}} -- } + - it iterates over array parts of nested table argument: | + subject = {{"one", {"two"}, {{"three"}, "four"}}, "five"} + expect (traverse (subject)). + to_equal {{"branch", {}, subject}, -- { + {"branch", {1}, subject[1]}, -- { + {"leaf", {1,1}, subject[1][1]}, -- one, + {"branch", {1,2}, subject[1][2]}, -- { + {"leaf", {1,2,1}, subject[1][2][1]}, -- two, + {"join", {1,2}, subject[1][2]}, -- }, + {"branch", {1,3}, subject[1][3]}, -- { + {"branch", {1,3,1}, subject[1][3][1]}, -- { + {"leaf", {1,3,1,1}, subject[1][3][1][1]}, -- three, + {"join", {1,3,1}, subject[1][3][1]}, -- }, + {"leaf", {1,3,2}, subject[1][3][2]}, -- four, + {"join", {1,3}, subject[1][3]}, -- }, + {"join", {1}, subject[1]}, -- }, + {"leaf", {2}, subject[2]}, -- five, + {"join", {}, subject}} -- } + - it skips hash part of a table argument: | + subject = {"first", "second"; third = "3rd"} + expect (traverse (subject)). + to_equal {{"branch", {}, subject}, -- { + {"leaf", {1}, subject[1]}, -- first, + {"leaf", {2}, subject[2]}, -- second, + {"join", {}, subject}} -- } + - it skips hash parts of nested table argument: | + subject = {{"one", {two=2}, {{"three"}, four=4}}, foo="bar", "five"} + expect (traverse (subject)). + to_equal {{"branch", {}, subject}, -- { + {"branch", {1}, subject[1]}, -- { + {"leaf", {1,1}, subject[1][1]}, -- one, + {"branch", {1,2}, subject[1][2]}, -- { + {"join", {1,2}, subject[1][2]}, -- }, + {"branch", {1,3}, subject[1][3]}, -- { + {"branch", {1,3,1}, subject[1][3][1]}, -- { + {"leaf", {1,3,1,1}, subject[1][3][1][1]}, -- three, + {"join", {1,3,1}, subject[1][3][1]}, -- }, + {"join", {1,3}, subject[1][3]}, -- }, + {"join", {1}, subject[1]}, -- }, + {"leaf", {2}, subject[2]}, -- five, + {"join", {}, subject}} -- } + - it works on trees too: | + subject = Tree {Tree {"one", + Tree {two=2}, + Tree {Tree {"three"}, four=4}}, + foo="bar", + "five"} + expect (traverse (subject)). + to_equal {{"branch", {}, subject}, -- { + {"branch", {1}, subject[1]}, -- { + {"leaf", {1,1}, subject[1][1]}, -- one, + {"branch", {1,2}, subject[1][2]}, -- { + {"join", {1,2}, subject[1][2]}, -- }, + {"branch", {1,3}, subject[1][3]}, -- { + {"branch", {1,3,1}, subject[1][3][1]}, -- { + {"leaf", {1,3,1,1}, subject[1][3][1][1]}, -- three, + {"join", {1,3,1}, subject[1][3][1]}, -- }, + {"join", {1,3}, subject[1][3]}, -- }, + {"join", {1}, subject[1]}, -- }, + {"leaf", {2}, subject[2]}, -- five, + {"join", {}, subject}} -- } + - it diagnoses non-table arguments: + expect (f ()).to_raise ("table expected") + expect (f "string").to_raise ("table expected") + + +- describe leaves: + - before: + f = Tree.leaves + l = {} + - it iterates over elements of a table argument: + for v in f {"first", "second", "3rd"} do l[1+#l]=v end + expect (l).to_equal {"first", "second", "3rd"} + - it iterates over elements of a nested table argument: + for v in f {{"one", {"two"}, {{"three"}, "four"}}, "five"} do + l[1+#l]=v + end + expect (l).to_equal {"one", "two", "three", "four", "five"} + - it includes the hash part of a table argument: + for v in f {"first", "second"; third = "3rd"} do l[1+#l]=v end + expect (l).to_equal {"first", "second", "3rd"} + - it includes hash parts of a nested table argument: + for v in f {{"one", {two=2}, {{"three"}, four=4}}, foo="bar", "five"} do + l[1+#l]=v + end + expect (l).to_contain. + a_permutation_of {"one", 2, "three", 4, "bar", "five"} + - it works on trees too: + for v in f (Tree {Tree {"one", + Tree {two=2}, + Tree {Tree {"three"}, four=4} + }, + foo="bar", "five"}) + do + l[1+#l]=v + end + expect (l).to_contain. + a_permutation_of {"one", 2, "three", 4, "bar", "five"} + - it diagnoses non-table arguments: + expect (f ()).to_raise ("table expected") + expect (f "string").to_raise ("table expected") + + +- describe merge: + - before: | + f = Tree.merge + + -- Additional merge keys which are moderately unusual + t1 = Tree { k1 = "v1", k2 = "if", k3 = Tree {"?"} } + t2 = Tree { ["if"] = true, [{"?"}] = false, _ = "underscore", k3 = "v2" } + + target = Tree.clone (t1) + for ty, p, n in Tree.nodes (t2) do + if ty == "leaf" then target[p] = n end + 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, Tree {})).to_be (t1) + - it copies t2 when t1 is empty: | + expect (f (Tree {}, 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 = Tree.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 diagnoses non-table arguments: + expect (f (nil, {})).to_raise ("table expected") + expect (f ({}, nil)).to_raise ("table expected") + + +- describe nodes: + - before: + f = Tree.nodes + + function traverse (subject) + l = {} + for ty, p, n in f (subject) do l[1+#l]={ty, Tree.clone (p), n} end + return l + end + - it iterates over the elements of a table argument: | + subject = {"first", "second", "3rd"} + expect (traverse (subject)). + to_equal {{"branch", {}, subject}, -- { + {"leaf", {1}, subject[1]}, -- first, + {"leaf", {2}, subject[2]}, -- second, + {"leaf", {3}, subject[3]}, -- 3rd, + {"join", {}, subject}} -- } + - it iterates over the elements of nested a table argument: | + subject = {{"one", {"two"}, {{"three"}, "four"}}, "five"} + expect (traverse (subject)). + to_equal {{"branch", {}, subject}, -- { + {"branch", {1}, subject[1]}, -- { + {"leaf", {1,1}, subject[1][1]}, -- one, + {"branch", {1,2}, subject[1][2]}, -- { + {"leaf", {1,2,1}, subject[1][2][1]}, -- two, + {"join", {1,2}, subject[1][2]}, -- }, + {"branch", {1,3}, subject[1][3]}, -- { + {"branch", {1,3,1}, subject[1][3][1]}, -- { + {"leaf", {1,3,1,1}, subject[1][3][1][1]}, -- three, + {"join", {1,3,1}, subject[1][3][1]}, -- }, + {"leaf", {1,3,2}, subject[1][3][2]}, -- four, + {"join", {1,3}, subject[1][3]}, -- }, + {"join", {1}, subject[1]}, -- }, + {"leaf", {2}, subject[2]}, -- five, + {"join", {}, subject}} -- } + - it includes the hash part of a table argument: | + -- like `pairs`, `nodes` can visit elements in any order, so we cannot + -- guarantee the array part is always visited before the hash part, or + -- even that the array elements are visited in order! + subject = {"first", "second"; third = "3rd"} + expect (traverse (subject)).to_contain. + a_permutation_of {{"branch", {}, subject}, -- { + {"leaf", {1}, subject[1]}, -- first, + {"leaf", {2}, subject[2]}, -- second, + {"leaf", {"third"}, subject["third"]}, -- 3rd + {"join", {}, subject}} -- } + - it includes hash parts of a nested table argument: | + -- like `pairs`, `nodes` can visit elements in any order, so we cannot + -- guarantee the array part is always visited before the hash part, or + -- even that the array elements are visited in order! + subject = {{"one", {two=2}, {{"three"}, four=4}}, foo="bar", "five"} + expect (traverse (subject)).to_contain. + a_permutation_of {{"branch", {}, subject}, -- { + {"branch", {1}, subject[1]}, -- { + {"leaf", {1,1}, subject[1][1]}, -- one, + {"branch", {1,2}, subject[1][2]}, -- { + {"leaf", {1,2,"two"}, subject[1][2]["two"]}, -- 2, + {"join", {1,2}, subject[1][2]}, -- }, + {"branch", {1,3}, subject[1][3]}, -- { + {"branch", {1,3,1}, subject[1][3][1]}, -- { + {"leaf", {1,3,1,1}, subject[1][3][1][1]}, -- three, + {"join", {1,3,1}, subject[1][3][1]}, -- }, + {"leaf", {1,3,"four"}, subject[1][3]["four"]}, -- 4, + {"join", {1,3}, subject[1][3]}, -- }, + {"join", {1}, subject[1]}, -- }, + {"leaf", {2}, subject[2]}, -- five, + {"leaf", {"foo"}, subject["foo"]}, -- bar, + {"join", {}, subject}} -- } + - it works on trees too: | + -- like `pairs`, `nodes` can visit elements in any order, so we cannot + -- guarantee the array part is always visited before the hash part, or + -- even that the array elements are visited in order! + subject = Tree {Tree {"one", + Tree {two=2}, + Tree {Tree {"three"}, four=4}}, + foo="bar", + "five"} + expect (traverse (subject)).to_contain. + a_permutation_of {{"branch", {}, subject}, -- { + {"branch", {1}, subject[1]}, -- { + {"leaf", {1,1}, subject[1][1]}, -- one, + {"branch", {1,2}, subject[1][2]}, -- { + {"leaf", {1,2,"two"}, subject[1][2]["two"]}, -- 2, + {"join", {1,2}, subject[1][2]}, -- }, + {"branch", {1,3}, subject[1][3]}, -- { + {"branch", {1,3,1}, subject[1][3][1]}, -- { + {"leaf", {1,3,1,1}, subject[1][3][1][1]}, -- three, + {"join", {1,3,1}, subject[1][3][1]}, -- }, + {"leaf", {1,3,"four"}, subject[1][3]["four"]}, -- 4, + {"join", {1,3}, subject[1][3]}, -- }, + {"join", {1}, subject[1]}, -- }, + {"leaf", {2}, subject[2]}, -- five, + {"leaf", {"foo"}, subject["foo"]}, -- bar, + {"join", {}, subject}} -- } + - it generates path key-lists that are valid __index arguments: | + subject = Tree {"first", Tree {"second"}, "3rd"} + expect (traverse (subject)). + to_equal {{"branch", {}, subject[{}]}, -- { + {"leaf", {1}, subject[{1}]}, -- first, + {"branch", {2}, subject[{2}]}, -- { + {"leaf", {2,1}, subject[{2,1}]}, -- second + {"join", {2}, subject[{2}]}, -- } + {"leaf", {3}, subject[{3}]}, -- 3rd, + {"join", {}, subject[{}]}} -- } + - it diagnoses non-table arguments: + expect (f ()).to_raise ("table expected") + expect (f "string").to_raise ("table expected") + + +- describe __index: + - it returns nil for a missing key: + expect (tr["no such key"]).to_be (nil) + - it returns nil for missing single element key lists: + expect (tr[{"no such key"}]).to_be (nil) + - it returns nil for missing multi-element key lists: + expect (tr[{"fnord", "foo"}]).to_be (nil) + expect (tr[{"no", "such", "key"}]).to_be (nil) + - it returns a value for the given key: + expect (tr["foo"]).to_be "foo" + expect (tr["quux"]).to_be "quux" + - it returns tree root for empty key list: + expect (tr[{}]).to_be (tr) + - it returns values for single element key lists: + expect (tr[{"foo"}]).to_be "foo" + expect (tr[{"quux"}]).to_be "quux" + - it returns values for multi-element key lists: + expect (tr[{"fnord", "branch", "bar"}]).to_be "bar" + expect (tr[{"fnord", "branch", "baz"}]).to_be "baz" + + +- describe __newindex: + - before: + tr = Tree {} + - it stores values for simple keys: + tr["foo"] = "foo" + expect (tr).to_equal (Tree {foo="foo"}) + - it stores values for single element key lists: + tr[{"foo"}] = "foo" + expect (tr).to_equal (Tree {foo="foo"}) + - it stores values for multi-element key lists: + tr[{"foo", "bar"}] = "baz" + expect (tr).to_equal (Tree {foo=Tree {bar="baz"}}) + - it separates branches for diverging key lists: + tr[{"foo", "branch", "bar"}] = "leaf1" + tr[{"foo", "branch", "baz"}] = "leaf2" + expect (tr).to_equal (Tree {foo=Tree {branch=Tree {bar="leaf1", baz="leaf2"}}}) + +- describe __tostring: + - it returns a string: + expect (prototype (tostring (tr))).to_be "string" + - it shows the type name: + expect (tostring (tr)).to_contain "Tree" + - it shows the contents in order: | + tr = Tree {foo = "foo", + fnord = Tree {branch = Tree {bar="bar", baz="baz"}}, + quux = "quux"} + expect (tostring (tr)). + to_contain 'fnord=Tree {branch=Tree {bar=bar, baz=baz}}, foo=foo, quux=quux' diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100755 index cf640d2..0000000 --- a/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/std.lua -/luadoc.css -/index.html -/files -/modules diff --git a/src/base.lua b/src/base.lua deleted file mode 100644 index 3ec219d..0000000 --- a/src/base.lua +++ /dev/null @@ -1,537 +0,0 @@ ---- Adds to the existing global functions -module ("base", package.seeall) - ---- Functional forms of infix operators. --- Defined here so that other modules can write to it. --- @class table --- @name _G.op -_G.op = {} - -require "table_ext" -local list = require "list" -require "string_ext" ---require "io_ext" FIXME: allow loops -local strbuf = require "strbuf" - - ---- Require a module with a particular version --- @param module module to require --- @param min lowest acceptable version (default: any) --- @param too_big lowest version that is too big (default: none) --- @pattern pattern to match version in module.version or --- module.VERSION (default: ".*[%.%d]+" -function _G.require_version (module, min, too_big, pattern) - local function version_to_list (v) - return list.new (string.split (v, "%.")) - end - local function module_version (module, pattern) - return version_to_list (string.match (module.version or module._VERSION, - pattern or ".*[%.%d]+")) - end - local m = require (module) - if min then - assert (module_version (m, pattern) >= version_to_list (min)) - end - if too_big then - assert (module_version (m, pattern) < version_to_list (too_big)) - end - return m -end - ---- Return given metamethod, if any, or nil. --- @param x object to get metamethod of --- @param n name of metamethod to get --- @return metamethod function or nil if no metamethod or not a --- function -function _G.metamethod (x, n) - local _, m = pcall (function (x) - return getmetatable (x)[n] - end, - x) - if type (m) ~= "function" then - m = nil - end - return m -end - ---- Turn tables into strings with recursion detection. --- N.B. Functions calling render should not recurse, or recursion --- detection will not work. --- @see render_OpenRenderer, render_CloseRenderer --- @see render_ElementRenderer, render_PairRenderer --- @see render_SeparatorRenderer --- @param x object to convert to string --- @param open open table renderer --- @param close close table renderer --- @param elem element renderer --- @param pair pair renderer --- @param sep separator renderer --- @return string representation -function _G.render (x, open, close, elem, pair, sep, roots) - local function stop_roots (x) - return roots[x] or render (x, open, close, elem, pair, sep, table.clone (roots)) - end - roots = roots or {} - if type (x) ~= "table" or metamethod (x, "__tostring") then - return elem (x) - else - local s = strbuf.new () - s = s .. open (x) - roots[x] = elem (x) - local i, v = nil, nil - for j, w in pairs (x) do - s = s .. sep (x, i, v, j, w) .. pair (x, j, w, stop_roots (j), stop_roots (w)) - i, v = j, w - end - s = s .. sep(x, i, v, nil, nil) .. close (x) - return s:tostring () - end -end - ---- --- @class function --- @name render_OpenRenderer --- @param t table --- @return open table string - ---- --- @class function --- @name render_CloseRenderer --- @param t table --- @return close table string - ---- --- @class function --- @name render_ElementRenderer --- @param e element --- @return element string - ---- --- @class function --- @name render_PairRenderer --- N.B. the function should not try to render i and v, or treat --- them recursively. --- @param t table --- @param i index --- @param v value --- @param is index string --- @param vs value string --- @return element string - ---- --- @class function --- @name render_SeparatorRenderer --- @param t table --- @param i preceding index (nil on first call) --- @param v preceding value (nil on first call) --- @param j following index (nil on last call) --- @param w following value (nil on last call) --- @return separator string - ---- Extend tostring to work better on tables. --- The original tostring is available as _tostring. --- @class function --- @name _G.tostring --- @param x object to convert to string --- @return string representation -_G._tostring = tostring -- make original tostring available -local _tostring = tostring -function _G.tostring (x) - return render (x, - function () return "{" end, - function () return "}" end, - _tostring, - function (t, _, _, i, v) - return i .. "=" .. v - end, - function (_, i, _, j) - if i and j then - return "," - end - return "" - end) -end - ---- Pretty-print a table. --- @param t table to print --- @param indent indent between levels ["\t"] --- @param spacing space before every line --- @return pretty-printed string -function _G.prettytostring (t, indent, spacing) - indent = indent or "\t" - spacing = spacing or "" - return render (t, - function () - local s = spacing .. "{" - spacing = spacing .. indent - return s - end, - function () - spacing = string.gsub (spacing, indent .. "$", "") - return spacing .. "}" - end, - function (x) - if type (x) == "string" then - return string.format ("%q", x) - else - return tostring (x) - end - end, - function (x, i, v, is, vs) - local s = spacing .. "[" - if type (i) == "table" then - s = s .. "\n" - end - s = s .. is - if type (i) == "table" then - s = s .. "\n" - end - s = s .. "] =" - if type (v) == "table" then - s = s .. "\n" - else - s = s .. " " - end - s = s .. vs - return s - end, - function (_, i) - local s = "\n" - if i then - s = "," .. s - end - return s - end) -end - ---- Turn an object into a table according to __totable metamethod. --- @param x object to turn into a table --- @return table or nil -function _G.totable (x) - local m = metamethod (x, "__totable") - if m then - return m (x) - elseif type (x) == "table" then - return x - else - return nil - end -end - ---- Convert a value to a string. --- The string can be passed to dostring to retrieve the value. ---
TODO: Make it work for recursive tables. --- @param x object to pickle --- @return string such that eval (s) is the same value as x -function _G.pickle (x) - if type (x) == "string" then - return string.format ("%q", x) - elseif type (x) == "number" or type (x) == "boolean" or - type (x) == "nil" then - return tostring (x) - else - x = totable (x) or x - if type (x) == "table" then - local s, sep = "{", "" - for i, v in pairs (x) do - s = s .. sep .. "[" .. pickle (i) .. "]=" .. pickle (v) - sep = "," - end - s = s .. "}" - return s - else - die ("cannot pickle " .. tostring (x)) - end - end -end - ---- Identity function. --- @param ... --- @return the arguments passed to the function -function _G.id (...) - return ... -end - ---- Turn a tuple into a list. --- @param ... tuple --- @return list -function _G.pack (...) - return {...} -end - ---- Partially apply a function. --- @param f function to apply partially --- @param ... arguments to bind --- @return function with ai already bound -function _G.bind (f, ...) - local fix = {...} - return function (...) - return f (unpack (list.concat (fix, {...}))) - end -end - ---- Curry a function. --- @param f function to curry --- @param n number of arguments --- @return curried version of f -function _G.curry (f, n) - if n <= 1 then - return f - else - return function (x) - return curry (bind (f, x), n - 1) - end - end -end - ---- Compose functions. --- @param f1...fn functions to compose --- @return composition of f1 ... fn -function _G.compose (...) - local arg = {...} - local fns, n = arg, #arg - return function (...) - local arg = {...} - for i = n, 1, -1 do - arg = {fns[i] (unpack (arg))} - end - return unpack (arg) - end -end - ---- Memoize a function, by wrapping it in a functable. --- @param fn function that returns a single result --- @return memoized function -function _G.memoize (fn) - return setmetatable ({}, { - __call = function (self, ...) - local k = tostring ({...}) - local v = self[k] - if v == nil then - v = fn (...) - self[k] = v - end - return v - end - }) -end - ---- Evaluate a string. --- @param s string --- @return value of string -function _G.eval (s) - return loadstring ("return " .. s)() -end - ---- An iterator like ipairs, but in reverse. --- @param t table to iterate over --- @return iterator function --- @return the table, as above --- @return #t + 1 -function _G.ripairs (t) - return function (t, n) - n = n - 1 - if n > 0 then - return n, t[n] - end - end, - t, #t + 1 -end - ---- --- @class function --- @name tree_Iterator --- @param n current node --- @return type ("leaf", "branch" (pre-order) or "join" (post-order)) --- @return path to node ({i1...ik}) --- @return node -local function _nodes (it, tr) - local p = {} - local function visit (n) - if type (n) == "table" then - coroutine.yield ("branch", p, n) - for i, v in it (n) do - table.insert (p, i) - visit (v) - table.remove (p) - end - coroutine.yield ("join", p, n) - else - coroutine.yield ("leaf", p, n) - end - end - return coroutine.wrap (visit), tr -end - ---- Tree iterator. --- @see tree_Iterator --- @param tr tree to iterate over --- @return iterator function --- @return the tree, as above -function _G.nodes (tr) - return _nodes (pairs, tr) -end - ---- Tree iterator over numbered nodes, in order. --- @see tree_Iterator --- @param tr tree to iterate over --- @return iterator function --- @return the tree, as above -function _G.inodes (tr) - return _nodes (ipairs, tr) -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 - coroutine.yield (n) - end - end - return coroutine.wrap (visit), tr -end - ---- Tree iterator which returns just numbered leaves, in order. --- @param tr tree to iterate over --- @return iterator function --- @return the tree, as above -function _G.ileaves (tr) - return _leaves (ipairs, tr) -end - ---- Tree iterator which returns just leaves. --- @param tr tree to iterate over --- @return iterator function --- @return the tree, as above -function _G.leaves (tr) - return _leaves (pairs, tr) -end - ---- Collect the results of an iterator. --- @param i iterator --- @return results of running the iterator on its arguments -function _G.collect (i, ...) - local t = {} - for e in i (...) do - table.insert (t, e) - end - return t -end - ---- Map a function over an iterator. --- @param f function --- @param i iterator --- @return result table -function _G.map (f, i, ...) - local t = {} - for e in i (...) do - local r = f (e) - if r then - table.insert (t, r) - end - end - return t -end - ---- Filter an iterator with a predicate. --- @param p predicate --- @param i iterator --- @return result table containing elements e for which p (e) -function _G.filter (p, i, ...) - local t = {} - for e in i (...) do - if p (e) then - table.insert (t, e) - end - end - return t -end - ---- Fold a binary function into an iterator. --- @param f function --- @param d initial first argument --- @param i iterator --- @return result -function _G.fold (f, d, i, ...) - local r = d - for e in i (...) do - r = f (r, e) - end - return r -end - ---- Extend to allow formatted arguments. --- @param v value to assert --- @param f format --- @param ... arguments to format --- @return value -function _G.assert (v, f, ...) - if not v then - if f == nil then - f = "" - end - error (string.format (f, ...)) - end - return v -end - ---- Give warning with the name of program and file (if any). --- @param ... arguments for format -function _G.warn (...) - if prog.name then - io.stderr:write (prog.name .. ":") - end - if prog.file then - io.stderr:write (prog.file .. ":") - end - if prog.line then - io.stderr:write (tostring (prog.line) .. ":") - end - if prog.name or prog.file or prog.line then - io.stderr:write (" ") - end - io.writelines (io.stderr, string.format (...)) -end - ---- Die with error. --- @param ... arguments for format -function _G.die (...) - warn (...) - error () -end - --- Function forms of operators. --- FIXME: Make these visible in LuaDoc (also list.concat in list) -_G.op["[]"] = function (t, s) - return t[s] -end -_G.op["+"] = function (a, b) - return a + b -end -_G.op["-"] = function (a, b) - return a - b -end -_G.op["*"] = function (a, b) - return a * b -end -_G.op["/"] = function (a, b) - return a / b -end -_G.op["and"] = function (a, b) - return a and b -end -_G.op["or"] = function (a, b) - return a or b -end -_G.op["not"] = function (a) - return not a -end -_G.op["=="] = function (a, b) - return a == b -end -_G.op["~="] = function (a, b) - return a ~= b -end diff --git a/src/bin.lua b/src/bin.lua deleted file mode 100644 index 46375dc..0000000 --- a/src/bin.lua +++ /dev/null @@ -1,26 +0,0 @@ ---- Binary data utilities - ---- Turn a little-endian word into a number -local function le_to_number (s) - local res = 0 - for i = #s, 1, -1 do - res = res * 256 + string.byte (s, i) - end - return res -end - ---- Turn a little-endian word into a hex string -local function le_to_hex (s) - local res = "" - for i = 1, #s do - res = res .. string.format ("%.2x", string.byte (s, i)) - end - return res -end - -local M = { - le_to_number = le_to_number, - le_to_hex = le_to_hex, -} - -return M diff --git a/src/debug_ext.lua b/src/debug_ext.lua deleted file mode 100644 index c2d5dfd..0000000 --- a/src/debug_ext.lua +++ /dev/null @@ -1,84 +0,0 @@ ---- Additions to the debug module -module ("debug", package.seeall) - -require "debug_init" -require "io_ext" -require "string_ext" - ---- To activate debugging set _DEBUG either to any true value --- (equivalent to {level = 1}), or as documented below. --- @class table --- @name _DEBUG --- @field level debugging level --- @field call do call trace debugging --- @field std do standard library debugging (run examples & test code) - - ---- Print a debugging message --- @param n debugging level, defaults to 1 --- @param ... objects to print (as for print) -function say (n, ...) - local level = 1 - local arg = {n, ...} - if type (arg[1]) == "number" then - level = arg[1] - table.remove (arg, 1) - end - if _DEBUG and - ((type (_DEBUG) == "table" and type (_DEBUG.level) == "number" and - _DEBUG.level >= level) - or level <= 1) then - io.writelines (io.stderr, table.concat (list.map (tostring, arg), "\t")) - end -end - ---- --- The global function debug is an abbreviation for --- debug.say (1, ...) --- @class function --- @name debug --- @see say -getmetatable (_M).__call = - function (self, ...) - say (1, ...) - end - ---- Trace function calls --- Use as debug.sethook (trace, "cr"), which is done automatically --- when _DEBUG.call is set. --- Based on test/trace-calls.lua from the Lua distribution. --- @class function --- @name trace --- @param event event causing the call -local level = 0 -function trace (event) - local t = getinfo (3) - local s = " >>> " .. string.rep (" ", level) - if t ~= nil and t.currentline >= 0 then - s = s .. t.short_src .. ":" .. t.currentline .. " " - end - t = getinfo (2) - if event == "call" then - level = level + 1 - else - level = math.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 - io.writelines (io.stderr, s) -end - --- Set hooks according to _DEBUG -if type (_DEBUG) == "table" and _DEBUG.call then - sethook (trace, "cr") -end diff --git a/src/debug_init.lua b/src/debug_init.lua deleted file mode 100644 index 37ab532..0000000 --- a/src/debug_init.lua +++ /dev/null @@ -1,2 +0,0 @@ --- Debugging is on by default -_G._DEBUG = true diff --git a/src/fstable.lua b/src/fstable.lua deleted file mode 100644 index 7e6574c..0000000 --- a/src/fstable.lua +++ /dev/null @@ -1,123 +0,0 @@ ---- Tables mapped to the filing system --- Only string keys are permitted; package.dirsep characters are --- converted to underscores. --- Values are stored as strings (converted by tostring). --- As with disk operations, a table's elements must be set to nil --- (deleted) before the table itself can be set to nil. - -require "io_ext" -require "table_ext" - -require "io_ext" -require "lfs" -require "posix" - -local new - -local function fsnext (dir) - local f - repeat - f = dir:next () - until f ~= "." and f ~= ".." - return f -end - --- Metamethods for persistent tables -local metatable = {} - -metatable.__index = -function (t, k) - local path = io.catfile (getmetatable (t).directory, k) - local attrs = lfs.attributes (path) - if attrs then - if attrs.mode == "file" then - return io.slurp (path) - elseif attrs.mode == "directory" then - return new (path) - end - end - return attrs -end - -metatable.__newindex = -function (t, k, v) - local ty = type (v) - if ty == "thread" or ty == "function" or ty == "userdata" then - error ("cannot persist a " .. ty .. "") - elseif type (k) ~= "string" then - error ("keys of persistent tables must be of type string") - else - k = string.gsub (k, package.dirsep, "_") - local path = io.catfile (getmetatable (t).directory, k) - local vm = getmetatable (v) - if v == nil then - os.remove (path) - elseif type (v) ~= "table" then - local h = io.open (path, "w") - h:write (tostring (v)) - h:close () - elseif type (vm) == "table" and vm.metatable == metatable then - -- To match Lua semantics we'd hardlink, but that's not allowed for directories - local ok, errmsg = posix.link (vm.directory, path, true) - else - local ok, errmsg = lfs.mkdir (path) - if not ok then - error (errmsg) - end - new (path, v) - end - end -end - -metatable.__pairs = -function (t) - local _, dir = lfs.dir (getmetatable (t).directory) - return function (dir) - local f = fsnext (dir) - if f then - return f, t[f] - end - end, - dir -end - -metatable.__ipairs = -function (t) - local _, dir = lfs.dir (getmetatable (t).directory) - return function (dir, i) - local f = fsnext (dir) - if f then - return i + 1, f - end - end, - dir, 0 -end - ---- Bind a directory to a table --- @param path directory path --- @param t table to merge with directory --- @return table bound to directory -function new (path, t) - if not path:find ("^" .. package.dirsep) then - path = io.catfile (lfs.currentdir (), path) - end - if lfs.attributes (path, "mode") ~= "directory" then - error ("`" .. path .. "' does not exist or is not a directory") - end - local m = table.clone (metatable) - m.directory = path - m.metatable = metatable - local d = setmetatable ({}, m) - if t then - for i, v in pairs (t) do - d[i] = v - end - end - return d -end - -local M = { - new = new, -} - -return M diff --git a/src/getopt.lua b/src/getopt.lua deleted file mode 100644 index 78734df..0000000 --- a/src/getopt.lua +++ /dev/null @@ -1,282 +0,0 @@ ---- Simplified getopt, based on Svenne Panne's Haskell GetOpt.
--- Usage: ---
    ---
  • options = {Option {...}, ...}
    --- getopt.processArgs ()
  • ---
  • Assumes prog = {name[, banner] [, purpose] [, notes] [, usage]}
  • ---
  • Options take a single dash, but may have a double dash.
  • ---
  • Arguments may be given as -opt=arg or -opt arg.
  • ---
  • If an option taking an argument is given multiple times, only the --- last value is returned; missing arguments are returned as 1.
  • ---
--- getOpt, usageInfo and usage can be called directly (see --- below, and the example at the end). Set _DEBUG.std to a non-nil --- value to run the example. ---
    ---
  • TODO: Sort out the packaging. getopt.Option is tedious to type, but --- surely Option shouldn't be in the root namespace?
  • ---
  • TODO: Wrap all messages; do all wrapping in processArgs, not --- usageInfo; use sdoc-like library (see string.format todos).
  • ---
  • TODO: Don't require name to be repeated in banner.
  • ---
  • TODO: Store version separately (construct banner?).
  • ---
- -require "base" -local list = require "list" -require "string_ext" -local Object = require "object" - - ---- Perform argument processing --- @param argIn list of command-line args --- @param options options table --- @return table of remaining non-options --- @return table of option key-value list pairs --- @return table of error messages -local function getOpt (argIn, options) - local noProcess = nil - local argOut, optOut, errors = {[0] = argIn[0]}, {}, {} - -- get an argument for option opt - local function getArg (o, opt, arg, oldarg) - if o.type == nil then - if arg ~= nil then - table.insert (errors, "option `" .. opt .. "' doesn't take an argument") - end - else - if arg == nil and argIn[1] and - string.sub (argIn[1], 1, 1) ~= "-" then - arg = argIn[1] - table.remove (argIn, 1) - end - if arg == nil and o.type == "Req" then - table.insert (errors, "option `" .. opt .. - "' requires an argument `" .. o.var .. "'") - return nil - end - end - return arg or 1 -- make sure arg has a value - end - - local function parseOpt (opt, arg) - local o = options.name[opt] - if o ~= nil then - optOut[o.name[1]] = optOut[o.name[1]] or {} - table.insert (optOut[o.name[1]], getArg (o, opt, arg, optOut[o.name[1]])) - else - table.insert (errors, "unrecognized option `-" .. opt .. "'") - end - end - while argIn[1] do - local v = argIn[1] - table.remove (argIn, 1) - local _, _, dash, opt = string.find (v, "^(%-%-?)([^=-][^=]*)") - local _, _, arg = string.find (v, "=(.*)$") - if v == "--" then - noProcess = 1 - elseif dash == nil or noProcess then -- non-option - table.insert (argOut, v) - else -- option - parseOpt (opt, arg) - end - end - return argOut, optOut, errors -end - - ---- Options table type. --- @class table --- @name _G.Option --- @field name list of names --- @field desc description of this option --- @field type type of argument (if any): Req(uired), --- Opt(ional) --- @field var descriptive name for the argument -_G.Option = Object {_init = {"name", "desc", "type", "var"}} - ---- Options table constructor: adds lookup tables for the option names -local function makeOptions (t) - t = list.concat (t or {}, - {Option {{"version", "V"}, - "output version information and exit"}, - Option {{"help", "h"}, - "display this help and exit"}} - ) - local name = {} - for v in list.elems (t) do - for j, s in pairs (v.name) do - if name[s] then - warn ("duplicate option '%s'", s) - end - name[s] = v - end - end - t.name = name - return t -end - - ---- Produce usage info for the given options --- @param header header string --- @param optDesc option descriptors --- @param pageWidth width to format to [78] --- @return formatted string -local function usageInfo (header, optDesc, pageWidth) - pageWidth = pageWidth or 78 - -- Format the usage info for a single option - -- @param opt the Option table - -- @return options - -- @return description - local function fmtOpt (opt) - local function fmtName (o) - return "-" .. o - end - local function fmtArg () - if opt.type == nil then - return "" - elseif opt.type == "Req" then - return "=" .. opt.var - else - return "[=" .. opt.var .. "]" - end - end - local textName = list.reverse (list.map (fmtName, opt.name)) - textName[#textName] = textName[#textName] .. fmtArg () - return {table.concat ({table.concat (textName, ", ")}, ", "), - opt.desc} - end - local function sameLen (xs) - local n = math.max (unpack (list.map (string.len, xs))) - for i, v in pairs (xs) do - xs[i] = string.sub (v .. string.rep (" ", n), 1, n) - end - return xs, n - end - local function paste (x, y) - return " " .. x .. " " .. y - end - local function wrapper (w, i) - return function (s) - return string.wrap (s, w, i, 0) - end - end - local optText = "" - if #optDesc > 0 then - local cols = list.transpose (list.map (fmtOpt, optDesc)) - local width - cols[1], width = sameLen (cols[1]) - cols[2] = list.map (wrapper (pageWidth, width + 4), cols[2]) - optText = "\n\n" .. - table.concat (list.mapWith (paste, - list.transpose ({sameLen (cols[1]), - cols[2]})), - "\n") - end - return header .. optText -end - ---- Emit a usage message. -local function usage () - local usage, purpose, notes = "[OPTION]... [FILE]...", "", "" - if prog.usage then - usage = prog.usage - end - if prog.purpose then - purpose = "\n" .. prog.purpose - end - if prog.notes then - notes = "\n\n" - if not string.find (prog.notes, "\n") then - notes = notes .. string.wrap (prog.notes) - else - notes = notes .. prog.notes - end - end - io.writelines (usageInfo ("Usage: " .. prog.name .. " " .. usage .. purpose, - options) - .. notes) -end - - ---- Simple getOpt wrapper. --- Adds -version/-V and --- -help/-h automatically; --- stops program if there was an error, or if -help or --- -version was used. -local function processArgs () - local totArgs = #arg - options = makeOptions (options) - local errors - _G.arg, opt, errors = getopt.getOpt (arg, options) - if (opt.version or opt.help) and prog.banner then - io.writelines (prog.banner) - end - if #errors > 0 or opt.help then - local name = prog.name - prog.name = nil - if #errors > 0 then - warn (table.concat (errors, "\n") .. "\n") - end - prog.name = name - usage () - if #errors > 0 then - error () - end - end - if opt.version or opt.help then - os.exit () - end -end -_G.options = nil - - --- A small and hopefully enlightening example: -if type (_DEBUG) == "table" and _DEBUG.std then - - options = makeOptions ({ - Option {{"verbose", "v"}, "verbosely list files"}, - Option {{"output", "o"}, "dump to FILE", "Opt", "FILE"}, - Option {{"name", "n"}, "only dump USER's files", "Req", "USER"}, - }) - - function test (cmdLine) - local nonOpts, opts, errors = getopt.getOpt (cmdLine, options) - if #errors == 0 then - print ("options=" .. tostring (opts) .. - " args=" .. tostring (nonOpts) .. "\n") - else - print (table.concat (errors, "\n") .. "\n" .. - usageInfo ("Usage: foobar [OPTION...] FILE...", - options)) - end - end - - -- FIXME: Turn the following documentation into unit tests - prog = {name = "foobar"} -- for errors - -- Example runs: - test {"foo", "-v"} - -- options={verbose={1}} args={1=foo} - test {"foo", "--", "-v"} - -- options={} args={1=foo,2=-v} - test {"-o", "-V", "-name", "bar", "--name=baz"} - -- options={name={"baz"},version={1},output={1}} args={} - test {"-foo"} - -- unrecognized option `-foo' - -- Usage: foobar [OPTION]... [FILE]... - -- - -- -v, -verbose verbosely list files - -- -o, -output[=FILE] dump to FILE - -- -n, -name=USER only dump USER's files - -- -V, -version output version information and exit - -- -h, -help display this help and exit - -end - --- Public interface -local M = { - getOpt = getOpt, - processArgs = processArgs, - usage = usage, - usageInfo = usageInfo, -} - -return M diff --git a/src/io_ext.lua b/src/io_ext.lua deleted file mode 100644 index d867f51..0000000 --- a/src/io_ext.lua +++ /dev/null @@ -1,115 +0,0 @@ ---- Additions to the io module -module ("io", package.seeall) - -require "base" -require "package_ext" - - --- Get file handle metatable -local file_metatable = getmetatable (io.stdin) - - --- Get an input file handle. --- @param h file handle or name (default: io.input ()) --- @return file handle, or nil on error -local function input_handle (h) - if h == nil then - h = input () - elseif _G.type (h) == "string" then - h = io.open (h) - end - return h -end - ---- Slurp a file handle. --- @param h file handle or name (default: io.input ()) --- @return contents of file or handle, or nil if error -function slurp (h) - h = input_handle (h) - if h then - local s = h:read ("*a") - h:close () - return s - end -end - ---- Read a file or file handle into a list of lines. --- @param h file handle or name (default: io.input ()); --- if h is a handle, the file is closed after reading --- @return list of lines -function readlines (h) - h = input_handle (h) - local l = {} - for line in h:lines () do - table.insert (l, line) - end - h:close () - return l -end -file_metatable.readlines = readlines - ---- Write values adding a newline after each. --- @param h file handle (default: io.output () --- @param ... values to write (as for write) -function writelines (h, ...) - if io.type (h) ~= "file" then - io.write (h, "\n") - h = io.output () - end - for v in ileaves ({...}) do - h:write (v, "\n") - end -end -file_metatable.writelines = writelines - ---- Split a directory path into components. --- Empty components are retained: the root directory becomes {"", ""}. --- @param path path --- @return list of path components -function splitdir (path) - return string.split (path, package.dirsep) -end - ---- Concatenate one or more directories and a filename into a path. --- @param ... path components --- @return path -function catfile (...) - return table.concat ({...}, package.dirsep) -end - ---- Concatenate two or more directories into a path, removing the trailing slash. --- @param ... path components --- @return path -function catdir (...) - return (string.gsub (catfile (...), "^$", package.dirsep)) -end - ---- Perform a shell command and return its output. --- @param c command --- @return output, or nil if error -function shell (c) - return io.slurp (io.popen (c)) -end - ---- Process files specified on the command-line. --- If no files given, process io.stdin; in list of files, --- - means io.stdin. ---
FIXME: Make the file list an argument to the function. --- @param f function to process files with, which is passed --- (name, arg_no) -function processFiles (f) - -- N.B. "arg" below refers to the global array of command-line args - if #arg == 0 then - table.insert (arg, "-") - end - for i, v in ipairs (arg) do - if v == "-" then - io.input (io.stdin) - else - io.input (v) - end - prog.file = v - f (v, i) - end - prog.file = nil -end diff --git a/src/lcs.lua b/src/lcs.lua deleted file mode 100644 index aab9920..0000000 --- a/src/lcs.lua +++ /dev/null @@ -1,61 +0,0 @@ ---- Longest Common Subsequence algorithm. --- After pseudo-code in lecture --- notes by David Eppstein. - - --- Find common subsequences. --- @param a first sequence --- @param b second sequence --- @return list of common subsequences --- @return the length of a --- @return the length of b -local function commonSubseqs (a, b) - local l, m, n = {}, #a, #b - for i = m + 1, 1, -1 do - l[i] = {} - for j = n + 1, 1, -1 do - if i > m or j > n then - l[i][j] = 0 - elseif a[i] == b[j] then - l[i][j] = 1 + l[i + 1][j + 1] - else - l[i][j] = math.max (l[i + 1][j], l[i][j + 1]) - end - end - end - return l, m, n -end - ---- Find the longest common subsequence of two sequences. --- The sequence objects must have an __append metamethod. --- This is provided by string_ext for strings, and by --- list for lists. --- @param a first sequence --- @param b second sequence --- @param s an empty sequence of the same type, to hold the result --- @return the LCS of a and b -local function longestCommonSubseq (a, b, s) - local l, m, n = commonSubseqs (a, b) - local i, j = 1, 1 - local f = getmetatable (s).__append - while i <= m and j <= n do - if a[i] == b[j] then - s = f (s, a[i]) - i = i + 1 - j = j + 1 - elseif l[i + 1][j] >= l[i][j + 1] then - i = i + 1 - else - j = j + 1 - end - end - return s -end - --- Public interface -local M = { - longestCommonSubseq = longestCommonSubseq, -} - -return M diff --git a/src/list.lua b/src/list.lua deleted file mode 100644 index f86a4ff..0000000 --- a/src/list.lua +++ /dev/null @@ -1,404 +0,0 @@ ---- Tables as lists. -require "base" -require "table_ext" - - ---- An iterator over the elements of a list. --- @param l list to iterate over --- @return iterator function which returns successive elements of the list --- @return the list l as above --- @return true -local function elems (l) - local n = 0 - return function (l) - n = n + 1 - if n <= #l then - return l[n] - end - end, - l, true -end - ---- An iterator over the elements of a list, in reverse. --- @param l list to iterate over --- @return iterator function which returns precessive elements of the list --- @return the list l as above --- @return true -local function relems (l) - local n = #l + 1 - return function (l) - n = n - 1 - if n > 0 then - return l[n] - end - end, - l, true -end - ---- Map a function over a list. --- @param f function --- @param l list --- @return result list {f (l[1]), ..., f (l[#l])} -local function map (f, l) - return _G.map (f, elems, l) -end - ---- Map a function over a list of lists. --- @param f function --- @param ls list of lists --- @return result list {f (unpack (ls[1]))), ..., f (unpack (ls[#ls]))} -local function mapWith (f, l) - return _G.map (compose (f, unpack), elems, l) -end - ---- Filter a list according to a predicate. --- @param p predicate (function of one argument returning a boolean) --- @param l list of lists --- @return result list containing elements e of --- l for which p (e) is true -local function filter (p, l) - return _G.filter (p, elems, l) -end - ---- Return a slice of a list. --- (Negative list indices count from the end of the list.) --- @param l list --- @param from start of slice (default: 1) --- @param to end of slice (default: #l) --- @return {l[from], ..., l[to]} -local function slice (l, from, to) - local m = {} - local len = #l - from = from or 1 - to = to or len - if from < 0 then - from = from + len + 1 - end - if to < 0 then - to = to + len + 1 - end - for i = from, to do - table.insert (m, l[i]) - end - return m -end - ---- Return a list with its first element removed. --- @param l list --- @return {l[2], ..., l[#l]} -local function tail (l) - return slice (l, 2) -end - ---- Fold a binary function through a list left associatively. --- @param f function --- @param e element to place in left-most position --- @param l list --- @return result -local function foldl (f, e, l) - return fold (f, e, elems, l) -end - ---- Fold a binary function through a list right associatively. --- @param f function --- @param e element to place in right-most position --- @param l list --- @return result -local function foldr (f, e, l) - return fold (function (x, y) return f (y, x) end, - e, relems, l) -end - ---- Prepend an item to a list. --- @param l list --- @param x item --- @return {x, unpack (l)} -local function cons (l, x) - return {x, unpack (l)} -end - ---- Append an item to a list. --- @param l list --- @param x item --- @return {l[1], ..., l[#l], x} -local function append (l, x) - local r = {unpack (l)} - table.insert (r, x) - return r -end - ---- Concatenate lists. --- @param ... lists --- @return {l1[1], ..., --- l1[#l1], ..., ln[1], ..., --- ln[#ln]} -local function concat (...) - local r = {} - for l in elems ({...}) do - for v in elems (l) do - table.insert (r, v) - end - end - return r -end - ---- Repeat a list. --- @param l list --- @param n number of times to repeat --- @return n copies of l appended together -local function rep (l, n) - local r = {} - for i = 1, n do - r = concat (r, l) - end - return r -end - ---- Reverse a list. --- @param l list --- @return list {l[#l], ..., l[1]} -local function reverse (l) - local m = {} - for i = #l, 1, -1 do - table.insert (m, l[i]) - end - return m -end - ---- Transpose a list of lists. --- This function in Lua is equivalent to zip and unzip in more --- strongly typed languages. --- @param ls {{l1,1, ..., l1,c}, ..., --- {lr,1, ..., lr,c}} --- @return {{l1,1, ..., lr,1}, ..., --- {l1,c, ..., lr,c}} -local function transpose (ls) - local ms, len = {}, #ls - for i = 1, math.max (unpack (map (function (l) return #l end, ls))) do - ms[i] = {} - for j = 1, len do - ms[i][j] = ls[j][i] - end - end - return ms -end - ---- Zip lists together with a function. --- @param f function --- @param ls list of lists --- @return {f (ls[1][1], ..., ls[#ls][1]), ..., f (ls[1][N], ..., ls[#ls][N]) --- where N = max {map (function (l) return #l end, ls)} -local function zipWith (f, ls) - return mapWith (f, transpose (ls)) -end - ---- Project a list of fields from a list of tables. --- @param f field to project --- @param l list of tables --- @return list of f fields -local function project (f, l) - return map (function (t) return t[f] end, l) -end - ---- Turn a table into a list of pairs. ---
FIXME: Find a better name. --- @param t table {i1=v1, ..., --- in=vn} --- @return list {{i1, v1}, ..., --- {in, vn}} -local function enpair (t) - local ls = {} - for i, v in pairs (t) do - table.insert (ls, {i, v}) - end - return ls -end - ---- Turn a list of pairs into a table. ---
FIXME: Find a better name. --- @param ls list {{i1, v1}, ..., --- {in, vn}} --- @return table {i1=v1, ..., --- in=vn} -local function depair (ls) - local t = {} - for v in elems (ls) do - t[v[1]] = v[2] - end - return t -end - ---- Flatten a list. --- @param l list to flatten --- @return flattened list -local function flatten (l) - local m = {} - for v in ileaves (l) do - table.insert (m, v) - end - return m -end - ---- Shape a list according to a list of dimensions. --- --- Dimensions are given outermost first and items from the original --- list are distributed breadth first; there may be one 0 indicating --- an indefinite number. Hence, {0} is a flat list, --- {1} is a singleton, {2, 0} is a list of --- two lists, and {0, 2} is a list of pairs. ---
--- Algorithm: turn shape into all positive numbers, calculating --- the zero if necessary and making sure there is at most one; --- recursively walk the shape, adding empty tables until the bottom --- level is reached at which point add table items instead, using a --- counter to walk the flattened original list. ---
--- @param s {d1, ..., dn} --- @param l list to reshape --- @return reshaped list --- FIXME: Use ileaves instead of flatten (needs a while instead of a --- for in fill function) -local function shape (s, l) - l = flatten (l) - -- Check the shape and calculate the size of the zero, if any - local size = 1 - local zero - for i, v in ipairs (s) do - if v == 0 then - if zero then -- bad shape: two zeros - return nil - else - zero = i - end - else - size = size * v - end - end - if zero then - s[zero] = math.ceil (#l / size) - end - local function fill (i, d) - if d > #s then - return l[i], i + 1 - else - local t = {} - for j = 1, s[d] do - local e - e, i = fill (i, d + 1) - table.insert (t, e) - end - return t, i - end - end - return (fill (1, 1)) -end - ---- Make an index of a list of tables on a given field --- @param f field --- @param l list of tables {t1, ..., --- tn} --- @return index {t1[f]=1, ..., --- tn[f]=n} -local function indexKey (f, l) - local m = {} - for i, v in ipairs (l) do - local k = v[f] - if k then - m[k] = i - end - end - return m -end - ---- Copy a list of tables, indexed on a given field --- @param f field whose value should be used as index --- @param l list of tables {i1=t1, ..., --- in=tn} --- @return index {t1[f]=t1, ..., --- tn[f]=tn} -local function indexValue (f, l) - local m = {} - for i, v in ipairs (l) do - local k = v[f] - if k then - m[k] = v - end - end - return m -end -permuteOn = indexValue - ---- Compare two lists element by element left-to-right --- @param l first list --- @param m second list --- @return -1 if l is less than m, 0 if they --- are the same, and 1 if l is greater than m -local function compare (l, m) - for i = 1, math.min (#l, #m) do - if l[i] < m[i] then - return -1 - elseif l[i] > m[i] then - return 1 - end - end - if #l < #m then - return -1 - elseif #l > #m then - return 1 - end - return 0 -end - --- Metamethods for lists -local metatable = { - -- list .. table = list.concat - __concat = concat, - -- list == list retains its referential meaning - -- list < list = list.compare returns < 0 - __lt = function (l, m) return compare (l, m) < 0 end, - -- list <= list = list.compare returns <= 0 - __le = function (l, m) return compare (l, m) <= 0 end, - __append = append, -} - ---- List constructor. --- Needed in order to use metamethods. --- @param t list (as a table) --- @return list (with list metamethods) -local function new (l) - return setmetatable (l, metatable) -end - --- Function forms of operators -_G.op[".."] = concat - --- Public interface -local M = { - append = append, - compare = compare, - concat = concat, - cons = cons, - depair = depair, - elems = elems, - enpair = enpair, - filter = filter, - flatten = flatten, - foldl = foldl, - foldr = foldr, - indexKey = indexKey, - indexValue = indexValue, - new = new, - map = map, - mapWith = mapWith, - project = project, - relems = relems, - rep = rep, - reverse = reverse, - shape = shape, - slice = slice, - tail = tail, - transpose = transpose, - zipWith = zipWith, -} - -return M diff --git a/src/math_ext.lua b/src/math_ext.lua deleted file mode 100644 index f328105..0000000 --- a/src/math_ext.lua +++ /dev/null @@ -1,27 +0,0 @@ ---- Additions to the math module. -module ("math", package.seeall) - - -local _floor = floor - ---- Extend math.floor to take the number of decimal places. --- @param n number --- @param p number of decimal places to truncate to (default: 0) --- @return n truncated to p decimal places -function floor (n, p) - if p and p ~= 0 then - local e = 10 ^ p - return _floor (n * e) / e - else - return _floor (n) - end -end - ---- Round a number to a given number of decimal places --- @param n number --- @param p number of decimal places to round to (default: 0) --- @return n rounded to p decimal places -function round (n, p) - local e = 10 ^ (p or 0) - return _floor (n * e + 0.5) / e -end diff --git a/src/mbox.lua b/src/mbox.lua deleted file mode 100644 index 996adb0..0000000 --- a/src/mbox.lua +++ /dev/null @@ -1,59 +0,0 @@ ---- mbox parser. --- Based on code by Diego Nahab. - -local function headers (s) - local header = {} - s = "\n" .. s .. "$$$:\n" - local i, j = 1, 1 - while true do - j = string.find (s, "\n%S-:", i + 1) - if not j then - break - end - local _, _, name, val = string.find (string.sub (s, i + 1, j - 1), - "(%S-):(.*)") - val = string.gsub (val or "", "\r\n", "\n") - val = string.gsub (val, "\n%s*", " ") - name = string.lower (name) - if header[name] then - header[name] = header[name] .. ", " .. val - else - header[name] = val - end - i, j = j, i - end - header["$$$"] = nil - return header -end - -local function message (s) - s = string.gsub (s, "^.-\n", "") - local _, s, body - _, _, s, body = string.find(s, "^(.-\n)\n(.*)") - return {header = headers (s or ""), body = body or ""} -end - ---- Parse a mailbox into messages. --- @param s mailbox as a string --- @return list of messages, each of form {header = {...}, body = "..."} -local function parse (s) - local mbox = {} - s = "\n" .. s .. "\nFrom " - local i, j = 1, 1 - while true do - j = string.find (s, "\nFrom ", i + 1) - if not j then - break - end - table.insert (mbox, message (string.sub (s, i + 1, j - 1))) - i, j = j, i - end - return mbox -end - --- Public interface -local M = { - parse = parse, -} - -return M diff --git a/src/modules.lua b/src/modules.lua deleted file mode 100644 index 1ca0556..0000000 --- a/src/modules.lua +++ /dev/null @@ -1,16 +0,0 @@ -return { - "debug_init", - --"strict", - "base", - "package_ext", - "debug_ext", - "table_ext", - "list", - "tree", - "string_ext", - "math_ext", - "io_ext", - "getopt", - "set", - "strbuf", -} diff --git a/src/object.lua b/src/object.lua deleted file mode 100644 index a52f321..0000000 --- a/src/object.lua +++ /dev/null @@ -1,57 +0,0 @@ ---- Prototype-based objects ---
    ---
  • Create an object/class:
  • ---
      ---
    • Either, if the _init field is a list: ---
        ---
      • object/Class = prototype {value, ...; field = value, ...}
      • ---
      • Named values are assigned to the corresponding fields, and unnamed values --- to the fields given by _init.
      • ---
      ---
    • Or, if the _init field is a function: ---
        ---
      • object/Class = prototype (value, ...)
      • ---
      • The given values are passed as arguments to the _init function.
      • ---
      ---
    • An object's metatable is itself.
    • ---
    • Private fields and methods start with "_".
    • ---
    ---
  • Access an object field: object.field
  • ---
  • Call an object method: object:method (...)
  • ---
  • Call a class method: Class.method (object, ...)
  • ---
  • Add a field: object.field = x
  • ---
  • Add a method: function object:method (...) ... end
  • --- - -require "table_ext" - - ---- Root object --- @class table --- @name Object --- @field _init constructor method or list of fields to be initialised by the --- constructor --- @field _clone object constructor which provides the behaviour for _init --- documented above -local Object = { - _init = {}, - - _clone = function (self, ...) - local object = table.clone (self) - if type (self._init) == "table" then - table.merge (object, table.clone_rename (self._init, ...)) - else - object = self._init (object, ...) - end - return setmetatable (object, object) - end, - - -- Sugar instance creation - __call = function (...) - -- First (...) gets first element of list - return (...)._clone (...) - end, -} -setmetatable (Object, Object) - -return Object diff --git a/src/package_ext.lua b/src/package_ext.lua deleted file mode 100644 index 974139a..0000000 --- a/src/package_ext.lua +++ /dev/null @@ -1,15 +0,0 @@ --- Additions to the package module. -module ("package", package.seeall) - - ---- Make named constants for package.config (undocumented --- in 5.1; see luaconf.h for C equivalents). --- @class table --- @name package --- @field dirsep directory separator --- @field pathsep path separator --- @field path_mark string that marks substitution points in a path template --- @field execdir (Windows only) replaced by the executable's directory in a path --- @field igmark Mark to ignore all before it when building luaopen_ function name. -dirsep, pathsep, path_mark, execdir, igmark = - string.match (package.config, "^([^\n]+)\n([^\n]+)\n([^\n]+)\n([^\n]+)\n([^\n]+)") diff --git a/src/parser.lua b/src/parser.lua deleted file mode 100644 index d93afb4..0000000 --- a/src/parser.lua +++ /dev/null @@ -1,267 +0,0 @@ ---- Parser generator. ---

    A parser is created by

    ---
    ---

    p = Parser {grammar}

    ---
    ---

    and called with

    ---
    ---

    result = p:parse (start_token, token_list[, --- from])

    ---
    ---

    where start_token is the non-terminal at which to start parsing --- in the grammar, token_list is a list of tokens of the form

    ---
    ---

    {ty = "token_type", tok = "token_text"}

    ---
    ---

    and from is the token in the list from which to start (the --- default value is 1).

    ---

    The output of the parser is a tree, each of whose --- nodes is of the form:

    ---
    ---

    {ty = symbol, node1 = tree1, --- node2 = tree2, ... [, list]}

    ---
    ---

    where each nodei is a symbolic name, and --- list is the list of trees returned if the corresponding token was a --- list token.

    ---

    A grammar is a table of rules of the form

    ---
    ---

    non-terminal = {production1, --- production2, ...}

    ---
    ---

    plus a special item

    ---
    ---

    lexemes = set.new {"class1", "class2", --- ...}

    ---
    ---

    Each production gives a form that a non-terminal may take. A --- production has the form

    ---
    ---

    production = {"token1", "token2", --- ..., [action][,abstract]}

    ---
    ---

    A production

    ---
      ---
    • must not start with the non-terminal being defined (it must not --- be left-recursive)
    • ---
    • must not be a prefix of a later production in the same --- non-terminal
    • ---
    ---

    Each token may be

    ---
      ---
    • a non-terminal, i.e. a token defined by the grammar
    • ---
        ---
      • an optional symbol is indicated by the suffix _opt
      • ---
      • a list is indicated by the suffix _list, and may be --- followed by _≤separator-symbol> (default is no separator)
      • ---
      ---
    • a lexeme class
    • ---
    • a string to match literally
    • ---
    ---

    The parse tree for a literal string or lexeme class is the string --- that was matched. The parse tree for a non-terminal is a table of --- the form

    ---
    ---

    {ty = "non_terminal_name", tree1, --- tree2, ...}

    ---
    ---

    where the treei are the parse trees for the --- corresponding terminals and non-terminals.

    ---

    An action is of the form

    ---
    ---

    action = function (tree, token, pos) ... return tree_ --- end

    ---
    ---

    It is passed the parse tree for the current node, the token list, --- and the current position in the token list, and returns a new parse --- tree.

    ---

    An abstract syntax rule is of the form

    ---
    ---

    name = {i1, i2, ...}

    ---
    ---

    where i1, i2, --- ... are numbers. This results in a parse tree of the form

    ---
    ---

    {ty = "name"; treei1, --- treei2, ...}

    ---
    ---

    If a production has no abstract syntax rule, the result is the --- parse node for the current node.

    ---

    FIXME: Give lexemes as an extra argument to Parser? ---
    FIXME: Rename second argument to parse method to "tokens"? ---
    FIXME: Make start_token an optional argument to parse? (swap with --- token list) and have it default to the first non-terminal?

    - -local Object = require "object" - - -local Parser = Object {_init = {"grammar"}} - - ---- Parser constructor --- @param grammar parser grammar --- @return parser -function Parser:_init (grammar) - local init = table.clone_rename ({"grammar"}, grammar) - -- Reformat the abstract syntax rules - for rname, rule in pairs (init.grammar) do - if name ~= "lexemes" then - for pnum, prod in ipairs (rule) do - local abstract - for i, v in pairs (prod) do - if type (i) == "string" and i ~= "action" then - if abstract then - die ("more than one abstract rule for " .. rname .. "." - .. tostring (pnum)) - else - if type (v) ~= "table" then - die ("bad abstract syntax rule of type " .. type (v)) - end - abstract = {ty = i, template = v} - prod[i] = nil - end - end - end - if abstract then - prod.abstract = abstract - end - end - end - end - return table.merge (self, init) -end - ---- Parse a token list. --- @param start the token at which to start --- @param token the list of tokens --- @param from the index of the token to start from (default: 1) --- @return parse tree -function Parser:parse (start, token, from) - - local grammar = self.grammar -- for consistency and brevity - local rule, symbol -- functions called before they are defined - - -- Try to parse an optional symbol. - -- @param sym the symbol being tried - -- @param from the index of the token to start from - -- @return the resulting parse tree, or false if empty - -- @return the index of the first unused token, or false to - -- indicate failure - local function optional (sym, from) - local tree, to = symbol (sym, from) - if to then - return tree, to - else - return false, from - end - end - - -- Try to parse a list of symbols. - -- @param sym the symbol being tried - -- @param sep the list separator - -- @param from the index of the token to start from - -- @return the resulting parse tree, or false if empty - -- @return the index of the first unused token, or false to - -- indicate failure - local function list (sym, sep, from) - local tree, to - tree, from = symbol (sym, from) - local list = {tree} - if from == false then - return list, false - end - to = from - repeat - if sep ~= "" then - tree, from = symbol (sep, from) - end - if from then - tree, from = symbol (sym, from) - if from then - table.insert (list, tree) - to = from - end - end - until from == false - return list, to - end - - -- Try to parse a given symbol. - -- @param sym the symbol being tried - -- @param from the index of the token to start from - -- @return tree the resulting parse tree, or false if empty - -- @return the index of the first unused token, or false to - -- indicate failure - symbol = function (sym, from) -- declared at the top - if string.sub (sym, -4, -1) == "_opt" then -- optional symbol - return optional (string.sub (sym, 1, -5), from) - elseif string.find (sym, "_list.-$") then -- list - local _, _, subsym, sep = string.find (sym, "^(.*)_list_?(.-)$") - return list (subsym, sep, from) - elseif grammar[sym] then -- non-terminal - return rule (sym, from) - elseif token[from] and -- not end of token list - ((grammar.lexemes:member (sym) and sym == token[from].ty) or -- lexeme - sym == token[from].tok) -- literal terminal - then - return token[from].tok, from + 1 -- advance to next token - else - return false, false - end - end - - -- Try a production. - -- @param name the name of the current rule - -- @param prod the production (list of symbols) being tried - -- @param from the index of the token to start from - -- @return the parse tree (incomplete if to is false) - -- @return the index of the first unused token, or false to - -- indicate failure - local function production (name, prod, from) - local tree = {ty = name} - local to = from - for prod in _G.list.elems (prod) do - local sym - sym, to = symbol (prod, to) - if to then - table.insert (tree, sym) - else - return tree, false - end - end - if prod.action then - tree = prod.action (tree, token, to) - end - if prod.abstract then - local ntree = {} - ntree.ty = prod.abstract.ty - for i, n in pairs (prod.abstract.template) do - ntree[i] = tree[n] - end - tree = ntree - end - return tree, to - end - - -- Parse according to a particular rule. - -- @param name the name of the rule to try - -- @param from the index of the token to start from - -- @return parse tree - -- @return the index of the first unused token, or false to - -- indicate failure - rule = function (name, from) -- declared at the top - local alt = grammar[name] - local tree, to - for alt in _G.list.elems (alt) do - tree, to = production (name, alt, from) - if to then - return tree, to - end - end - return tree, false - end - - return rule (start, 1, from or 1) -end - -return Parser diff --git a/src/set.lua b/src/set.lua deleted file mode 100644 index 763759e..0000000 --- a/src/set.lua +++ /dev/null @@ -1,167 +0,0 @@ -local list = require "list" - - --- Primitive methods (know about representation) --- The representation is a table whose tags are the elements, and --- whose values are true. - ---- Say whether an element is in a set --- @param s set --- @param e element --- @return true if e is in set, false --- otherwise -local function member (s, e) - return rawget (s.contents, e) == true -end - ---- Insert an element into a set --- @param s set --- @param e element -local function insert (s, e) - rawset (s.contents, e, true) -end - ---- Delete an element from a set --- @param s set --- @param e element -local function delete (s, e) - rawset (s.contents, e, nil) -end - ---- Make a list into a set --- @param l list --- @return set -local metatable = {} -local function new (l) - local s = setmetatable ({contents={}}, metatable) - for e in list.elems (l) do - insert (s, e) - end - return s -end - ---- Iterator for sets --- TODO: Make the iterator return only the key -local function elems (s) - return pairs (s.contents) -end - - --- High level methods (representation-independent) - -local difference, symmetric_difference, intersection, union, subset, equal - ---- Find the difference of two sets --- @param s set --- @param t set --- @return s with elements of t removed -function difference (s, t) - local r = new {} - for e in elems (s) do - if not member (t, e) then - insert (r, e) - end - end - return r -end - ---- Find the symmetric difference of two sets --- @param s set --- @param t set --- @return elements of s and t that are in s or t but not both -function symmetric_difference (s, t) - return difference (union (s, t), intersection (t, s)) -end - ---- Find the intersection of two sets --- @param s set --- @param t set --- @return set intersection of s and t -function intersection (s, t) - local r = new {} - for e in elems (s) do - if member (t, e) then - insert (r, e) - end - end - return r -end - ---- Find the union of two sets --- @param s set --- @param t set --- @return set union of s and t -function union (s, t) - local r = new {} - for e in elems (s) do - insert (r, e) - end - for e in elems (t) do - insert (r, e) - end - return r -end - ---- Find whether one set is a subset of another --- @param s set --- @param t set --- @return true if s is a subset of t, false --- otherwise -function subset (s, t) - for e in elems (s) do - if not member (t, e) then - return false - end - end - return true -end - ---- Find whether one set is a proper subset of another --- @param s set --- @param t set --- @return true if s is a proper subset of t, false otherwise -function propersubset (s, t) - return subset (s, t) and not subset (t, s) -end - ---- Find whether two sets are equal --- @param s set --- @param t set --- @return true if sets are equal, false --- otherwise -function equal (s, t) - return subset (s, t) and subset (t, s) -end - --- Public interface -local M = { - delete = delete, - difference = difference, - elems = elems, - equal = equal, - insert = insert, - intersection = intersection, - member = member, - new = new, - subset = subset, - symmetric_difference = symmetric_difference, - union = union, -} - ---- Metamethods for sets --- set:method () -metatable.__index = M --- set + table = union -metatable.__add = union --- set - table = set difference -metatable.__sub = difference --- set * table = intersection -metatable.__mul = intersection --- set / table = symmetric difference -metatable.__div = symmetric_difference --- set <= table = subset -metatable.__le = subset --- set < table = proper subset -metatable.__lt = propersubset - -return M diff --git a/src/std.lua.in b/src/std.lua.in deleted file mode 100644 index 312bce9..0000000 --- a/src/std.lua.in +++ /dev/null @@ -1,20 +0,0 @@ ---- Lua standard library ---
      ---
    • 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: Add tests for each function immediately after the function; --- this also helps to check module dependencies.
    • ---
    • TODO: pre-compile.
    • ---
    -local version = "General Lua libraries / @VERSION@" - -for _, m in ipairs (require "modules") do - _G[m] = require (m) -end - -local M = { - version = version, -} - -return M diff --git a/src/strbuf.lua b/src/strbuf.lua deleted file mode 100644 index 66d77cf..0000000 --- a/src/strbuf.lua +++ /dev/null @@ -1,41 +0,0 @@ ---- String buffers - ---- Create a new string buffer -local metatable = {} -local function new () - return setmetatable ({}, metatable) -end - ---- Add a string to a buffer --- @param b buffer --- @param s string to add --- @return buffer -local function concat (b, s) - table.insert (b, s) - return b -end - ---- Convert a buffer to a string --- @param b buffer --- @return string -local function tostring (b) - return table.concat (b) -end - - --- Public interface -local M = { - concat = concat, - new = new, - tostring = tostring, -} - ---- Metamethods for string buffers --- buffer:method () -metatable.__index = M --- buffer .. string -metatable.__concat = concat --- tostring -metatable.__tostring = tostring - -return M diff --git a/src/strict.lua b/src/strict.lua deleted file mode 100644 index 1eb22c7..0000000 --- a/src/strict.lua +++ /dev/null @@ -1,40 +0,0 @@ ---- Checks uses of undeclared global variables. --- All global variables must be 'declared' through a regular --- assignment (even assigning nil will do) in a top-level --- chunk before being used anywhere or assigned to inside a function. --- From Lua distribution (etc/strict.lua). --- @class module --- @name strict - -local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget - -local mt = getmetatable (_G) -if mt == nil then - mt = {} - setmetatable (_G, mt) -end - -mt.__declared = {} - -local function what () - local d = getinfo (3, "S") - return d and d.what or "C" -end - -mt.__newindex = function (t, n, v) - if not mt.__declared[n] then - local w = what () - if w ~= "main" and w ~= "C" then - error ("assignment to undeclared variable '" .. n .. "'", 2) - end - mt.__declared[n] = true - end - rawset (t, n, v) -end - -mt.__index = function (t, n) - if not mt.__declared[n] and what () ~= "C" then - error ("variable '" .. n .. "' is not declared", 2) - end - return rawget (t, n) -end diff --git a/src/string_ext.lua b/src/string_ext.lua deleted file mode 100644 index a24c9f4..0000000 --- a/src/string_ext.lua +++ /dev/null @@ -1,274 +0,0 @@ ---- Additions to the string module --- TODO: Pretty printing (use in getopt); see source for details. -module ("string", package.seeall) - - --- Write pretty-printing based on: --- --- John Hughes's and Simon Peyton Jones's Pretty Printer Combinators --- --- Based on "The Design of a Pretty-printing Library in Advanced --- Functional Programming", Johan Jeuring and Erik Meijer (eds), LNCS 925 --- http://www.cs.chalmers.se/~rjmh/Papers/pretty.ps --- Heavily modified by Simon Peyton Jones, Dec 96 --- --- Haskell types: --- data Doc list of lines --- quote :: Char -> Char -> Doc -> Doc Wrap document in ... --- (<>) :: Doc -> Doc -> Doc Beside --- (<+>) :: Doc -> Doc -> Doc Beside, separated by space --- ($$) :: Doc -> Doc -> Doc Above; if there is no overlap it "dovetails" the two --- nest :: Int -> Doc -> Doc Nested --- punctuate :: Doc -> [Doc] -> [Doc] punctuate p [d1, ... dn] = [d1 <> p, d2 <> p, ... dn-1 <> p, dn] --- render :: Int Line length --- -> Float Ribbons per line --- -> (TextDetails -> a -> a) What to do with text --- -> a What to do at the end --- -> Doc The document --- -> a Result - - ---- Give strings a subscription operator. --- @param s string --- @param i index --- @return string.sub (s, i, i) if i is a number, or --- falls back to any previous metamethod (by default, string methods) -local old__index = getmetatable ("").__index -getmetatable ("").__index = function (s, i) - if type (i) == "number" then - return sub (s, i, i) - -- Fall back to old metamethods - elseif type (old__index) == "function" then - return old__index (s, i) - else - return old__index[i] - end -end - ---- Give strings an append metamethod. --- @param s string --- @param c character (1-character string) --- @return s .. c -getmetatable ("").__append = function (s, c) - return s .. c -end - ---- Give strings a concat metamethod. --- @param s string --- @param o object --- @return s .. tostring (o) -getmetatable ("").__concat = function (s, o) - return tostring (s) .. tostring (o) -end - ---- Capitalise each word in a string. --- @param s string --- @return capitalised string -function caps (s) - return (gsub (s, "(%w)([%w]*)", - function (l, ls) - return upper (l) .. ls - end)) -end - ---- Remove any final newline from a string. --- @param s string to process --- @return processed string -function chomp (s) - return (gsub (s, "\n$", "")) -end - ---- Escape a string to be used as a pattern --- @param s string to process --- @return --- @param s_: processed string -function escapePattern (s) - return (gsub (s, "(%W)", "%%%1")) -end - --- Escape a string to be used as a shell token. --- Quotes spaces, parentheses, brackets, quotes, apostrophes and --- whitespace. --- @param s string to process --- @return processed string -function escapeShell (s) - return (gsub (s, "([ %(%)%\\%[%]\"'])", "\\%1")) -end - ---- Return the English suffix for an ordinal. --- @param n number of the day --- @return suffix -function ordinalSuffix (n) - n = math.mod (n, 100) - local d = math.mod (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 - ---- Extend to work better with one argument. --- If only one argument is passed, no formatting is attempted. --- @param f format --- @param ... arguments to format --- @return formatted string -local _format = format -function format (f, arg1, ...) - if arg1 == nil then - return f - else - return _format (f, arg1, ...) - end -end - ---- Justify a string. --- When the string is longer than w, it is truncated (left or right --- according to the sign of w). --- @param s string to justify --- @param w width to justify to (-ve means right-justify; +ve means --- left-justify) --- @param p string to pad with (default: " ") --- @return justified string -function pad (s, w, p) - p = rep (p or " ", math.abs (w)) - if w < 0 then - return sub (p .. s, w) - end - return sub (s .. p, 1, w) -end - ---- Wrap a string into a paragraph. --- @param s string to wrap --- @param w width to wrap to (default: 78) --- @param ind indent (default: 0) --- @param ind1 indent of first line (default: ind) --- @return wrapped paragraph -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") - s = rep (" ", ind1) .. s - local lstart, len = 1, len (s) - while len - lstart > w - ind do - local i = lstart + w - ind - while i > lstart and sub (s, i, i) ~= " " do - i = i - 1 - end - local j = i - while j > lstart and sub (s, j, j) == " " do - j = j - 1 - end - s = sub (s, 1, j) .. "\n" .. rep (" ", ind) .. - sub (s, i + 1, -1) - local change = ind + 1 - (i - j) - lstart = j + change - len = len + change - end - return s -end - ---- Write a number using SI suffixes. --- The number is always written to 3 s.f. --- @param n number --- @return string -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 = t:find(".(.%...)e(.+)") - local man, exp = tonumber (m), tonumber (e) - local siexp = math.floor (exp / 3) - local shift = exp - siexp * 3 - local s = SIprefix[siexp] or "e" .. tostring (siexp) - man = man * (10 ^ shift) - return tostring (man) .. s -end - ---- Do find, returning captures as a list. --- @param s target string --- @param p pattern --- @param init start position (default: 1) --- @param plain inhibit magic characters (default: nil) --- @return start of match, end of match, table of captures -function tfind (s, p, init, plain) - local function pack (from, to, ...) - return from, to, {...} - end - return pack (p.find (s, p, init, plain)) -end - ---- Do multiple finds on a string. --- @param s target string --- @param p pattern --- @param init start position (default: 1) --- @param plain inhibit magic characters (default: nil) --- @return list of {from, to; capt = {captures}} -function finds (s, p, init, plain) - init = init or 1 - local l = {} - local from, to, r - repeat - from, to, r = tfind (s, p, init, plain) - if from ~= nil then - table.insert (l, {from, to, capt = r}) - init = to + 1 - end - until not from - return l -end - ---- Split a string at a given separator. --- FIXME: Consider Perl and Python versions. --- @param s string to split --- @param sep separator regex --- @return list of strings -function split (s, sep) - -- finds gets a list of {from, to, capt = {}} lists; we then - -- flatten the result, discarding the captures, and prepend 0 (1 - -- before the first character) and append 0 (1 after the last - -- character), and then read off the result in pairs. - local pairs = list.concat ({0}, list.flatten (finds (s, sep)), {0}) - local l = {} - for i = 1, #pairs, 2 do - table.insert (l, sub (s, pairs[i] + 1, pairs[i + 1] - 1)) - end - return l -end - ---- Remove leading matter from a string. --- @param s string --- @param r leading regex (default: "%s+") --- @return string without leading r -function ltrim (s, r) - r = r or "%s+" - return (gsub (s, "^" .. r, "")) -end - ---- Remove trailing matter from a string. --- @param s string --- @param r trailing regex (default: "%s+") --- @return string without trailing r -function rtrim (s, r) - r = r or "%s+" - return (gsub (s, r .. "$", "")) -end - ---- Remove leading and trailing matter from a string. --- @param s string --- @param r leading/trailing regex (default: "%s+") --- @return string without leading/trailing r -function trim (s, r) - return rtrim (ltrim (s, r), r) -end diff --git a/src/table_ext.lua b/src/table_ext.lua deleted file mode 100644 index c2eb4ad..0000000 --- a/src/table_ext.lua +++ /dev/null @@ -1,117 +0,0 @@ --- Extensions to the table module -module ("table", package.seeall) - ---local list = require "list" FIXME: allow require loops - - -local _sort = sort ---- Make table.sort return its result. --- @param t table --- @param c comparator function --- @return sorted table -function sort (t, c) - _sort (t, c) - return t -end - ---- Return whether table is empty. --- @param t table --- @return true if empty or false otherwise -function empty (t) - return not next (t) -end - ---- Find the number of elements in a table. --- @param t table --- @return number of elements in t -function size (t) - local n = 0 - for _ in pairs (t) do - n = n + 1 - end - return n -end - ---- Make the list of keys of a table. --- @param t table --- @return list of keys -function keys (t) - local u = {} - for i, v in pairs (t) do - insert (u, i) - end - return u -end - ---- Make the list of values of a table. --- @param t table --- @return list of values -function values (t) - local u = {} - for i, v in pairs (t) do - insert (u, v) - end - return u -end - ---- Invert a table. --- @param t table {i=v, ...} --- @return inverted table {v=i, ...} -function invert (t) - local u = {} - for i, v in pairs (t) do - u[v] = i - end - return u -end - ---- Make a shallow copy of a table, including any metatable (for a --- deep copy, use tree.clone). --- @param t table --- @param nometa if non-nil don't copy metatable --- @return copy of table -function clone (t, nometa) - local u = {} - if not nometa then - setmetatable (u, getmetatable (t)) - end - for i, v in pairs (t) do - u[i] = v - end - return u -end - ---- Clone a table, renaming some keys. --- @param map table {old_key=new_key, ...} --- @param t table to copy --- @return copy of table -function clone_rename (map, t) - local r = clone (t) - for i, v in pairs (map) do - r[v] = t[i] - r[i] = nil - end - return r -end - ---- Merge one table into another. u is merged into t. --- @param t first table --- @param u second table --- @return first table -function merge (t, u) - for i, v in pairs (u) do - t[i] = v - end - return t -end - ---- Make a table with a default value for unset keys. --- @param x default entry value (default: nil) --- @param t initial table (default: {}) --- @return table whose unset elements are x -function new (x, t) - return setmetatable (t or {}, - {__index = function (t, i) - return x - end}) -end diff --git a/src/tree.lua b/src/tree.lua deleted file mode 100644 index 1f1159b..0000000 --- a/src/tree.lua +++ /dev/null @@ -1,102 +0,0 @@ ---- Tables as trees. -local list = require "list" - - -local metatable = {} ---- Make a table into a tree --- @param t table --- @return tree -local function new (t) - return setmetatable (t or {}, metatable) -end - ---- Tree __index metamethod. --- @param tr tree --- @param i non-table, or list of keys {i1 ... --- in} --- @return tr[i]...[in] if i is a table, or --- tr[i] otherwise -function metatable.__index (tr, i) - -- FIXME: the following doesn't treat list keys correctly - -- e.g. tr[{{1, 2}, {3, 4}}], maybe flatten first? - if type (i) == "table" and #i > 0 then - return list.foldl (op["[]"], tr, i) - else - return rawget (tr, i) - end -end - ---- Tree __newindex metamethod. --- Sets tr[i1]...[in] = v if i is a --- table, or tr[i] = v otherwise --- @param tr tree --- @param i non-table, or list of keys {i1 ... --- in} --- @param v value -function metatable.__newindex (tr, i, v) - if type (i) == "table" then - for n = 1, #i - 1 do - if getmetatable (tr[i[n]]) ~= metatable then - rawset (tr, i[n], new ()) - end - tr = tr[i[n]] - end - rawset (tr, i[#i], v) - else - rawset (tr, i, v) - end -end - ---- Make a deep copy of a tree, including any metatables --- @param t table --- @param nometa if non-nil don't copy metatables --- @return copy of table -local function clone (t, nometa) - local r = {} - if not nometa then - setmetatable (r, getmetatable (t)) - end - local d = {[t] = r} - local function copy (o, x) - for i, v in pairs (x) do - if type (v) == "table" then - if not d[v] then - d[v] = {} - if not nometa then - setmetatable (d[v], getmetatable (v)) - end - o[i] = copy (d[v], v) - else - o[i] = d[v] - end - else - o[i] = v - end - end - return o - end - return copy (r, t) -end - ---- Deep-merge one tree into another. u is merged into ---- t. --- @param t first tree --- @param u second tree --- @return first tree -local function merge (t, u) - for ty, p, n in nodes (u) do - if ty == "leaf" then - t[p] = n - end - end - return t -end - --- Public interface -local M = { - clone = clone, - merge = merge, - new = new, -} - -return M diff --git a/src/xml.lua b/src/xml.lua deleted file mode 100644 index 14a2203..0000000 --- a/src/xml.lua +++ /dev/null @@ -1,75 +0,0 @@ --- XML extensions to string module. --- @class module --- @name xml - -require "base" -require "string_ext" - - ---- Write a table as XML. --- The input format is assumed to be that output by luaexpat. --- @param t table to print. --- In each element, tag is its name, attr is the table of attributes, --- and the sub-elements are held in the integer keys --- @param indent indent between levels (default: "\t") --- @param spacing space before every line --- @returns XML string -function string.writeXML (t, indent, spacing) - indent = indent or "\t" - spacing = spacing or "" - return render (t, - function (x) - spacing = spacing .. indent - if x.tag then - local s = "<" .. x.tag - if type (x.attr) == "table" then - for i, v in pairs (x.attr) do - if type (i) ~= "number" then - -- luaexpat gives names of attributes in list elements - s = s .. " " .. tostring (i) .. "=" .. string.format ("%q", tostring (v)) - end - end - end - if #x == 0 then - s = s .. " /" - end - s = s .. ">" - return s - end - return "" - end, - function (x) - spacing = string.gsub (spacing, indent .. "$", "") - if x.tag and #x > 0 then - return spacing .. "" - end - return "" - end, - function (s) - s = tostring (s) - s = string.gsub (s, "&([%S]+)", - function (s) - if not string.match (s, "^#?%w+;") then - return "&" .. s - else - return "&" .. s - end - end) - s = string.gsub (s, "<", "<") - s = string.gsub (s, ">", ">") - return s - end, - function (x, i, v, is, vs) - local s = "" - if type (i) == "number" then - s = spacing .. vs - end - return s - end, - function (_, i, _, j) - if type (i) == "number" or type (j) == "number" then - return "\n" - end - return "" - end) -end diff --git a/stdlib-41.2.2-1.rockspec b/stdlib-41.2.2-1.rockspec new file mode 100644 index 0000000..7be7654 --- /dev/null +++ b/stdlib-41.2.2-1.rockspec @@ -0,0 +1,40 @@ +package = "stdlib" +version = "41.2.2-1" +description = { + detailed = "stdlib is a library of modules for common programming tasks, including list, table and functional operations, objects, pickling, pretty-printing and command-line option parsing.", + homepage = "http://lua-stdlib.github.io/lua-stdlib", + license = "MIT/X11", + summary = "General Lua Libraries", +} +source = { + dir = "lua-stdlib-release-v41.2.2", + url = "http://github.com/lua-stdlib/lua-stdlib/archive/release-v41.2.2.zip", +} +dependencies = { + "lua >= 5.1, < 5.5", +} +external_dependencies = nil +build = { + modules = { + std = "lib/std.lua", + ["std.base"] = "lib/std/base.lua", + ["std.container"] = "lib/std/container.lua", + ["std.debug"] = "lib/std/debug.lua", + ["std.debug_init"] = "lib/std/debug_init/init.lua", + ["std.functional"] = "lib/std/functional.lua", + ["std.io"] = "lib/std/io.lua", + ["std.list"] = "lib/std/list.lua", + ["std.math"] = "lib/std/math.lua", + ["std.object"] = "lib/std/object.lua", + ["std.operator"] = "lib/std/operator.lua", + ["std.optparse"] = "lib/std/optparse.lua", + ["std.package"] = "lib/std/package.lua", + ["std.set"] = "lib/std/set.lua", + ["std.strbuf"] = "lib/std/strbuf.lua", + ["std.strict"] = "lib/std/strict.lua", + ["std.string"] = "lib/std/string.lua", + ["std.table"] = "lib/std/table.lua", + ["std.tree"] = "lib/std/tree.lua", + }, + type = "builtin", +} diff --git a/stdlib.rockspec.in b/stdlib.rockspec.in deleted file mode 100644 index bc69d01..0000000 --- a/stdlib.rockspec.in +++ /dev/null @@ -1,25 +0,0 @@ -package="stdlib" -version="@VERSION@-1" -source = { - url = "git://github.com/rrthomas/lua-stdlib.git", - branch = "release-v@VERSION@", -} -description = { - summary = "General Lua libraries", - detailed = [[ - stdlib is a library of modules for common programming tasks, - including list, table and functional operations, regexps, objects, - pickling, pretty-printing and getopt. - ]], - homepage = "http://github.com/rrthomas/lua-stdlib/", - license = "MIT/X11" -} -dependencies = { - "lua >= @LUA_MIN_VERSION@" -} -build = { - type = "command", - build_command = "LUA=$(LUA) CPPFLAGS=-I$(LUA_INCDIR) ./configure --prefix=$(PREFIX) --libdir=$(LIBDIR) --datadir=$(LUADIR) && make clean && make", - install_command = "make install", - copy_directories = {} -} diff --git a/template.lua b/template.lua deleted file mode 100644 index 1d2abe7..0000000 --- a/template.lua +++ /dev/null @@ -1,33 +0,0 @@ -#! /usr/bin/env lua -prog = { - name = "", - banner = " VERSION (DATE) by AUTHOR )", - purpose = "", -} - - -require "std" - - --- Process a file -function main (file, number) -end - - --- Command-line options -options = { - Option {{"test", "t"}, - "test option"}, -} - --- Main routine -getopt.processArgs () -if table.getn (arg) == 0 then - getopt.dieWithUsage () -end -io.processFiles (main) - - --- Changelog - --- 0.1 Program started diff --git a/travis.yml.in b/travis.yml.in new file mode 100644 index 0000000..b1762c8 --- /dev/null +++ b/travis.yml.in @@ -0,0 +1,154 @@ +language: c + +env: + global: + - _COMPILE="libtool --mode=compile --tag=CC gcc" + - _CFLAGS="-O2 -Wall -DLUA_COMPAT_ALL -DLUA_COMPAT_5_2 -DLUA_USE_LINUX" + - _INSTALL="libtool --mode=install install -p" + - _LINK="libtool --mode=link --tag=CC gcc" + - _LIBS="-lm -Wl,-E -ldl -lreadline" + + - prefix=/usr/local + - bindir=$prefix/bin + - incdir=$prefix/include + - libdir=$prefix/lib + + - _inst=$TRAVIS_BUILD_DIR/_inst + - luadir=$_inst/share/lua + - luaexecdir=$_inst/lib/lua + matrix: + - LUA=lua5.3 + - LUA=lua5.2 + - LUA=lua5.1 + - LUA=luajit + + +before_install: + # Put back the links for libyaml, which are missing on recent Travis VMs + - test -f /usr/lib/libyaml.so || + sudo find /usr/lib -name 'libyaml*' -exec ln -s {} /usr/lib \; + - sudo apt-get install help2man + + # Fetch Lua sources. + - cd $TRAVIS_BUILD_DIR + - 'if test lua5.3 = "$LUA"; then + curl http://www.lua.org/ftp/lua-5.3.0.tar.gz | tar xz; + cd lua-5.3.0; + fi' + - 'if test lua5.2 = "$LUA"; then + curl http://www.lua.org/ftp/lua-5.2.4.tar.gz | tar xz; + cd lua-5.2.4; + fi' + - 'if test lua5.1 = "$LUA"; then + curl http://www.lua.org/ftp/lua-5.1.5.tar.gz | tar xz; + cd lua-5.1.5; + fi' + + # Unpack, compile and install Lua. + - 'if test luajit = "$LUA"; then + curl http://luajit.org/download/LuaJIT-2.0.3.tar.gz | tar xz; + cd LuaJIT-2.0.3; + make && sudo make install; + for header in lua.h luaconf.h lualib.h lauxlib.h luajit.h lua.hpp; do + if test -f /usr/local/include/luajit-2.0/$header; then + sudo ln -s /usr/local/include/luajit-2.0/$header /usr/local/include/$header; + fi; + done; + else + for src in src/*.c; do + test src/lua.c = "$src" || test src/luac.c = "$src" || eval $_COMPILE $_CFLAGS -c $src; + done; + eval $_LINK -o lib$LUA.la -version-info 0:0:0 -rpath $libdir *.lo; + sudo mkdir -p $libdir; + eval sudo $_INSTALL lib$LUA.la $libdir/lib$LUA.la; + + eval $_COMPILE $_CFLAGS -c src/lua.c; + eval $_LINK -static -o $LUA lua.lo lib$LUA.la $_LIBS; + sudo mkdir -p $bindir; + eval sudo $_INSTALL $LUA $bindir/$LUA; + + sudo mkdir -p $incdir; + for header in lua.h luaconf.h lualib.h lauxlib.h lua.hpp; do + if test -f src/$header; then + eval sudo $_INSTALL src/$header $incdir/$header; + fi; + done; + fi' + + # Fetch LuaRocks. + - cd $TRAVIS_BUILD_DIR + - 'git clone https://github.com/keplerproject/luarocks.git luarocks-2.2.0' + - cd luarocks-2.2.0 + - git checkout v2.2.0 + + # Compile and install luarocks. + - if test luajit = "$LUA"; then + ./configure --lua-suffix=jit; + else + ./configure; + fi + - 'make build && sudo make install' + + # Tidy up file droppings. + - cd $TRAVIS_BUILD_DIR + - rm -rf lua-5.3.0 lua-5.2.3 lua-5.1.5 LuaJIT-2.0.3 luarocks-2.2.0 + + +install: + # Use Lua 5.3 compatible rocks, where available. + - 'for rock in @EXTRA_ROCKS@""; do + if test -z "$rock"; then break; fi; + if luarocks list | grep "^$rock$" >/dev/null; then continue; fi; + sudo luarocks install --server=http://rocks.moonscript.org/manifests/gvvaughan $rock; + done' + + # Fudge timestamps on release branches. + - 'if test -f configure; then + test -f aclocal.m4 && touch aclocal.m4; + sleep 1; touch Makefile.in; + sleep 1; test -f config.h.in && touch config.h.in; + sleep 1; touch configure; + fi' + + # Build from rockspec, forcing uninstall of older luarocks installed + # above when testing the git rockspec, both for enforcing backwards + # compatibility by default, and for ease of maintenance. + - if test -f '@PACKAGE@-@VERSION@-1.rockspec'; then + sudo luarocks make '@PACKAGE@-@VERSION@-1.rockspec' LUA="$LUA"; + else + sudo luarocks make --force '@PACKAGE@-git-1.rockspec' LUA="$LUA"; + fi + + # Clean up files created by root + - sudo git clean -dfx + - sudo rm -rf slingshot /tmp/ldoc + + +script: + # Reconfigure for in-tree test install. + - test -f configure || ./bootstrap --verbose + - ./configure --prefix="$_inst" --disable-silent-rules LUA="$LUA" + + # Verify luarocks installation. + - make installcheck || make installcheck V=1 + + # Verify local build. + - make + - make check || make check V=1 + + # Verify configured installation. + - make install prefix="$_inst" luadir="$luadir" luaexecdir="$luaexecdir" + - LUA_PATH="$luadir/?.lua;$luadir/?/init.lua;;" + LUA_CPATH="$luaexecdir/?.so;;" + make installcheck V=1 + + +# Run sanity checks on CI server, ignoring buggy automakes. +after_success: + - '{ _assign="="; + if grep local-checks-to-skip build-aux/sanity-cfg.mk >/dev/null; then + _assign="+="; + fi; + printf "local-checks-to-skip %s sc_vulnerable_makefile_CVE-2012-3386\n" "$_assign"; + } >> build-aux/sanity-cfg.mk' + - 'make syntax-check || : this will usually fail on the release branch'