diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml deleted file mode 100644 index 484a51b..0000000 --- a/.github/workflows/spec.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: spec - -on: - push: - branches: [ '*' ] - pull_request: - branches: [ 'master' ] - -jobs: - test: - strategy: - fail-fast: false - matrix: - lua-version: ["5.4", "5.3", "5.2", "5.1", "luajit"] - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - uses: leafo/gh-actions-lua@v8.0.0 - with: - luaVersion: ${{ matrix.lua-version }} - - - uses: leafo/gh-actions-luarocks@v4.0.0 - - - name: install - run: | - sudo apt-get install -y libyaml-dev - luarocks install ansicolors - luarocks install ldoc - luarocks install luacov - luarocks install specl - - - name: build - run: | - make all doc - luarocks make - - - name: test - run: | - make check SPECL_OPTS='-vfreport --coverage' - bash <(curl -s https://codecov.io/bash) -f luacov.report.out diff --git a/.gitignore b/.gitignore index f57d2b9..6009d70 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,3 @@ -*~ .DS_Store -/*.src.rock -/build-aux/config.ld /doc -/luacov.*.out -/lib/std/prototype/version.lua -/prototype-*.tar.gz +!/doc/config.ld.in diff --git a/.luacov b/.luacov deleted file mode 100644 index b44eaef..0000000 --- a/.luacov +++ /dev/null @@ -1,51 +0,0 @@ -return { - -- filename to store stats collected - ["statsfile"] = "luacov.stats.out", - - -- filename to store report - ["reportfile"] = "luacov.report.out", - - -- luacov.stats file updating frequency. - -- The lower this value - the more frequenty results will be written out to luacov.stats - -- You may want to reduce this value for short lived scripts (to for example 2) to avoid losing coverage data. - ["savestepsize"] = 100, - - -- Run reporter on completion? (won't work for ticks) - runreport = true, - - -- Delete stats file after reporting? - deletestats = false, - - -- Process Lua code loaded from raw strings - -- (that is, when the 'source' field in the debug info - -- does not start with '@') - codefromstrings = false, - - -- Patterns for files to include when reporting - -- all will be included if nothing is listed - -- (exclude overrules include, do not include - -- the .lua extension, path separator is always '/') - ["include"] = { - "lib/std/prototype/_base$", - "lib/std/prototype/container$", - "lib/std/prototype/init$", - "lib/std/prototype/object$", - "lib/std/prototype/set$", - "lib/std/prototype/strbuf$", - "lib/std/prototype/trie$", - "lib/std/prototype/version$", - }, - - -- Patterns for files to exclude when reporting - -- all will be included if nothing is listed - -- (exclude overrules include, do not include - -- the .lua extension, path separator is always '/') - ["exclude"] = { - "luacov$", - "luacov/reporter$", - "luacov/defaults$", - "luacov/runner$", - "luacov/stats$", - "luacov/tick$", - }, -} diff --git a/AUTHORS.md b/AUTHORS.md deleted file mode 100644 index 3cff008..0000000 --- a/AUTHORS.md +++ /dev/null @@ -1,17 +0,0 @@ -# prototype's contributors - -This file lists major contributors to [prototype][]. If you think you -should be on it, please raise a [github issue][]. Thanks also to all -those who have contributed bug fixes, suggestions and support. - -Gary V. Vaughan now maintains _prototype_, having rewritten and -reorganised all the original code from [lua-stdlib][], in addition to -to adding a lot of new functionality. - -Reuben Thomas started the standard libraries project, which included the -original implementation of many of the functions now distributed with -this package. - -[github issue]: https://github.com/lua-stdlib/prototype/issues -[lua-stdlib]: https://github.com/lua-stdlib/lua-stdlib -[prototype]: https://github.com/lua-stdlib/prototype diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index fae68ec..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (C) 2002-2022 prototype 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/Makefile b/Makefile deleted file mode 100644 index c52772f..0000000 --- a/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -# Prototype Oriented Programming with Lua -# Copyright (C) 2002-2022 std.prototype authors - -LDOC = ldoc -LUA = lua -MKDIR = mkdir -p -SED = sed -SPECL = specl - -VERSION = git - -luadir = lib/std/prototype -SOURCES = \ - $(luadir)/_base.lua \ - $(luadir)/container.lua \ - $(luadir)/init.lua \ - $(luadir)/object.lua \ - $(luadir)/set.lua \ - $(luadir)/strbuf.lua \ - $(luadir)/trie.lua \ - $(luadir)/version.lua \ - $(NOTHING_ELSE) - - -all: doc $(luadir)/version.lua - - -$(luadir)/version.lua: .FORCE - @echo 'return "Prototype Object Libraries / $(VERSION)"' > '$@T'; \ - if cmp -s '$@' '$@T'; then \ - rm -f '$@T'; \ - else \ - echo 'echo return "Prototype Object Libraries / $(VERSION)" > $@'; \ - mv '$@T' '$@'; \ - fi - -doc: build-aux/config.ld $(SOURCES) - $(LDOC) -c build-aux/config.ld . - -build-aux/config.ld: build-aux/config.ld.in - $(SED) -e "s,@PACKAGE_VERSION@,$(VERSION)," '$<' > '$@' - - -CHECK_ENV = LUA=$(LUA) - -check: $(SOURCES) - LUA=$(LUA) $(SPECL) $(SPECL_OPTS) spec/*_spec.yaml - - -.FORCE: diff --git a/NEWS.md b/NEWS.md deleted file mode 100644 index 9226529..0000000 --- a/NEWS.md +++ /dev/null @@ -1,85 +0,0 @@ -# std.prototype NEWS - User visible changes - -## Noteworthy changes in release ?.? (????-??-??) [?] - -### New Features - - - Initial support for Lua 5.4. - - -## Noteworthy changes in release 1.0.1 (2016-02-07) [stable] - -### Bug fixes - - - The former lua-stdlib `strict` module, has moved to `std.strict` - to avoid confusion with the original PUC-Rio strict.lua. The base - module now looks for it there. - - -## Noteworthy changes in release 1.0 (2016-02-07) [stable] - -### New features (since lua-stdlib-41.2) - - - Initial release, now separated out from lua-stdlib. - - - Objects and Modules are no longer conflated - what you get back from - a `require "std.prototype.something"` is now ALWAYS a module: - - ```lua - local object = require "std.prototype.object" - assert (object.type (object) == "Module") - ``` - - And the modules that provide objects have a new `prototype` field - that contains the prototye for that kind of object: - - ```lua - local Object = object.prototype - assert (object.type (Object) == "Object") - ``` - - For backwards compatibility, if you call the module with a - constructor table, the previous recommended way to disambiguate - between a module and the object it prototyped, that table is passed - through to the module's object prototype. - - - Now that we have proper separation of concerns between module tables - and object prototype tables, the central `std.prototype.object.mapfields` - instantiation function is much cleaner and faster. - - - We used to have an object module method, `std.object.type`, which - often got imported using: - - ```lua - local prototype = require "std.object".type - ``` - - So we renamed it to `std.object.prototype` to avoid a name clash with - the `type` symbol, and subsequently deprecated the earlier equivalent - `type` method; but that was a mistake, because core Lua provides `type`, - and `io.type` (and in recent releases, `math.type`). So now, for - orthogonality with core Lua, we're going back to using - `std.prototype.object.type`, because that just makes more sense. Sorry! - -### Bug fixes - - - You can now derive other types from `std.prototype.set` by passing a - `_type` field in the init argument, just like the other table argument - objects. - - - In-order iteration with `__pairs` metamethod has been reinstated. - There were no spec examples, and the implementation mysteriously - went missing in a previous round of refactoring. - -### Incompatible changes - - - Deprecated methods and functions have all been removed. - - - `std.tree` is now `std.prototype.trie` and defines a Trie object, not a - Tree object. The implementation has been a _Radix Tree_ (aka _Trie_) - all along. - - - Objects no longer honor mangling and stripping `_functions` tables - from objects during instantiation, instead move your actual object - into the module `prototype` field, and add the module functions to - the parent table returned when the module is required. diff --git a/README.md b/README.md deleted file mode 100644 index 014b660..0000000 --- a/README.md +++ /dev/null @@ -1,93 +0,0 @@ -Prototype Oriented Programming with Lua -======================================= - -Copyright (C) 2000-2022 [std.prototype authors][authors] - -[![License](http://img.shields.io/:license-mit-blue.svg)](http://mit-license.org) -[![workflow status](https://github.com/lua-stdlib/prototype/actions/workflows/spec.yml/badge.svg?branch=master)](https://github.com/lua-stdlib/prototype/actions) -[![codecov.io](https://codecov.io/github/lua-stdlib/prototype/coverage.svg?branch=master)](https://codecov.io/github/lua-stdlib/prototype?branch=master) - - -This is a collection of Prototype Oriented Programming libraries for -Lua 5.1 (including LuaJIT), 5.2, 5.3 and 5.4. The libraries are copyright -by their authors (see the [AUTHORS][] file for details), and released -under the [MIT license][mit] (the same license as Lua itself). There is -no warranty. - -_prototype_ has no run-time prerequisites beyond a standard Lua system, -though it will take advantage of [stdlib][], [strict][] and [typecheck][] -if they are installed. - -[authors]: http://github.com/lua-stdlib/prototype/blob/master/AUTHORS.md -[github]: http://github.com/lua-stdlib/prototype/ "Github repository" -[lua]: http://www.lua.org "The Lua Project" -[mit]: http://mit-license.org "MIT License" -[stdlib]: https://github.com/lua-stdlib/lua-stdlib "Standard Lua Libraries" -[strict]: https://github.com/lua-stdlib/strict "strict variables" -[typecheck]: https://github.com/gvvaughan/typecheck "function type checks" - - -Installation ------------- - -The simplest and best way to install prototype is with [LuaRocks][]. To -install the latest release (recommended): - -```bash - luarocks install prototype -``` - -To install current git master (for testing, before submitting a bug -report for example): - -```bash - luarocks install http://raw.githubusercontent.com/lua-stdlib/prototype/master/prototype-git-1.rockspec -``` - -The best way to install without [LuaRocks][] is to copy the `prototype` -folder and its contents into a directory on your package search path. - -[luarocks]: http://www.luarocks.org "Lua package manager" - - -Documentation -------------- - -The latest release of these libraries is [documented in LDoc][github.io]. -Pre-built HTML files are included in the release. - -[github.io]: http://lua-stdlib.github.io/prototype - - -Bug reports and code contributions ----------------------------------- - -These libraries are written and maintained by their users. - -Please make bug reports and suggestions as [GitHub Issues][issues]. -Pull requests are especially appreciated. - -But first, please check that your issue has not already been reported by -someone else, and that it is not already fixed by [master][github] in -preparation for the next release (see Installation section above for how -to temporarily install master with [LuaRocks][]). - -There is no strict coding style, but please bear in mind the following -points when proposing changes: - -0. Follow existing code. There are a lot of useful patterns and avoided - traps there. - -1. 3-character indentation using SPACES in Lua sources: It makes rogue - TABs easier to see, and lines up nicely with 'if' and 'end' keywords. - -2. Simple strings are easiest to type using single-quote delimiters, - saving double-quotes for where a string contains apostrophes. - -3. Save horizontal space by only using SPACEs where the parser requires - them. - -4. Use vertical space to separate out compound statements to help the - coverage reports discover untested lines. - -[issues]: http://github.com/lua-stdlib/prototype/issues diff --git a/build-aux/config.ld.in b/build-aux/config.ld.in deleted file mode 100644 index 34e9df3..0000000 --- a/build-aux/config.ld.in +++ /dev/null @@ -1,74 +0,0 @@ ---[[ - Prototype Oriented Programming with Lua - Copyright (C) 2002-2022 std.prototype authors -]] - -title = 'std.prototype @PACKAGE_VERSION@ Reference' -project = 'std.prototype @PACKAGE_VERSION@' -description = [[ -# Prototype Oriented Programming with Lua - -A straight forward prototype-based object system, and a selection of -useful objects built on it. - -This is a collection of light-weight libraries for Lua 5.1 (including -LuaJIT), 5.2, 5.3 and 5.4 written in pure Lua. - -Each of the modules in this package returns a table with an empty -prototype object in the `prototype` field, and often a selection of -_module functions_ not related to a specific instance. That is, when -you require one of these modules, you get a conventional table of -functions plus an empty object of some sort: - - - local object = require 'std.prototype.object' - for k, v in pairs (object) do print (k, type (v)) end - --> prototype table - --> type function - -In this case, a module function called `type` which looks up the -`_type` field in any prototype's metatable, and an empty `Object`: - - print (object.prototype) - --> Object {} - -You can instantiate additional copies of a prototype by calling it with -a table of specialised attributes: - - print (object.prototype { myattribute = 'my value' }) - --> Object {myattribute=my value} - -As a convenience, calling the module itself passes the argument table -through to that module's prototype, although its faster to save an -empty instance of the prototype in a `local` and use that: - - print (object { 1, 2, foo = 'bar' }) - --> Object {1, 2; foo=bar} - - local Object = object.prototype - print (Object { 'Woo!' }) - --> Object {Woo!} - -## LICENSE - -The code is copyright by its respective authors, and released under the -MIT license (the same license as Lua itself). There is no warranty. -]] - -dir = '../doc/' - -file = { - '../lib/std/prototype/init.lua', - '../lib/std/prototype/container.lua', - '../lib/std/prototype/object.lua', - '../lib/std/prototype/set.lua', - '../lib/std/prototype/strbuf.lua', - '../lib/std/prototype/trie.lua', -} - -new_type ('object', 'Objects', false, 'Fields') -new_type ('init', 'Initialisation', false, 'Parameters') - -format = 'markdown' -backtick_references = false -sort = false diff --git a/index.html b/index.html new file mode 100644 index 0000000..bf7ea27 --- /dev/null +++ b/index.html @@ -0,0 +1,138 @@ + + + + + std.prototype 1.0.1 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + +

+

Prototype Oriented Programming with Lua

+ +

A straight forward prototype-based object system, and a selection of +useful objects built on it.

+ +

This is a collection of light-weight libraries for Lua 5.1 (including +LuaJIT), 5.2 and 5.3 written in pure Lua.

+ +

Each of the modules in this package returns a table with an empty +prototype object in the prototype field, and often a selection of +module functions not related to a specific instance. That is, when +you require one of these modules, you get a conventional table of +functions plus an empty object of some sort:

+ + +
local object = require "std.prototype.object"
+for k, v in pairs (object) do print (k, type (v)) end
+--> prototype  table
+--> type       function
+
+ +

In this case, a module function called type which looks up the +_type field in any prototype's metatable, and an empty Object:

+ +
print (object.prototype)
+--> Object {}
+
+ +

You can instantiate additional copies of a prototype by calling it with +a table of specialised attributes:

+ +
print (object.prototype { myattribute = "my value" })
+--> Object {myattribute=my value}
+
+ +

As a convenience, calling the module itself passes the argument table +through to that module's prototype, although its faster to save an +empty instance of the prototype in a local and use that:

+ +
print (object { 1, 2, foo = "bar" })
+--> Object {1, 2; foo=bar}
+
+local Object = object.prototype
+print (Object { "Woo!" })
+--> Object {Woo!}
+
+ +

LICENSE

+ +

The code is copyright by its respective authors, and released under the +MIT license (the same license as Lua itself). There is no warranty.

+ + + +

Modules

+ + + + + + + + + + + + + + + + + + + + + + + + + +
std.prototypeModule table.
std.prototype.containerContainer Prototype.
std.prototype.objectObject Prototype.
std.prototype.setSet Prototype.
std.prototype.strbufStrBuf Prototype.
std.prototype.trieTrie Prototype.
+ +
+
+
+generated by LDoc 1.4.3 +Last updated 2016-02-08 00:31:49 +
+
+ + diff --git a/ldoc.css b/ldoc.css new file mode 100644 index 0000000..ce77ac8 --- /dev/null +++ b/ldoc.css @@ -0,0 +1,304 @@ +/* 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 { + background-color: rgb(245, 245, 245); + border: 1px solid #C0C0C0; /* silver */ + padding: 10px; + margin: 10px 0 10px 0; + overflow: auto; + font-family: "Andale Mono", monospace; +} + +pre.example { + font-size: .85em; +} + +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 .user-keyword { color: #800080; } +pre .prompt { color: #558817; } +pre .url { color: #272fc2; text-decoration: underline; } + diff --git a/lib/std/prototype/_base.lua b/lib/std/prototype/_base.lua deleted file mode 100644 index 72009a4..0000000 --- a/lib/std/prototype/_base.lua +++ /dev/null @@ -1,65 +0,0 @@ ---[[ - Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 - Copyright (C) 2000-2022 std.prototype authors -]] - -local _ENV = require 'std.normalize' { - nonempty = next, - sub = string.sub, -} - -local argscheck -do - local ok, typecheck = pcall(require, 'typecheck') - if ok then - argscheck = typecheck.argscheck - else - argscheck = function(decl, fn) - return fn - end - end -end - - -return { - Module = function(t) - return setmetatable(t, { - _type = 'Module', - __call = function(self, ...) - return self.prototype(...) - end, - }) - end, - - argscheck = argscheck, - - mapfields = function(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 nonempty(map) then - map = map or {} - for k, v in next, src do - local key, dst = map[k] or k, obj - local kind = type(key) - if kind == 'string' and sub(key, 1, 1) == '_' then - mt[key] = v - elseif nonempty(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 - end - end - - -- Only set non-empty metatable. - if nonempty(mt) then - setmetatable(obj, mt) - end - return obj - end, -} diff --git a/lib/std/prototype/container.lua b/lib/std/prototype/container.lua deleted file mode 100644 index fd8449b..0000000 --- a/lib/std/prototype/container.lua +++ /dev/null @@ -1,296 +0,0 @@ ---[[ - Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 - Copyright (C) 2000-2022 std.prototype authors -]] ---[[-- - Container Prototype. - - This module supplies 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. - - The functionality of a container based object is entirely defined by its - *meta*methods. However, since we can store *any* object in a container, - we cannot rely on the `__index` metamethod, because it is only a - fallback for when that key is not already in the container itself. Of - course that does not entirely preclude the use of `__index` with - containers, so long as this limitation is observed. - - When making your own prototypes, derive from @{prototype.container.prototype} - if you want to access the contents of your containers with the `[]` - operator, otherwise from @{prototype.object.prototype} if you want to access - the functionality of your objects with named object methods. - - Prototype Chain - --------------- - - table - `-> Container - - @module std.prototype.container -]] - - -local _ENV = require 'std.normalize' { - Module = require 'std.prototype._base'.Module, - argscheck = require 'std.prototype._base'.argscheck, - concat = table.concat, - mapfields = require 'std.prototype._base'.mapfields, - nonempty = next, - sort = table.sort, -} - - - ---[[ ================= ]]-- ---[[ Helper Functions. ]]-- ---[[ ================= ]]-- - - -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 shallow_copy(t) - local r = {} - for k, v in next, t do - r[k] = v - end - return r -end - - ---- Instantiate a new object based on *proto*. --- --- This is equivalent to: --- --- merge(copy(proto), t or {}) --- --- Except that, by not checking arguments or metatables, it is 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 = {} - for k, v in next, proto do - obj[k] = v - end - for k, v in next, t or {} do - obj[k] = v - end - return obj -end - - - ---[[ ================= ]]-- ---[[ Container Object. ]]-- ---[[ ================= ]]-- - - ---- Container prototype. --- @object prototype --- @string[opt='Container'] _type object name --- @tfield[opt] table|function _init object initialisation --- @usage --- local Container = require 'prototype.container'.prototype --- local Graph = Container { _type = 'Graph' } --- local function nodes(graph) --- local n = 0 --- for _ in pairs(graph) do --- n = n + 1 --- end --- return n --- end --- local g = Graph { 'node1', 'node2' } --- assert(nodes(g) == 2) -local prototype = { - _type = 'Container', - - --- Metamethods - -- @section metamethods - - --- Return a clone of this container and its metatable. - -- - -- Like any Lua table, a container is essentially a collection of - -- `field_n = value_n` pairs, except that field names beginning with - -- an underscore `_` are usually kept in that container's metatable - -- where they define the behaviour of a container object rather than - -- being part of its actual contents. In general, cloned objects - -- also clone the behaviour of the object they cloned, unless... - -- - -- When calling @{prototype.container.prototype}, you pass a single table - -- argument with additional fields(and values) to be merged into the - -- clone. Any field names beginning with an underscore `_` are copied - -- to the clone's metatable, and all other fields to the cloned - -- container itself. For instance, you can change the name of the - -- cloned object by setting the `_type` field in the argument table. - -- - -- The `_init` private field is also special: When set to a sequence of - -- field names, unnamed fields in the call argument table are assigned - -- to those field names in subsequent clones, like the example below. - -- - -- Alternatively, you can set the `_init` private field of a cloned - -- container object to a function instead of a sequence, in which case - -- all the arguments passed when *it* is called/cloned(including named - -- and unnamed fields in the initial table argument, if there is one) - -- are passed through to the `_init` function, following the nascent - -- cloned object. See the @{mapfields} usage example below. - -- @function prototype:__call - -- @param ... arguments to prototype's *\_init*, often a single table - -- @treturn prototype clone of this container, with shared or - -- merged metatable as appropriate - -- @usage - -- local Cons = Container {_type='Cons', _init={'car', 'cdr'}} - -- local list = Cons {'head', Cons {'tail', nil}} - __call = function(self, ...) - 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. - for k, v in next, self do - obj[k] = v - end - - if type(mt._init) == 'function' then - obj = mt._init(obj, ...) - else - obj = (self.mapfields or mapfields)(obj, (...), mt._init) - end - - -- If a metatable was set, then merge our fields and use it. - if nonempty(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, - - --- Return an in-order iterator over public object fields. - -- @function prototype:__pairs - -- @treturn function iterator function - -- @treturn Object *self* - -- @usage - -- for k, v in pairs(anobject) do - -- process(k, v) - -- end - __pairs = function(self) - local keys, i = {}, 0 - for k in next, self do - keys[#keys + 1] = k - end - sort(keys, keysort) - return function(t) - i = i + 1 - local k = keys[i] - if k ~= nil then - return k, t[k] - end - end, self - end, - - --- Return a compact string representation of this object. - -- - -- First the container name, and then between { and } an ordered list - -- of the array elements of the contained values with numeric keys, - -- followed by asciibetically sorted remaining public key-value pairs. - -- - -- This metamethod doesn't recurse explicitly, but relies upon - -- suitable `__tostring` metamethods for non-primitive content objects. - -- @function prototype:__tostring - -- @treturn string stringified object representation - -- @see tostring - -- @usage - -- assert(tostring(list) == 'Cons {car='head', cdr=Cons {car='tail'}}') - __tostring = function(self) - return concat { - -- Pass a shallow copy to render to avoid triggering __tostring - -- again and blowing the stack. - getmetatable(self)._type, ' ', str(shallow_copy(self)), - } - end, -} - - -if argcheck then - local __call = prototype.__call - - prototype.__call = function(self, ...) - 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, n = mt._type, select('#', ...) - -- Don't count `self` as an argument for error messages, because - -- it just refers back to the object being called: `prototype {'x'}. - argcheck(name, 1, 'table',(...)) - if n > 1 then - argerror(name, 2, extramsg_toomany('argument', 1, n), 2) - end - end - - return __call(self, ...) - end -end - - -local function X(decl, fn) - return argscheck('prototype.container.' .. decl, fn) -end - - -return Module { - prototype = setmetatable({}, prototype), - - --- Module Functions - -- @section modulefunctions - - --- Return *new* with references to the fields of *src* merged in. - -- - -- This is the function used to instantiate the contents of a newly - -- cloned container, as called by @{__call} above, to split the - -- fields of a @{__call} argument table into private '_' prefixed - -- field namess, -- which are merged into the *new* metatable, and - -- public(everything else) names, which are merged into *new* itself. - -- - -- You might want to use this function from `_init` functions of your - -- own derived containers. - -- @function mapfields - -- @tparam table new partially instantiated clone container - -- @tparam table src @{__call} argument table that triggered cloning - -- @tparam[opt={}] table map key renaming specification in the form - -- `{old_key=new_key, ...}` - -- @treturn table merged public fields from *new* and *src*, with a - -- metatable of private fields(if any), both renamed according to - -- *map* - -- @usage - -- local Bag = Container { - -- _type = 'Bag', - -- _init = function(new, ...) - -- if type(...) == 'table' then - -- return container.mapfields(new,(...)) - -- end - -- return functional.reduce(operator.set, new, ipairs, {...}) - -- end, - -- } - -- local groceries = Bag('apple', 'banana', 'banana') - -- local purse = Bag {_type = 'Purse'}('cards', 'cash', 'id') - mapfields = X('mapfields(table, table|object, ?table)', mapfields), -} diff --git a/lib/std/prototype/init.lua b/lib/std/prototype/init.lua deleted file mode 100644 index 87280ad..0000000 --- a/lib/std/prototype/init.lua +++ /dev/null @@ -1,48 +0,0 @@ ---[[ - Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 - Copyright (C) 2000-2022 std.prototype authors -]] ---[[-- - Module table. - - Lazy loading of submodules, and metadata for the Prototype package. - - @module std.prototype -]] - - -local _ENV = require 'std.normalize' {} - - - ---[[ =============== ]]-- ---[[ Implementation. ]]-- ---[[ =============== ]]-- - - -return setmetatable({ - --- Module table. - -- @table prototype - -- @field version Release version string -}, { - --- Metamethods - -- @section Metamethods - - --- Lazy loading of prototype 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 prototype = require 'prototype' - -- local Object = prototype.object.prototype - __index = function(self, name) - local ok, t = pcall(require, 'std.prototype.' .. name) - if ok then - rawset(self, name, t) - return t - end - end, -}) diff --git a/lib/std/prototype/object.lua b/lib/std/prototype/object.lua deleted file mode 100644 index 523a105..0000000 --- a/lib/std/prototype/object.lua +++ /dev/null @@ -1,137 +0,0 @@ ---[[ - Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 - Copyright (C) 2000-2022 std.prototype authors -]] ---[[-- - Object Prototype. - - This module provides a specialization of the @{prototype.container.prototype} - with the addition of object methods. In addition to the functionality - described here, object prototypes also have all the methods and - metamethods of the @{prototype.container.prototype}. - - Note that object methods are stored in the `__index` field of their - metatable, and so cannot also use the `__index` metamethod to lookup - references with square brackets. Use a @{prototype.container.prototype} based - object if you want to do that. - - Prototype Chain - --------------- - - table - `-> Container - `-> Object - - @module std.prototype.object -]] - - -local _ENV = require 'std.normalize' { - Container = require 'std.prototype.container'.prototype, - Module = require 'std.prototype._base'.Module, - argscheck = require 'std.prototype._base'.argscheck, - mapfields = require 'std.prototype._base'.mapfields, -} - - - ---[[ ======= ]]-- ---[[ Object. ]]-- ---[[ ======= ]]-- - - -local function X(decl, fn) - return argscheck('std.prototype.object.' .. decl, fn) -end - - ---- Object prototype. --- @object prototype --- @string[opt='Object'] _type object name --- @tfield[opt] table|function _init object initialisation --- @usage --- local Object = require 'std.prototype.object'.prototype --- 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 --- } - -local Object = Container { - _type = 'Object', - - --- Methods - -- @section methods - - __index = { - --- Return a clone of this object and its metatable. - -- - -- This function is useful if you need to override the normal use of - -- the `__call` metamethod for object cloning, without losing the - -- ability to clone an object. - -- @function prototype:clone - -- @param ... arguments to prototype's *\_init*, often a single table - -- @treturn prototype a clone of this object, with shared or merged - -- metatable as appropriate - -- @see prototype.container.__call - -- @usage - -- local Node = Object {_type='Node'} - -- -- A trivial FSA to recognize powers of 10, either '0' or a '1' - -- -- followed by zero or more '0's can transition to state 'finish' - -- local states; states = { - -- start = Node {['1']=states[1], ['0']=states.finish}, - -- [1] = Node {['0']=states[1], ['']=states.finish}, - -- finish = Node {}, - -- } - clone = getmetamethod(Container, '__call'), - - --- Return *new* with references to the fields of *src* merged in. - -- - -- You can change the value of this function in an object, and that - -- new function will be called during cloning instead of the - -- standard @{prototype.container.mapfields} implementation. - -- @function prototype.mapfields - -- @tparam table new partially instantiated clone container - -- @tparam table src @{clone} argument table that triggered cloning - -- @tparam[opt={}] table map key renaming specification in the form - -- `{old_key=new_key, ...}` - -- @treturn table merged public fields from *new* and *src*, with a - -- metatable of private fields(if any), both renamed according to - -- *map* - -- @see prototype.container.mapfields - mapfields = X('mapfields(table, table|object, ?table)', mapfields), - }, -} - - -return Module { - prototype = Object, - - --- Module Functions - -- @section modulefunctions - - --- Type of an object. - -- - -- It's conventional to organise similar objects according to a string - -- valued `_type` field, which can then be queried using this - -- function. - -- @function type - -- @param x an object - -- @treturn string type of *x*, or `nil` if *x* has no `_type` - -- metatable entry. - -- @usage - -- local Object = require 'std.object'.prototype - -- assert(object.type(Object) == 'Object') - -- local Stack = Object { - -- _type = 'Stack', - -- ... - -- } - -- local stack = Stack {'some stuff'} - -- assert(object.type(stack) == getmetatable(stack)._type) - type = function(x) - return(getmetatable(x) or {})._type - end, -} diff --git a/lib/std/prototype/set.lua b/lib/std/prototype/set.lua deleted file mode 100644 index a006ace..0000000 --- a/lib/std/prototype/set.lua +++ /dev/null @@ -1,377 +0,0 @@ ---[[ - Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 - Copyright (C) 2000-2022 std.prototype authors -]] ---[[-- - Set Prototype. - - This module returns a table of set operators, as well as the prototype - for a Set container object. - - Every possible object or primitive value is always present in any Set - container exactly zero or one times. - - In addition to the functionality described here, Set containers also - have all the methods and metamethods of the @{prototype.container.prototype} -(except where overridden here). - - Prototype Chain - --------------- - - table - `-> Container - `-> Set - - @module std.prototype.set -]] - - -local _ENV = require 'std.normalize' { - Container = require 'std.prototype.container'.prototype, - Module = require 'std.prototype._base'.Module, - argscheck = require 'std.prototype._base'.argscheck, - concat = table.concat, - nonempty = next, - sort = table.sort, - sub = string.sub, -} - - -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 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 next, 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 next, 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 next, set2 do - insert(r, e) - end - return r -end - - -function subset(set1, set2) - for e in next, 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 argscheck('std.prototype.set.' .. decl, fn) -end - - ---- Set prototype object. --- @object prototype --- @string[opt='Set'] _type object name --- @see prototype.container.prototype --- @usage --- local Set = require 'std.prototype.set'.prototype --- assert(prototype.type(Set) == 'Set') - - -Set = Container { - _type = 'Set', - - --- Set object initialisation. - -- - -- Returns partially initialised Set container with contents - -- from *t*. - -- @init prototype._init - -- @tparam table new uninitialised Set container object - -- @tparam table t initialisation table from `__call` - _init = function(new, t) - local mt = {} - for k, v in pairs(t) do - local type_k = type(k) - if type_k == 'number' then - insert(new, v) - elseif type_k == 'string' and sub(k, 1, 1) == '_' then - mt[k] = v - end - -- non-underscore-prefixed string keys are discarded! - end - return nonempty(mt) and setmetatable(new, mt) or new - end, - - --- Metamethods - -- @section metamethods - - --- Union operation. - -- @function prototype:__add - -- @tparam prototype s another set - -- @treturn prototype everything from *this* set plus everything from *s* - -- @see union - -- @usage - -- union = this + s - __add = union, - - --- Difference operation. - -- @function prototype:__sub - -- @tparam prototype s another set - -- @treturn prototype everything from *this* set that is not also in *s* - -- @see difference - -- @usage - -- difference = this - s - __sub = difference, - - --- Intersection operation. - -- @function prototype:__mul - -- @tparam prototype s another set - -- @treturn prototype anything in both *this* set and in *s* - -- @see intersection - -- @usage - -- intersection = this * s - __mul = intersection, - - --- Symmetric difference operation. - -- @function prototype:__div - -- @tparam prototype s another set - -- @treturn prototype everything in *this* set or in *s* but not in both - -- @see symmetric_difference - -- @usage - -- symmetric_difference = this / s - __div = symmetric_difference, - - --- Subset operation. - -- @static - -- @function prototype:__le - -- @tparam prototype s another set - -- @treturn boolean `true` if everything in *this* set is also in *s* - -- @see subset - -- @usage - -- issubset = this <= s - __le = subset, - - --- Proper subset operation. - -- @function prototype:__lt - -- @tparam prototype s another set - -- @treturn boolean `true` if *s* is not equal to *this* set, but does - -- contain everything from *this* set - -- @see proper_subset - -- @usage - -- ispropersubset = this < s - __lt = proper_subset, - - --- Return a string representation of this set. - -- @function prototype:__tostring - -- @treturn string string representation of a set. - -- @see tostring - __tostring = function(self) - local keys = {} - for k in pairs(self) do - keys[#keys + 1] = str(k) - end - sort(keys) - return getmetatable(self)._type .. ' {' .. concat(keys, ', ') .. '}' - end, -} - - -return Module { - prototype = Set, - - --- Module Functions - -- @section modulefunctions - - --- Delete an element from a set. - -- @function delete - -- @tparam prototype set a set - -- @param e element - -- @treturn prototype 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. - -- @function difference - -- @tparam prototype set1 a set - -- @tparam prototype set2 another set - -- @treturn prototype a copy of *set1* with elements of *set2* removed - -- @usage - -- all = set.difference(all, Set {32, 49, 56}) - difference = X('difference(Set, Set)', difference), - - --- Iterator for sets. - -- @function elems - -- @tparam prototype set a set - -- @return *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)', pairs), - - --- Find whether two sets are equal. - -- @function equal - -- @tparam prototype set1 a set - -- @tparam prototype set2 another set - -- @treturn boolean `true` if *set1* and *set2* each contain identical - -- elements, `false` otherwise - -- @usage - -- if set.equal(keys, Set {META, CTRL, 'x'}) then - -- process(keys) - -- end - equal = X( 'equal(Set, Set)', equal), - - --- Insert an element into a set. - -- @function insert - -- @tparam prototype set a set - -- @param e element - -- @treturn prototype 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. - -- @function intersection - -- @tparam prototype set1 a set - -- @tparam prototype set2 another set - -- @treturn prototype 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. - -- @function difference - -- @tparam prototype 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. - -- @function proper_subset - -- @tparam prototype set1 a set - -- @tparam prototype 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. - -- @function subset - -- @tparam prototype set1 a set - -- @tparam prototype 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. - -- @function symmetric_difference - -- @tparam prototype set1 a set - -- @tparam prototype set2 another set - -- @treturn prototype 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. - -- @function union - -- @tparam prototype set1 a set - -- @tparam prototype set2 another set - -- @treturn prototype a copy of *set1* with elements in *set2* merged in - -- @usage - -- all = set.union(a, b) - union = X('union(Set, Set)', union), -} diff --git a/lib/std/prototype/strbuf.lua b/lib/std/prototype/strbuf.lua deleted file mode 100644 index 05f16cf..0000000 --- a/lib/std/prototype/strbuf.lua +++ /dev/null @@ -1,126 +0,0 @@ ---[[ - Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 - Copyright (C) 2000-2022 std.prototype authors -]] ---[[-- - StrBuf Prototype. - - Buffers are mutable by default, but being based on objects, they can - also be used in a functional style: - - local StrBuf = require 'std.prototype.strbuf'.prototype - 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 - - In addition to the functionality described here, StrBuf objects also - have all the methods and metamethods of the @{prototype.object.prototype} - (except where overridden here), - - Prototype Chain - --------------- - - table - `-> Container - `-> Object - `-> StrBuf - - @module std.prototype.strbuf -]] - - -local _ENV = require 'std.normalize' { - Module = require 'std.prototype._base'.Module, - Object = require 'std.prototype.object'.prototype, - argscheck = require 'std.prototype._base'.argscheck, - concat = table.concat, -} - - - ---[[ ================= ]]-- ---[[ Helper Functions. ]]-- ---[[ ================= ]]-- - - -local function __concat(self, x) - self[#self + 1] = x - return self -end - - - ---[[ ============== ]]-- ---[[ StrBuf Object. ]]-- ---[[ ============== ]]-- - - ---- StrBuf prototype object. --- @object prototype --- @string[opt='StrBuf'] _type object name --- @see prototype.object.prototype --- @usage --- local StrBuf = require 'std.prototype.strbuf'.prototype --- local a = StrBuf {1, 2, 3} --- local b = StrBuf {a, 'five', 'six'} --- a = a .. 4 --- b = b:concat 'seven' --- print(a, b) --> 1234 1234fivesixseven --- os.exit(0) - - -local function X(decl, fn) - return argscheck('std.prototype.strbuf.' .. decl, fn) -end - - -return Module { - prototype = Object { - _type = 'StrBuf', - - __index = { - --- Methods - -- @section methods - - --- Add a object to a buffer. - -- Elements are stringified lazily, so if you add a table and then - -- change its contents, the contents of the buffer will be affected - -- too. - -- @function prototype:concat - -- @param x object to add to buffer - -- @treturn prototype modified buffer - -- @usage - -- c = StrBuf {} :concat 'append this' :concat(StrBuf {' and', ' this'}) - concat = X('concat(StrBuf, any)', __concat), - }, - - - --- Metamethods - -- @section metamethods - - --- Support concatenation to StrBuf objects. - -- @function prototype:__concat - -- @param x a string, or object that can be coerced to a string - -- @treturn prototype modified *buf* - -- @see concat - -- @usage - -- buf = buf .. x - __concat = __concat, - - --- Support fast conversion to Lua string. - -- @function prototype:__tostring - -- @treturn string concatenation of buffer contents - -- @see tostring - -- @usage - -- str = tostring(buf) - __tostring = function(self) - local strs = {} - for _, e in ipairs(self) do - strs[#strs + 1] = tostring(e) - end - return concat(strs) - end, - }, -} diff --git a/lib/std/prototype/trie.lua b/lib/std/prototype/trie.lua deleted file mode 100644 index 53989ef..0000000 --- a/lib/std/prototype/trie.lua +++ /dev/null @@ -1,324 +0,0 @@ ---[[ - Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 - Copyright (C) 2000-2022 std.prototype authors -]] ---[[-- - Trie Prototype. - - This module returns a table of trie operators, as well as the prototype - for a Trie container object. - - This is not a search tree, but rather a radix tree to efficiently store - and retrieve values stored with a path as a key, such as a multi-key - keytable. Although it does have iterators for walking the trie with - various algorithms. - - In addition to the functionality described here, Trie containers also - have all the methods and metamethods of the @{prototype.container.prototype} - (except where overridden here), - - Prototype Chain - --------------- - - table - `-> Container - `-> Trie - - @module std.prototype.trie -]] - - -local _ENV = require 'std.normalize' { - Container = require 'std.prototype.container'.prototype, - Module = require 'std.prototype._base'.Module, - argscheck = require 'std.prototype._base'.argscheck, - remove = table.remove, - wrap = coroutine.wrap, - yield = coroutine.yield, -} - - - ---[[ =============== ]]-- ---[[ Implementation. ]]-- ---[[ =============== ]]-- - - -local function _nodes(it, tr) - local p = {} - local function visit(n) - if type(n) == 'table' then - yield('branch', p, n) - for i, v in it(n) do - p[#p + 1] = i - visit(v) - remove(p) - end - yield('join', p, n) - else - yield('leaf', p, n) - end - end - return 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 deep_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] = deep_copy(d[v], v) - else - o[i] = d[v] - end - else - o[i] = v - end - end - return o - end - return deep_copy(r, 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 - yield(n) - end - end - return wrap(visit), tr -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 - - - ---[[ ============ ]]-- ---[[ Trie Object. ]]-- ---[[ ============ ]]-- - - -local function X(decl, fn) - return argscheck('std.prototype.trie.' .. decl, fn) -end - - ---- Return the object type, if set, otherwise the Lua type. --- @param x item to act on --- @treturn string object type of *x*, otherwise `type(x)` -local function _type(x) - return(getmetatable(x) or {})._type or type(x) -end - - ---- Trie prototype object. --- @object prototype --- @string[opt='Trie'] _type object name --- @see prototype.container.prototype --- @usage --- local trie = require 'std.prototype.trie' --- local Trie = trie.prototype --- local tr = Trie {} --- tr[{'branch1', 1}] = 'leaf1' --- tr[{'branch1', 2}] = 'leaf2' --- tr[{'branch2', 1}] = 'leaf3' --- print(tr[{'branch1'}]) --> Trie {leaf1, leaf2} --- print(tr[{'branch1', 2}]) --> leaf2 --- print(tr[{'branch1', 3}]) --> nil --- --> leaf1 leaf2 leaf3 --- for leaf in trie.leaves(tr) do --- io.write(leaf .. '\t') --- end - -local Trie - -Trie = Container { - _type = 'Trie', - - --- Metamethods - -- @section metamethods - - --- Deep retrieval. - -- @function prototype:__index - -- @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 _type(i) == 'table' then - local r = tr - for j = 1, len(i) do - if r == nil then - return nil - end - r = r[i[j]] - end - return r - else - return rawget(tr, i) - end - end, - - --- Deep insertion. - -- @function prototype:__newindex - -- @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 _type(i) == 'table' then - for n = 1, len(i) - 1 do - if _type(tr[i[n]]) ~= 'Trie' then - rawset(tr, i[n], Trie {}) - end - tr = tr[i[n]] - end - rawset(tr, i[len(i)], v) - else - rawset(tr, i, v) - end - end, -} - - -return Module { - prototype = Trie, - - --- Module Functions - -- @section modulefunctions - - --- Make a deep copy of a trie or table, including any metatables. - -- @function clone - -- @tparam table tr trie or trie-like table - -- @tparam boolean nometa if non-`nil` don't copy metatables - -- @treturn prototype|table a deep copy of *tr* - -- @see prototype.object.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), - - --- Trie iterator which returns just numbered leaves, in order. - -- @function ileaves - -- @tparam prototype|table tr trie or trie-like table - -- @treturn function iterator function - -- @treturn prototype|table the trie *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), - - --- Trie 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. - -- @function inodes - -- @tparam prototype|table tr trie or trie-like table to iterate over - -- @treturn function iterator function - -- @treturn trie|table the trie, *tr* - -- @see nodes - inodes = X('inodes(table)', function(t) - return _nodes(ipairs, t) - end), - - --- Trie iterator which returns just leaves. - -- @function leaves - -- @tparam table t trie or trie-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 trie into another. - -- @function merge - -- @tparam table t destination trie - -- @tparam table u table with nodes to merge - -- @treturn table *t* with nodes from *u* merged in - -- @usage - -- merge(dest, {{exists=1}, {{not = {present = {inside='dest'}}}}}) - merge = X('merge(table, table)', merge), - - --- Trie iterator over all nodes. - -- - -- The returned iterator function performs a depth-first traversal of - -- `tr`, and at each node it returns `{node-type, trie-path, trie-node}` - -- where `node-type` is `branch`, `join` or `leaf`; `trie-path` is a - -- list of keys used to reach this node, and `trie-node` is the current - -- node. - -- - -- Note that the `trie-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 `trie-path` list before the next iteration - -- changes it. - -- @function nodes - -- @tparam prototype|table tr trie or trie-like table to iterate over - -- @treturn function iterator function - -- @treturn prototype|table the trie, *tr* - -- @see inodes - -- @usage - -- -- trie = +-- node1 - -- -- | +-- leaf1 - -- -- | '-- leaf2 - -- -- '-- leaf 3 - -- trie = Trie {Trie {'leaf1', 'leaf2'}, 'leaf3'} - -- for node_type, path, node in nodes(trie) do - -- print(node_type, path, node) - -- end - -- --> 'branch' {} {{'leaf1', 'leaf2'}, 'leaf3'} - -- --> 'branch' {1} {'leaf1', 'leaf2') - -- --> '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), -} diff --git a/modules/std.prototype.container.html b/modules/std.prototype.container.html new file mode 100644 index 0000000..0db69ae --- /dev/null +++ b/modules/std.prototype.container.html @@ -0,0 +1,368 @@ + + + + + std.prototype 1.0.1 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.prototype.container

+

Container Prototype.

+

+ + +

This module supplies 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.

+ +

The functionality of a container based object is entirely defined by its + metamethods. However, since we can store any object in a container, + we cannot rely on the __index metamethod, because it is only a + fallback for when that key is not already in the container itself. Of + course that does not entirely preclude the use of __index with + containers, so long as this limitation is observed.

+ +

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

+ +

Prototype Chain

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

+ + +

Objects

+ + + + + +
prototypeContainer prototype.
+

Metamethods

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

Module Functions

+ + + + + +
mapfields (new, src[, map={}])Return new with references to the fields of src merged in.
+ +
+
+ + +

Objects

+ +
+
+ + prototype +
+
+ Container prototype. + + +

Fields:

+
    +
  • _init + table or function + object initialisation + (optional) +
  • +
  • _type + string + object name + (default "Container") +
  • +
+ + + + +

Usage:

+
    +
    + local Container = require "prototype.container".prototype
    + local Graph = Container { _type = "Graph" }
    + local function nodes (graph)
    +   local n = 0
    +   for _ in pairs (graph) do n = n + 1 end
    +   return n
    + end
    + local g = Graph { "node1", "node2" }
    + assert (nodes (g) == 2)
    +
+ +
+
+

Metamethods

+ +
+
+ + prototype:__call (...) +
+
+ Return a clone of this container and its metatable.

+ +

Like any Lua table, a container is essentially a collection of + field_n = value_n pairs, except that field names beginning with + an underscore _ are usually kept in that container's metatable + where they define the behaviour of a container object rather than + being part of its actual contents. In general, cloned objects + also clone the behaviour of the object they cloned, unless...

+ +

When calling prototype.container.prototype, you pass a single table + argument with additional fields (and values) to be merged into the + clone. Any field names beginning with an underscore _ are copied + to the clone's metatable, and all other fields to the cloned + container itself. For instance, you can change the name of the + cloned object by setting the _type field in the argument table.

+ +

The _init private field is also special: When set to a sequence of + field names, unnamed fields in the call argument table are assigned + to those field names in subsequent clones, like the example below.

+ +

Alternatively, you can set the _init private field of a cloned + container object to a function instead of a sequence, in which case + all the arguments passed when it is called/cloned (including named + and unnamed fields in the initial table argument, if there is one) + are passed through to the _init function, following the nascent + cloned object. See the mapfields usage example below. + + +

Parameters:

+
    +
  • ... + arguments to prototype's _init, often a single table +
  • +
+ +

Returns:

+
    + + prototype + clone of this container, with shared or + merged metatable as appropriate +
+ + + +

Usage:

+
    +
    + local Cons = Container {_type="Cons", _init={"car", "cdr"}}
    + local list = Cons {"head", Cons {"tail", nil}}
    +
+ +
+
+ + prototype:__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 pairs (anobject) do process (k, v) end
    +
+ +
+
+ + prototype:__tostring () +
+
+ Return a compact string representation of this object.

+ +

First the container name, and then between { and } an ordered list + of the array elements of the contained values with numeric keys, + followed by asciibetically sorted remaining public key-value pairs.

+ +

This metamethod doesn't recurse explicitly, but relies upon + suitable __tostring metamethods for non-primitive content objects. + + + +

Returns:

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

See also:

+ + +

Usage:

+
    +
    assert (tostring (list) == 'Cons {car="head", cdr=Cons {car="tail"}}')
    +
+ +
+
+

Module Functions

+ +
+
+ + mapfields (new, src[, map={}]) +
+
+ Return new with references to the fields of src merged in.

+ +

This is the function used to instantiate the contents of a newly + cloned container, as called by __call above, to split the + fields of a __call argument table into private "_" prefixed + field namess, -- which are merged into the new metatable, and + public (everything else) names, which are merged into new itself.

+ +

You might want to use this function from _init functions of your + own derived containers. + + +

Parameters:

+
    +
  • new + table + partially instantiated clone container +
  • +
  • src + table + __call argument table that triggered cloning +
  • +
  • map + table + key renaming specification in the form + {old_key=new_key, ...} + (default {}) +
  • +
+ +

Returns:

+
    + + table + merged public fields from new and src, with a + metatable of private fields (if any), both renamed according to + map +
+ + + +

Usage:

+
    +
    + local Bag = Container {
    +   _type = "Bag",
    +   _init = function (new, ...)
    +     if type (...) == "table" then
    +       return container.mapfields (new, (...))
    +     end
    +     return functional.reduce (operator.set, new, ipairs, {...})
    +   end,
    + }
    + local groceries = Bag ("apple", "banana", "banana")
    + local purse = Bag {_type = "Purse"} ("cards", "cash", "id")
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2016-02-08 00:31:49 +
+
+ + diff --git a/modules/std.prototype.html b/modules/std.prototype.html new file mode 100644 index 0000000..11be839 --- /dev/null +++ b/modules/std.prototype.html @@ -0,0 +1,152 @@ + + + + + std.prototype 1.0.1 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.prototype

+

Module table.

+

Lazy loading of submodules, and metadata for the Prototype package.

+ + +

Tables

+ + + + + +
prototypeModule table.
+

Metamethods

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

Tables

+ +
+
+ + prototype +
+
+ Module table. + + +

Fields:

+
    +
  • version + Release version string +
  • +
+ + + + + +
+
+

Metamethods

+ +
+
+ + __index (name) +
+
+ Lazy loading of prototype 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 prototype = require "prototype"
    + local Object = prototype.object.prototype
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2016-02-08 00:31:49 +
+
+ + diff --git a/modules/std.prototype.object.html b/modules/std.prototype.object.html new file mode 100644 index 0000000..cb5e796 --- /dev/null +++ b/modules/std.prototype.object.html @@ -0,0 +1,311 @@ + + + + + std.prototype 1.0.1 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.prototype.object

+

Object Prototype.

+

+ + +

This module provides a specialization of the prototype.container.prototype + with the addition of object methods. In addition to the functionality + described here, object prototypes also have all the methods and + metamethods of the prototype.container.prototype.

+ +

Note that object methods are stored in the __index field of their + metatable, and so cannot also use the __index metamethod to lookup + references with square brackets. Use a prototype.container.prototype based + object if you want to do that.

+ +

Prototype Chain

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

+ + +

Objects

+ + + + + +
prototypeObject prototype.
+

Module Functions

+ + + + + +
type (x)Type of an object.
+

Methods

+ + + + + + + + + +
prototype:clone (...)Return a clone of this object and its metatable.
prototype.mapfields (new, src[, map={}])Return new with references to the fields of src merged in.
+ +
+
+ + +

Objects

+ +
+
+ + prototype +
+
+ Object prototype. + + +

Fields:

+
    +
  • _init + table or function + object initialisation + (optional) +
  • +
  • _type + string + object name + (default "Object") +
  • +
+ + + + +

Usage:

+
    +
    + local Object = require "std.prototype.object".prototype
    + 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
    + }
    +
+ +
+
+

Module Functions

+ +
+
+ + type (x) +
+
+ Type of an object.

+ +

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

Parameters:

+
    +
  • x + an object +
  • +
+ +

Returns:

+
    + + string + type of x, or nil if x has no _type + metatable entry. +
+ + + +

Usage:

+
    +
    +   local Object = require "std.object".prototype
    +   assert (object.type (Object) == "Object")
    +   local Stack = Object {
    +     _type = "Stack",
    +     ...
    +   }
    +   local stack = Stack {"some stuff"}
    +   assert (object.type (stack) == getmetatable (stack)._type)
    +
+ +
+
+

Methods

+ +
+
+ + prototype:clone (...) +
+
+ Return a clone of this object and its metatable.

+ +

This function is useful if you need to override the normal use of + the __call metamethod for object cloning, without losing the + ability to clone an object. + + +

Parameters:

+
    +
  • ... + arguments to prototype's _init, often a single table +
  • +
+ +

Returns:

+
    + + prototype + a clone of this object, with shared or merged + metatable as appropriate +
+ + +

See also:

+ + +

Usage:

+
    +
    + local Node = Object { _type = "Node" }
    + -- A trivial FSA to recognize powers of 10, either "0" or a "1"
    + -- followed by zero or more "0"s can transition to state 'finish'
    + local states; states = {
    +   start  = Node { ["1"] = states[1], ["0"] = states.finish },
    +   [1]    = Node { ["0"] = states[1], [""] = states.finish },
    +   finish = Node {},
    + }
    +
+ +
+
+ + prototype.mapfields (new, src[, map={}]) +
+
+ Return new with references to the fields of src merged in.

+ +

You can change the value of this function in an object, and that + new function will be called during cloning instead of the + standard prototype.container.mapfields implementation. + + +

Parameters:

+
    +
  • new + table + partially instantiated clone container +
  • +
  • src + table + clone argument table that triggered cloning +
  • +
  • map + table + key renaming specification in the form + {old_key=new_key, ...} + (default {}) +
  • +
+ +

Returns:

+
    + + table + merged public fields from new and src, with a + metatable of private fields (if any), both renamed according to + map +
+ + +

See also:

+ + + +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2016-02-08 00:31:49 +
+
+ + diff --git a/modules/std.prototype.set.html b/modules/std.prototype.set.html new file mode 100644 index 0000000..dbbee23 --- /dev/null +++ b/modules/std.prototype.set.html @@ -0,0 +1,891 @@ + + + + + std.prototype 1.0.1 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.prototype.set

+

Set Prototype.

+

+ + +

This module returns a table of set operators, as well as the prototype + for a Set container object.

+ +

Every possible object or primitive value is always present in any Set + container exactly zero or one times.

+ +

In addition to the functionality described here, Set containers also + have all the methods and metamethods of the prototype.container.prototype + (except where overridden here).

+ +

Prototype Chain

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

+ + +

Objects

+ + + + + +
prototypeSet prototype object.
+

Initialisation

+ + + + + +
prototype._initSet object initialisation.
+

Metamethods

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
prototype:__add (s)Union operation.
prototype:__sub (s)Difference operation.
prototype:__mul (s)Intersection operation.
prototype:__div (s)Symmetric difference operation.
prototype:__le (s)Subset operation.
prototype:__lt (s)Proper subset operation.
prototype:__tostring ()Return a string representation of this set.
+

Module Functions

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

Objects

+ +
+
+ + prototype +
+
+ Set prototype object. + + +

Fields:

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

See also:

+ + +

Usage:

+
    +
    + local Set = require "std.prototype.set".prototype
    + assert (prototype.type (Set) == "Set")
    +
+ +
+
+

Initialisation

+ +
+
+ + prototype._init +
+
+ Set object initialisation.

+ +

Returns partially initialised Set container with contents + from t. + + +

Parameters:

+
    +
  • new + table + uninitialised Set container object +
  • +
  • t + table + initialisation table from __call +
  • +
+ + + + + +
+
+

Metamethods

+ +
+
+ + prototype:__add (s) +
+
+ Union operation. + + +

Parameters:

+ + +

Returns:

+
    + + prototype + everything from this set plus everything from s +
+ + +

See also:

+ + +

Usage:

+
    +
    union = this + s
    +
+ +
+
+ + prototype:__sub (s) +
+
+ Difference operation. + + +

Parameters:

+ + +

Returns:

+
    + + prototype + everything from this set that is not also in s +
+ + +

See also:

+ + +

Usage:

+
    +
    difference = this - s
    +
+ +
+
+ + prototype:__mul (s) +
+
+ Intersection operation. + + +

Parameters:

+ + +

Returns:

+
    + + prototype + anything in both this set and in s +
+ + +

See also:

+ + +

Usage:

+
    +
    intersection = this * s
    +
+ +
+
+ + prototype:__div (s) +
+
+ Symmetric difference operation. + + +

Parameters:

+ + +

Returns:

+
    + + prototype + everything in this set or in s but not in both +
+ + +

See also:

+ + +

Usage:

+
    +
    symmetric_difference = this / s
    +
+ +
+
+ + prototype:__le (s) +
+
+ Subset operation. + + +

Parameters:

+ + +

Returns:

+
    + + boolean + true if everything in this set is also in s +
+ + +

See also:

+ + +

Usage:

+
    +
    issubset = this <= s
    +
+ +
+
+ + prototype:__lt (s) +
+
+ Proper subset operation. + + +

Parameters:

+ + +

Returns:

+
    + + boolean + true if s is not equal to this set, but does + contain everything from this set +
+ + +

See also:

+ + +

Usage:

+
    +
    ispropersubset = this < s
    +
+ +
+
+ + prototype:__tostring () +
+
+ Return a string representation of this set. + + + +

Returns:

+
    + + string + string representation of a set. +
+ + +

See also:

+ + + +
+
+

Module Functions

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

Parameters:

+
    +
  • set + prototype + a set +
  • +
  • e + element +
  • +
+ +

Returns:

+
    + + prototype + the modified set +
+ + + +

Usage:

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

Parameters:

+ + +

Returns:

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

Usage:

+
    +
    all = set.difference (all, Set {32, 49, 56})
    +
+ +
+
+ + elems (set) +
+
+ Iterator for sets. + + +

Parameters:

+ + +

Returns:

+
    + + set iterator +
+ + + +

Usage:

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

Parameters:

+ + +

Returns:

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

Usage:

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

Parameters:

+
    +
  • set + prototype + a set +
  • +
  • e + element +
  • +
+ +

Returns:

+
    + + prototype + the modified set +
+ + + +

Usage:

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

Parameters:

+ + +

Returns:

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

Usage:

+
    +
    common = set.intersection (a, b)
    +
+ +
+
+ + difference (set, e) +
+
+ Say whether an element is in a set. + + +

Parameters:

+
    +
  • set + prototype + 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
    +
+ +
+
+ + proper_subset (set1, set2) +
+
+ Find whether one set is a proper subset of another. + + +

Parameters:

+ + +

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))
    +
+ +
+
+ + subset (set1, set2) +
+
+ Find whether one set is a subset of another. + + +

Parameters:

+ + +

Returns:

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

Usage:

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

Parameters:

+ + +

Returns:

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

Usage:

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

Parameters:

+ + +

Returns:

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

Usage:

+
    +
    all = set.union (a, b)
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2016-02-08 00:31:49 +
+
+ + diff --git a/modules/std.prototype.strbuf.html b/modules/std.prototype.strbuf.html new file mode 100644 index 0000000..1a5af5e --- /dev/null +++ b/modules/std.prototype.strbuf.html @@ -0,0 +1,275 @@ + + + + + std.prototype 1.0.1 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.prototype.strbuf

+

StrBuf Prototype.

+

+ + +

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

+ + +
+local StrBuf = require "std.prototype.strbuf".prototype
+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
+
+
+ +

In addition to the functionality described here, StrBuf objects also + have all the methods and metamethods of the prototype.object.prototype + (except where overridden here),

+ +

Prototype Chain

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

+ + +

Objects

+ + + + + +
prototypeStrBuf prototype object.
+

Metamethods

+ + + + + + + + + +
prototype:__concat (x)Support concatenation to StrBuf objects.
prototype:__tostring ()Support fast conversion to Lua string.
+

Methods

+ + + + + +
prototype:concat (x)Add a object to a buffer.
+ +
+
+ + +

Objects

+ +
+
+ + prototype +
+
+ StrBuf prototype object. + + +

Fields:

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

See also:

+ + +

Usage:

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

Metamethods

+ +
+
+ + prototype:__concat (x) +
+
+ Support concatenation to StrBuf objects. + + +

Parameters:

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

Returns:

+
    + + prototype + modified buf +
+ + +

See also:

+ + +

Usage:

+
    +
    buf = buf .. x
    +
+ +
+
+ + prototype:__tostring () +
+
+ Support fast conversion to Lua string. + + + +

Returns:

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

See also:

+ + +

Usage:

+
    +
    str = tostring (buf)
    +
+ +
+
+

Methods

+ +
+
+ + prototype:concat (x) +
+
+ Add a object to a buffer. + Elements are stringified lazily, so if you 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:

+
    + + prototype + modified buffer +
+ + + +

Usage:

+
    +
    c = StrBuf {} :concat "append this" :concat (StrBuf {" and", " this"})
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2016-02-08 00:31:49 +
+
+ + diff --git a/modules/std.prototype.trie.html b/modules/std.prototype.trie.html new file mode 100644 index 0000000..61ea3f4 --- /dev/null +++ b/modules/std.prototype.trie.html @@ -0,0 +1,525 @@ + + + + + std.prototype 1.0.1 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module std.prototype.trie

+

Trie Prototype.

+

+ + +

This module returns a table of trie operators, as well as the prototype + for a Trie container object.

+ +

This is not a search tree, but rather a radix tree to efficiently store + and retrieve values stored with a path as a key, such as a multi-key + keytable. Although it does have iterators for walking the trie with + various algorithms.

+ +

In addition to the functionality described here, Trie containers also + have all the methods and metamethods of the prototype.container.prototype + (except where overridden here),

+ +

Prototype Chain

+ + +
+table
+ `-> Container
+      `-> Trie
+
+ +

+ + +

Objects

+ + + + + +
prototypeTrie prototype object.
+

Metamethods

+ + + + + + + + + +
prototype:__index (i)Deep retrieval.
prototype:__newindex (i[, v])Deep insertion.
+

Module Functions

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

Objects

+ +
+
+ + prototype +
+
+ Trie prototype object. + + +

Fields:

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

See also:

+ + +

Usage:

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

Metamethods

+ +
+
+ + prototype:__index (i) +
+
+ Deep retrieval. + + +

Parameters:

+
    +
  • 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}]
    +
+ +
+
+ + prototype:__newindex (i[, v]) +
+
+ Deep insertion. + + +

Parameters:

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

Usage:

+
    +
    function bindkey (keylist, fn) keymap[keylist] = fn end
    +
+ +
+
+

Module Functions

+ +
+
+ + clone (tr, nometa) +
+
+ Make a deep copy of a trie or table, including any metatables. + + +

Parameters:

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

Returns:

+
    + + prototype 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)
    +
+ +
+
+ + ileaves (tr) +
+
+ Trie iterator which returns just numbered leaves, in order. + + +

Parameters:

+ + +

Returns:

+
    +
  1. + function + iterator function
  2. +
  3. + prototype or table + the trie 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
    +
+ +
+
+ + inodes (tr) +
+
+ Trie 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 + prototype or table + trie or trie-like table to iterate over +
  • +
+ +

Returns:

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

See also:

+ + + +
+
+ + leaves (t) +
+
+ Trie iterator which returns just leaves. + + +

Parameters:

+
    +
  • t + table + trie or trie-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)")
    +
+ +
+
+ + merge (t, u) +
+
+ Destructively deep-merge one trie into another. + + +

Parameters:

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

Returns:

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

Usage:

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

+ +

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

+ +

Note that the trie-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 trie-path list before the next iteration + changes it. + + +

Parameters:

+
    +
  • tr + prototype or table + trie or trie-like table to iterate over +
  • +
+ +

Returns:

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

See also:

+ + +

Usage:

+
    +
    + -- trie = +-- node1
    + --        |    +-- leaf1
    + --        |    '-- leaf2
    + --        '-- leaf 3
    + trie = Trie { Trie { "leaf1", "leaf2"}, "leaf3" }
    + for node_type, path, node in nodes (trie) do
    +   print (node_type, path, node)
    + end
    + --> "branch"   {}      {{"leaf1", "leaf2"}, "leaf3"}
    + --> "branch"   {1}     {"leaf1", "leaf2")
    + --> "leaf"     {1,1}   "leaf1"
    + --> "leaf"     {1,2}   "leaf2"
    + --> "join"     {1}     {"leaf1", "leaf2"}
    + --> "leaf"     {2}     "leaf3"
    + --> "join"     {}      {{"leaf1", "leaf2"}, "leaf3"}
    + os.exit (0)
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.3 +Last updated 2016-02-08 00:31:49 +
+
+ + diff --git a/spec/container_spec.yaml b/spec/container_spec.yaml deleted file mode 100644 index fd35da6..0000000 --- a/spec/container_spec.yaml +++ /dev/null @@ -1,156 +0,0 @@ -# Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 -# Copyright (C) 2014-2022 std.prototype authors - -before: - this_module = 'std.prototype.container' - - Container = require(this_module).prototype - -specify std.prototype.container: -- context when required: - - context by name: - - it does not touch the global table: - expect(show_apis {added_to='_G', by=this_module}). - to_equal {} - - context via the prototype module: - - it does not perturb the global namespace: - expect(show_apis {added_to='_G', by='std.prototype'}).to_equal {} - -- describe construction: - - context with table _init: - - it diagnoses missing arguments: | - if have_typecheck then - expect(Container()). - to_raise "bad argument #1 to 'Container'(table expected, got no value)" - end - - it diagnoses too many arguments: | - if have_typecheck then - expect(Container({}, false)). - to_raise "bad argument #2 to 'Container'(no more than 1 argument expected, got 2)" - end - - 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(objtype(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(objtype(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' - - it propagates '_type' field: - things = Container {1} - u, v = things {'u'}, things {'v'} - expect(objtype(u)).to_be 'Container' - expect(objtype(v)).to_be(objtype(Container)) - - context with module functions: - - before: - Bag = require 'std.prototype._base'.Module { - prototype = Container {_type='Bag'}, - count = function(bag) - local n = 0 - for _, m in pairs(bag) do n = n + m end - return n - end, - } - - it does not propagate module 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'", - "method 'count' is not callable (a nil value)" - } - - 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) - - it propagates '_type' field: - things = Bag {bananas=0} - u, v = things {bananas=1}, things {coconuts=0} - expect(objtype(u)).to_be 'Bag' - expect(objtype(v)).to_be(objtype(Bag.prototype)) - - -- 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 __pairs: - - before: - things = Container {'one', 'two', three=3, four=4, 'five', 6} - iter = getmetatable(things).__pairs - - it returns a function: - expect(type(iter)).to_be 'function' - - it iterates all contents: - r = {} - for k, v in iter(things) do r[k] = v end - expect(r).to_contain.a_permutation_of(copy(things)) - - it returns keys in order: - keys = {} - for k in iter(things) do keys[#keys + 1] = k end - expect(keys).to_equal {1, 2, 3, 4, 'four', 'three'} - - -- describe __tostring: - - 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(objtype(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/spec/init_spec.yaml b/spec/init_spec.yaml deleted file mode 100644 index 5e3117b..0000000 --- a/spec/init_spec.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 -# Copyright (C) 2014-2022 std.prototype authors - -before: - this_module = 'std.prototype' - - M = require(this_module) - M.version = nil -- previous specs may have autoloaded it - - -specify std.prototype: -- context when required: - - it does not touch the global table: - expect(show_apis {added_to='_G', by=this_module}). - to_equal {} - -- 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.object - expect(lazy).to_be(require 'std.prototype.object') - - it loads submodule functions on demand: - expect(M.object.type(M.object.prototype)).to_be 'Object' - - -- describe version: - - before: - x = M.version - - - it returns a string: - expect(type(M.version)).to_be 'string' - - it contains package description: - expect(string.match(M.version, 'Prototype Object Libraries')). - not_to_be(nil) diff --git a/spec/object_spec.yaml b/spec/object_spec.yaml deleted file mode 100644 index 2d3c39a..0000000 --- a/spec/object_spec.yaml +++ /dev/null @@ -1,299 +0,0 @@ -# Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 -# Copyright (C) 2014-2022 std.prototype authors - -before: - this_module = 'std.prototype.object' - - object = require(this_module) - Object = object.prototype - obj = Object {'foo', 'bar', baz='quux'} - - function copy(t) - local r = {} - for k, v in pairs(t) do r[k] = v end - return r - end - -specify std.prototype.object: -- context when required: - - context by name: - - it does not touch the global table: - expect(show_apis {added_to='_G', by=this_module}). - to_equal {} - - context via the prototype module: - - it does not perturb the global namespace: - expect(show_apis {added_to='_G', by='std.prototype'}).to_equal {} - -- describe construction: - - context from Object clone method: - - it constructs a new object: - o = Object:clone {} - expect(o).not_to_be(Object) - expect(type(o)).to_be 'table' - expect(objtype(o)).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(objtype(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: - o = Object:clone {foo='bar', _baz='quux'} - expect(getmetatable(o)).not_to_be(getmetatable(Object)) - expect(getmetatable(o)._baz).to_be 'quux' - - -- describe type: - - before: - o = Object {} - fn = object.type - - - context when called from the object module: - - it reports the type stored in the object's metatable: - expect(fn(o)).to_be 'Object' - - it reports the type of a cloned object: - expect(fn(o {})).to_be 'Object' - - it reports the type of a derived object: - Example = Object {_type='Example'} - expect(fn(Example)).to_be 'Example' - - it reports the type of a cloned derived object: - Portal = Object {_type='Demon'} - p = Portal {} - expect(fn(p)).to_be 'Demon' - expect(fn(p {})).to_be 'Demon' - - it returns nil for a primitive object: - expect(fn(nil)).to_be(nil) - expect(fn(0.0)).to_be(nil) - expect(fn('0.0')).to_be(nil) - expect(fn(function() end)).to_be(nil) - expect(fn {}).to_be(nil) - - -- 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 surplus 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 {...} - self.count = select('#', ...) - 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'}} - - it propagates arguments correctly: - expect(Prototype().count).to_be(0) - expect(Prototype('one').count).to_be(1) - expect(Prototype('one', 'two').count).to_be(2) - - it propagates nil arguments correctly: - expect(Prototype(nil).count).to_be(1) - expect(Prototype(false, nil).count).to_be(2) - expect(Prototype(nil, false).count).to_be(2) - expect(Prototype(nil, nil).count).to_be(2) - - -- describe field access: - - before: - Prototype = Object { - _type = 'Prototype', - _init = { 'field', 'method'}, - field = 'in prototype', - method = function(self, ...) - return objtype(self) .. ' class, ' .. - table.concat({...}, ', ') - end, - } - instance = Prototype {'in object', function(self, ...) - return objtype(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 objtype(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 objtype(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: - # :type is a method defined by the root object - - it inherits prototype object methods: - instance = Object {type=object.type} - expect(instance:type()).to_be 'Object' - - it propagates prototype methods to derived instances: - Derived = Object {_type='Derived', type=object.type} - instance = Derived {} - expect(instance:type()).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, - type = object.type, - }, - } - - it inherits prototype object methods: - expect(bag:type()).to_be 'bag' - - it propagates prototype methods to derived instances: - instance = bag {} - expect(instance:type()).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: - o = Object {_type='Derived', 'one', 'two', 'three'} - - it returns a string: - expect(type(tostring(o))).to_be 'string' - - it contains the type: - expect(tostring(Object {})).to_contain 'Object' - expect(tostring(o)).to_contain(objtype(o)) - - it contains the ordered array part elements: - expect(tostring(o)).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(o {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(o)).not_to_contain ';' - expect(tostring(Object {one=true, two=true, three=true})). - not_to_contain ';' - expect(tostring(o {one=true, two=true, three=true})). - to_contain ';' diff --git a/spec/set_spec.yaml b/spec/set_spec.yaml deleted file mode 100644 index c44188a..0000000 --- a/spec/set_spec.yaml +++ /dev/null @@ -1,313 +0,0 @@ -# Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 -# Copyright (C) 2014-2022 std.prototype authors - -before: - this_module = 'std.prototype.set' - - set = require(this_module) - Set = set.prototype - s = Set {'foo', 'bar', 'bar'} - -specify std.prototype.set: -- describe require: - - it does not perturb the global namespace: - expect(show_apis {added_to='_G', by=this_module}). - to_equal {} - - context via the prototype module: - - it does not perturb the global namespace: - expect(show_apis {added_to='_G', by='std.prototype'}).to_equal {} - - -- describe construction: - - it constructs a new set: - s = Set {} - expect(s).not_to_be(Set) - expect(objtype(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(objtype(obj)).to_be 'Set' - expect(obj).to_equal(s) - expect(getmetatable(obj)).to_be(getmetatable(s)) - - it serves as a prototype for new types: - Bag = Set {_type='Bag'} - expect(objtype(Bag)).to_be 'Bag' - bag = Bag {'goo', 'gar', 'gar'} - expect(objtype(bag)).to_be 'Bag' - -- describe delete: - - context when called as a Set module function: - - before: - fn = set.delete - s = Set {'foo', 'bar', 'baz'} - - it returns a set object: - expect(objtype(fn(s, 'foo'))).to_be 'Set' - - it is destructive: - fn(s, 'bar') - expect(s).not_to_have_member 'bar' - - it returns the modified set: - expect(fn(s, 'baz')).not_to_have_member 'baz' - - it ignores removal of non-members: | - clone = s {} - expect(fn(s, 'quux')).to_equal(clone) - - it deletes a member from the set: - expect(s).to_have_member 'bar' - fn(s, 'bar') - expect(s).not_to_have_member 'bar' - - it works with an empty set: - expect(fn(Set {}, 'quux')).to_equal(Set {}) - - -- describe difference: - - before: - fn = set.difference - r = Set {'foo', 'bar', 'baz'} - s = Set {'bar', 'baz', 'quux'} - - - context when called as a Set module function: - - it returns a set object: - expect(objtype(fn(r, s))).to_be 'Set' - - it is non-destructive: - fn(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(fn(r, s)).to_equal(Set {'foo'}) - - context when called as a set metamethod: - - it returns a set object: - expect(objtype(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: - fn = set.elems - s = Set {'foo', 'bar', 'baz'} - - - context when called as a Set module function: - - it is an iterator over set members: - t = {} - for e in fn(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 fn(Set {}) do table.insert(t, e) end - expect(t).to_equal {} - - -- describe insert: - - context when called as a Set module function: - - before: - fn = set.insert - s = Set {'foo'} - - it returns a set object: - expect(objtype(fn(s, 'bar'))).to_be 'Set' - - it is destructive: - fn(s, 'bar') - expect(s).to_have_member 'bar' - - it returns the modified set: - expect(fn(s, 'baz')).to_have_member 'baz' - - it ignores insertion of existing members: - expect(fn(s, 'foo')).to_equal(Set {'foo'}) - - it inserts a new member into the set: - expect(s).not_to_have_member 'bar' - fn(s, 'bar') - expect(s).to_have_member 'bar' - - it works with an empty set: - expect(fn(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(objtype(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: - fn = set.intersection - r = Set {'foo', 'bar', 'baz'} - s = Set {'bar', 'baz', 'quux'} - - - context when called as a Set module function: - - it returns a set object: - expect(objtype(fn(r, s))).to_be 'Set' - - it is non-destructive: - fn(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(fn(r, s)). - to_equal(Set {'bar', 'baz'}) - - context when called as a set metamethod: - - it returns a set object: - q = r * s - expect(objtype(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: - fn = set.member - s = Set {'foo', 'bar'} - - - context when called as a Set module function: - - it succeeds when set contains the given member: - expect(fn(s, 'foo')).to_be(true) - - it fails when set does not contain the given member: - expect(fn(s, 'baz')).not_to_be(true) - - it works with the empty set: - s = Set {} - expect(fn(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: - fn = set.proper_subset - 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(fn(s, r)).to_be(true) - - it fails when two sets are equal: - r = s {} - expect(fn(s, r)).to_be(false) - - it fails when set does not contain all elements of another: - s = s + Set {'quux'} - expect(fn(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: - fn = set.subset - 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(fn(s, r)).to_be(true) - - it succeeds when two sets are equal: - r = s {} - expect(fn(s, r)).to_be(true) - - it fails when set does not contain all elements of another: - s = s + Set {'quux'} - expect(fn(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: - fn = set.symmetric_difference - r = Set {'foo', 'bar', 'baz'} - s = Set {'bar', 'baz', 'quux'} - - - context when called as a Set module function: - - it returns a set object: - expect(objtype(fn(r, s))).to_be 'Set' - - it is non-destructive: - fn(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(fn(r, s)).to_equal(Set {'foo', 'quux'}) - - context when called as a set metamethod: - - it returns a set object: - expect(objtype(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: - fn = set.union - r = Set {'foo', 'bar', 'baz'} - s = Set {'bar', 'baz', 'quux'} - - - context when called as a Set module function: - - it returns a set object: - expect(objtype(fn(r, s))).to_be 'Set' - - it is non-destructive: - fn(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(fn(r, s)). - to_equal(Set {'foo', 'bar', 'baz', 'quux'}) - - context when called as a set metamethod: - - it returns a set object: - expect(objtype(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/spec/spec_helper.lua b/spec/spec_helper.lua deleted file mode 100644 index c312489..0000000 --- a/spec/spec_helper.lua +++ /dev/null @@ -1,142 +0,0 @@ -local typecheck -have_typecheck, typecheck = pcall (require, 'typecheck') - -local inprocess = require 'specl.inprocess' -local hell = require 'specl.shell' -local std = require 'specl.std' - -badargs = require 'specl.badargs' - -package.path = std.package.normalize ('./lib/?.lua', './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' - - --- Allow use of bare 'unpack' even in Lua 5.3. -unpack = table.unpack or unpack - - -function copy (t) - local r = {} - for k, v in next, t do r[k] = v end - return r -end - - --- In case we're not using a bleeding edge release of Specl... -_diagnose = badargs.diagnose -badargs.diagnose = function (...) - if have_typecheck then - return _diagnose (...) - end -end - - --- A copy of base.lua:type, so that an unloadable base.lua doesn't --- prevent everything else from working. -function objtype (o) - return (getmetatable (o) or {})._type or io.type (o) or type (o) -end - - -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 - - -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} --- --- @tparam table argt one of the combinations above --- @treturn table a list of keys according to criteria above -function show_apis (argt) - return tabulate_output ([[ - local before, after = {}, {} - for k in pairs (]] .. argt.added_to .. [[) do - before[k] = true - end - - local M = require ']] .. argt.by .. [[' - for k in pairs (]] .. argt.added_to .. [[) do - after[k] = true - end - - for k in pairs (after) do - if not before[k] then print (k) end - end - ]]) -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_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/spec/strbuf_spec.yaml b/spec/strbuf_spec.yaml deleted file mode 100644 index 2d6de60..0000000 --- a/spec/strbuf_spec.yaml +++ /dev/null @@ -1,106 +0,0 @@ -# Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 -# Copyright (C) 2014-2022 std.prototype authors - -before: - this_module = 'std.prototype.strbuf' - - StrBuf = require(this_module).prototype - b = StrBuf {'foo', 'bar'} - - -specify std.prototype.strbuf: -- describe require: - - it does not perturb the global namespace: - expect(show_apis {added_to='_G', by=this_module}). - to_equal {} - - context via the prototype module: - - it does not perturb the global namespace: - expect(show_apis {added_to='_G', by='std.prototype'}).to_equal {} - - -- describe construction: - - context from StrBuf clone method: - - it constructs a new strbuf: - b = StrBuf:clone {} - expect(b).not_to_be(StrBuf) - expect(objtype(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(objtype(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(objtype(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(objtype(obj)).to_be 'StrBuf' - expect(obj).to_equal(b) - expect(getmetatable(obj)).to_be(getmetatable(b)) - - -- describe tostring: - - 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(objtype(a)).to_be 'StrBuf' - expect(tostring(a)).to_be 'foobarbaz' - - it appends a StrBuf: - a = StrBuf.concat(a, b) - expect(objtype(a)).to_be 'StrBuf' - expect(tostring(a)).to_be 'foobarbazquux' - - context as an object method: - - it appends a string: - a = a:concat 'baz' - expect(objtype(a)).to_be 'StrBuf' - expect(tostring(a)).to_be 'foobarbaz' - - it appends a StrBuf: - a = a:concat(b) - expect(objtype(a)).to_be 'StrBuf' - expect(tostring(a)).to_be 'foobarbazquux' - - context as a metamethod: - - it appends a string: - a = a .. 'baz' - expect(objtype(a)).to_be 'StrBuf' - expect(tostring(a)).to_be 'foobarbaz' - - it appends a StrBuf: - a = a .. b - expect(objtype(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/spec/trie_spec.yaml b/spec/trie_spec.yaml deleted file mode 100644 index ac8621e..0000000 --- a/spec/trie_spec.yaml +++ /dev/null @@ -1,423 +0,0 @@ -# Prototype Oriented Programming for Lua 5.1, 5.2, 5.3 & 5.4 -# Copyright (C) 2014-2022 std.prototype authors - -before: - this_module = 'std.prototype.trie' - - trie = require(this_module) - Trie = trie.prototype - -specify std.prototype.trie: -- before: - t = {foo='foo', fnord={branch={bar='bar', baz='baz'}}, quux='quux'} - tr = Trie(t) - -- context when required: - - context by name: - - it does not touch the global table: - expect(show_apis {added_to='_G', by=this_module}). - to_equal {} - - - context via the prototype module: - - it does not touch the global table: - expect(show_apis {added_to='_G', by='std.prototype'}). - to_equal {} - -- describe construction: - - it constructs a new trie: - tr = Trie {} - expect(tr).not_to_be(Trie) - expect(objtype(tr)).to_be 'Trie' - - it turns a table argument into a trie: - expect(objtype(Trie(t))).to_be 'Trie' - - it does not turn table argument values into sub-Tries: - expect(objtype(tr['fnord'])).to_be 'table' - - it understands branched nodes: - expect(tr).to_equal(Trie(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(objtype(obj)).to_be 'Trie' - expect(obj).to_equal(tr) - expect(getmetatable(obj)).to_be(getmetatable(tr)) - - -- describe clone: - - before: - subject = {k1={'v1'}, k2={'v2'}, k3={'v3'}} - f = trie.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: - if have_typecheck then - expect(f()).to_raise('table expected') - expect(f 'foo').to_raise('table expected') - end - - -- describe ileaves: - - before: - f = trie.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 tries too: - for v in f(Trie {Trie {'one', - Trie {two=2}, - Trie {Trie {'three'}, four=4} - }, - foo='bar', 'five'}) - do - l[1+#l]=v - end - expect(l).to_equal {'one', 'three', 'five'} - - it diagnoses non-table arguments: - if have_typecheck then - expect(f()).to_raise('table expected') - expect(f 'string').to_raise('table expected') - end - - -- describe inodes: - - before: | - f = trie.inodes - - function traverse(subject) - l = {} - for ty, p, n in f(subject) do - l[1+#l]={ty, trie.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 tries too: | - subject = Trie {Trie {'one', - Trie {two=2}, - Trie {Trie {'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: - if have_typecheck then - expect(f()).to_raise('table expected') - expect(f 'string').to_raise('table expected') - end - - -- describe leaves: - - before: - f = trie.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 tries too: - for v in f(Trie {Trie {'one', - Trie {two=2}, - Trie {Trie {'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: - if have_typecheck then - expect(f()).to_raise('table expected') - expect(f 'string').to_raise('table expected') - end - - -- describe merge: - - before: | - f = trie.merge - - -- Additional merge keys which are moderately unusual - t1 = Trie {k1='v1', k2='if', k3=Trie {'?'}} - t2 = Trie {['if']=true, [{'?'}]=false, _='underscore', k3='v2'} - - target = trie.clone(t1) - for ty, p, n in trie.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, Trie {})).to_be(t1) - - it copies t2 when t1 is empty: | - expect(f(Trie {}, 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 = trie.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: - if have_typecheck then - expect(f(nil, {})).to_raise('table expected') - expect(f({}, nil)).to_raise('table expected') - end - - -- describe nodes: - - before: - f = trie.nodes - - function traverse(subject) - l = {} - for ty, p, n in f(subject) do l[1+#l]={ty, trie.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 tries 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 = Trie {Trie {'one', - Trie {two=2}, - Trie {Trie {'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 = Trie {'first', Trie {'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: - if have_typecheck then - expect(f()).to_raise('table expected') - expect(f 'string').to_raise('table expected') - end - - -- 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 trie 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 = Trie {} - - it stores values for simple keys: - tr['foo'] = 'foo' - expect(tr).to_equal(Trie {foo='foo'}) - - it stores values for single element key lists: - tr[{'foo'}] = 'foo' - expect(tr).to_equal(Trie {foo='foo'}) - - it stores values for multi-element key lists: - tr[{'foo', 'bar'}] = 'baz' - expect(tr).to_equal(Trie {foo=Trie {bar='baz'}}) - - it separates branches for diverging key lists: - tr[{'foo', 'branch', 'bar'}] = 'leaf1' - tr[{'foo', 'branch', 'baz'}] = 'leaf2' - expect(tr).to_equal(Trie {foo=Trie {branch=Trie {bar='leaf1', baz='leaf2'}}}) - - -- describe __tostring: - - it returns a string: - expect(objtype(tostring(tr))).to_be 'string' - - it shows the type name: - expect(tostring(tr)).to_contain 'Trie' - - it shows the contents in order: | - tr = Trie {foo = 'foo', - fnord = Trie {branch = Trie {bar='bar', baz='baz'}}, - quux = 'quux'} - expect(tostring(tr)). - to_contain 'fnord=Trie {branch=Trie {bar=bar, baz=baz}}, foo=foo, quux=quux' diff --git a/std.prototype-git-1.rockspec b/std.prototype-git-1.rockspec deleted file mode 100644 index adb934a..0000000 --- a/std.prototype-git-1.rockspec +++ /dev/null @@ -1,50 +0,0 @@ -local _MODREV, _SPECREV = 'git', '-1' - -package = 'std.prototype' -version = _MODREV .. _SPECREV - -description = { - summary = 'Prototype Oriented Programming with Lua', - detailed = [[ - A straight forward prototype-based object system, and a selection of - useful objects build on it. - ]], - homepage = 'http://lua-stdlib.github.io/prototype', - license = 'MIT/X11', -} - -source = (function(gitp) - if gitp then - return { - url = 'git://github.com/lua-stdlib/prototype.git', - } - else - return { - url = 'http://github.com/lua-stdlib/prototype/archive' .. _MODREV .. '.zip', - dir = 'prototype-' .. _MODREV, - } - end -end)(_MODREV == 'git') - -dependencies = { - 'lua >= 5.1, < 5.5', - 'std.normalize >= 1.0.3', -} - -if _MODREV == 'git' then - dependencies[#dependencies + 1] = 'ldoc' -end - -build = { - type = 'builtin', - modules = { - ['std.prototype'] = 'lib/std/prototype/init.lua', - ['std.prototype._base'] = 'lib/std/prototype/_base.lua', - ['std.prototype.container'] = 'lib/std/prototype/container.lua', - ['std.prototype.object'] = 'lib/std/prototype/object.lua', - ['std.prototype.set'] = 'lib/std/prototype/set.lua', - ['std.prototype.strbuf'] = 'lib/std/prototype/strbuf.lua', - ['std.prototype.trie'] = 'lib/std/prototype/trie.lua', - ['std.prototype.version'] = 'lib/std/prototype/version.lua', - }, -}