diff options
220 files changed, 60803 insertions, 87 deletions
diff --git a/Data/Libraries/Penlight/.busted b/Data/Libraries/Penlight/.busted new file mode 100644 index 0000000..ed6f887 --- /dev/null +++ b/Data/Libraries/Penlight/.busted @@ -0,0 +1,6 @@ +return { + default = { + lpath = "./lua/?.lua;./lua/?/init.lua", + } +} +-- vim: ft=lua diff --git a/Data/Libraries/Penlight/.editorconfig b/Data/Libraries/Penlight/.editorconfig new file mode 100644 index 0000000..3434e8a --- /dev/null +++ b/Data/Libraries/Penlight/.editorconfig @@ -0,0 +1,22 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +[*.lua] +indent_style = space +indent_size = 2 + +[kong/templates/nginx*] +indent_style = space +indent_size = 4 + +[*.template] +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab diff --git a/Data/Libraries/Penlight/.github/workflows/luacheck.yml b/Data/Libraries/Penlight/.github/workflows/luacheck.yml new file mode 100644 index 0000000..20b706c --- /dev/null +++ b/Data/Libraries/Penlight/.github/workflows/luacheck.yml @@ -0,0 +1,23 @@ +name: Luacheck + +on: [push, pull_request] + +jobs: + luacheck: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Lua + uses: leafo/gh-actions-lua@v8 + with: + luaVersion: 5.4 + - name: Setup Lua Rocks + uses: leafo/gh-actions-luarocks@v4 + - name: Setup dependencies + run: luarocks install luacheck + - name: Run Code Linter + run: | + luacheck . + luarocks lint *.rockspec diff --git a/Data/Libraries/Penlight/.gitignore b/Data/Libraries/Penlight/.gitignore new file mode 100644 index 0000000..1237f8f --- /dev/null +++ b/Data/Libraries/Penlight/.gitignore @@ -0,0 +1,2 @@ +luacov.stats.out +*.rock diff --git a/Data/Libraries/Penlight/.luacheckrc b/Data/Libraries/Penlight/.luacheckrc new file mode 100644 index 0000000..82dab88 --- /dev/null +++ b/Data/Libraries/Penlight/.luacheckrc @@ -0,0 +1,40 @@ +unused_args = false +redefined = false +max_line_length = false + +globals = { + "ngx", +} + +not_globals = { + "string.len", + "table.getn", +} + +include_files = { + "**/*.lua", + "*.rockspec", + ".busted", + ".luacheckrc", +} + +files["spec/**/*.lua"] = { + std = "+busted", +} + +exclude_files = { + "tests/*.lua", + "tests/**/*.lua", + -- Travis Lua environment + "here/*.lua", + "here/**/*.lua", + -- GH Actions Lua Environment + ".lua", + ".luarocks", + ".install", + + -- TODO: fix these files + "examples/symbols.lua", + "examples/test-symbols.lua", +} + diff --git a/Data/Libraries/Penlight/.luacov b/Data/Libraries/Penlight/.luacov new file mode 100644 index 0000000..93b9f3b --- /dev/null +++ b/Data/Libraries/Penlight/.luacov @@ -0,0 +1,4 @@ +modules = { + ["pl"] = "lua/pl/init.lua", + ["pl.*"] = "lua" +} diff --git a/Data/Libraries/Penlight/.travis.yml b/Data/Libraries/Penlight/.travis.yml new file mode 100644 index 0000000..3203334 --- /dev/null +++ b/Data/Libraries/Penlight/.travis.yml @@ -0,0 +1,30 @@ +language: python +sudo: false + +env: + - LUA="lua 5.1" + - LUA="lua 5.2" + - LUA="lua 5.3" + - LUA="lua 5.4" + - LUA="luajit 2.0" + - LUA="luajit 2.0 --compat 5.2" + - LUA="luajit 2.1" + - LUA="luajit 2.1 --compat 5.2" + +before_install: + - pip install hererocks + - hererocks here -r^ --$LUA + - source here/bin/activate + - luarocks install luacov-coveralls + - luarocks install busted + +install: + - luarocks make + +script: + - busted -c -v + - lua run.lua tests --luacov + - lua run.lua examples + +after_success: + - luacov-coveralls diff --git a/Data/Libraries/Penlight/CHANGELOG.md b/Data/Libraries/Penlight/CHANGELOG.md new file mode 100644 index 0000000..bbbbfbe --- /dev/null +++ b/Data/Libraries/Penlight/CHANGELOG.md @@ -0,0 +1,595 @@ +# Changelog + +Versioning is strictly according to [Semantic Versioning](https://semver.org/), +see the [README.md](README.md#versioning) for details on version scoping and +deprecation policy. + +see [CONTRIBUTING.md](CONTRIBUTING.md#release-instructions-for-a-new-version) for release instructions + +## 1.12.0 (unreleased) + - feat: `utils.npairs` added. An iterator with a range that honours the `n` field + [#387](https://github.com/lunarmodules/Penlight/pull/387) + +## 1.11.0 (2021-08-18) + + - fix: `stringx.strip` behaved badly with string lengths > 200 + [#382](https://github.com/lunarmodules/Penlight/pull/382) + - fix: `path.currentdir` now takes no arguments and calls `lfs.currentdir` without argument + [#383](https://github.com/lunarmodules/Penlight/pull/383) + - feat: `utils.raise_deprecation` now has an option to NOT include a + stack-trace [#385](https://github.com/lunarmodules/Penlight/pull/385) + + +## 1.10.0 (2021-04-27) + + - deprecate: `permute.iter`, renamed to `permute.order_iter` (removal later) + [#360](https://github.com/lunarmodules/Penlight/pull/360) + - deprecate: `permute.table`, renamed to `permute.order_table` (removal later) + [#360](https://github.com/lunarmodules/Penlight/pull/360) + - deprecate: `Date` module (removal later) + [#367](https://github.com/lunarmodules/Penlight/pull/367) + - feat: `permute.list_iter` to iterate over different sets of values + [#360](https://github.com/lunarmodules/Penlight/pull/360) + - feat: `permute.list_table` generate table with different sets of values + [#360](https://github.com/lunarmodules/Penlight/pull/360) + - feat: Lua 5.4 'warn' compatibility function + [#366](https://github.com/lunarmodules/Penlight/pull/366) + - feat: deprecation functionality `utils.raise_deprecation` + [#361](https://github.com/lunarmodules/Penlight/pull/361) + - feat: `utils.splitv` now takes same args as `split` + [#373](https://github.com/lunarmodules/Penlight/pull/373) + - fix: `dir.rmtree` failed to remove symlinks to directories + [#365](https://github.com/lunarmodules/Penlight/pull/365) + - fix: `pretty.write` could error out on failing metamethods (Lua 5.3+) + [#368](https://github.com/lunarmodules/Penlight/pull/368) + - fix: `app.parse` now correctly parses values containing '=' or ':' + [#373](https://github.com/lunarmodules/Penlight/pull/373) + - fix: `dir.makepath` failed to create top-level directories + [#372](https://github.com/lunarmodules/Penlight/pull/372) + - overhaul: `array2d` module was updated, got additional tests and several + documentation updates + [#377](https://github.com/lunarmodules/Penlight/pull/377) + - feat: `aray2d` now accepts negative indices + - feat: `array2d.row` added to align with `column` + - fix: bad error message in `array2d.map` + - fix: `array2d.flatten` now ensures to deliver a 'square' result if `nil` is + encountered + - feat: `array2d.transpose` added + - feat: `array2d.swap_rows` and `array2d.swap_cols` now return the array + - fix: `aray2d.range` correctly recognizes `R` column in spreadsheet format, was + mistaken for `R1C1` format. + - fix: `aray2d.range` correctly recognizes 2 char column in spreadsheet format + - feat: `array2d.default_range` added (previously private) + - feat: `array2d.set` if used with a function now passes `i,j` to the function + in line with the `new` implementation. + - fix: `array2d.iter` didn't properly iterate the indices + [#376](https://github.com/lunarmodules/Penlight/issues/376) + - feat: `array2d.columns` now returns a second value; the column index + - feat: `array2d.rows` added to be in line with `columns` + + +## 1.9.2 (2020-09-27) + + - fix: dir.walk [#350](https://github.com/lunarmodules/Penlight/pull/350) + + +## 1.9.1 (2020-09-24) + + - released to superseed the 1.9.0 version which was retagged in git after some + distro's already had picked it up. This version is identical to 1.8.1. + +## 1.8.1 (2020-09-24) (replacing a briefly released but broken 1.9.0 version) + +## Fixes + + - In `pl.class`, `_init` can now be inherited from grandparent (or older ancestor) classes. [#289](https://github.com/lunarmodules/Penlight/pull/289) + - Fixes `dir`, `lexer`, and `permute` to no longer use coroutines. [#344](https://github.com/lunarmodules/Penlight/pull/344) + +## 1.8.0 (2020-08-05) + +### New features + + - `pretty.debug` quickly dumps a set of values to stdout for debug purposes + +### Changes + + - `pretty.write`: now also sorts non-string keys [#319](https://github.com/lunarmodules/Penlight/pull/319) + - `stringx.count` has an extra option to allow overlapping matches + [#326](https://github.com/lunarmodules/Penlight/pull/326) + - added an extra changelog entry for `types.is_empty` on the 1.6.0 changelog, due + to additional fixed behaviour not called out appropriately [#313](https://github.com/lunarmodules/Penlight/pull/313) + - `path.packagepath` now returns a proper error message with names tried if + it fails + +### Fixes + + - Fix: `stringx.rfind` now properly works with overlapping matches + [#314](https://github.com/lunarmodules/Penlight/pull/314) + - Fix: `package.searchpath` (in module `pl.compat`) + [#328](https://github.com/lunarmodules/Penlight/pull/328) + - Fix: `path.isabs` now reports drive + relative-path as `false`, eg. "c:some/path" (Windows only) + - Fix: OpenResty coroutines, used by `dir.dirtree`, `pl.lexer`, `pl.permute`. If + available the original coroutine functions are now used [#329](https://github.com/lunarmodules/Penlight/pull/329) + - Fix: in `pl.strict` also predefine global `_PROMPT2` + - Fix: in `pl.strict` apply `tostring` to the given name, in case it is not a string. + - Fix: the lexer would not recognize numbers without leading zero; "-.123". + See [#315](https://github.com/lunarmodules/Penlight/issues/315) + +## 1.7.0 (2019-10-14) + +### New features + + - `utils.quote_arg` will now optionally take an array of arguments and escape + them all into a single string. + - `app.parse_args` now accepts a 3rd parameter with a list of valid flags and aliasses + - `app.script_name` returns the name of the current script (previously a private function) + +### Changes + + - Documentation updates + - `utils.quit`: exit message is no longer required, and closes the Lua state (on 5.2+). + - `utils.assert_arg` and `utils.assert_string`: now return the validated value + - `pl.compat`: now exports the `jit` and `jit52` flags + - `pretty.write`: now sorts the output for easier diffs [#293](https://github.com/lunarmodules/Penlight/pull/293) + +### Fixes + + - `utils.raise` changed the global `on_error`-level when passing in bad arguments + - `utils.writefile` now checks and returns errors when writing + - `compat.execute` now handles the Windows exitcode -1 properly + - `types.is_empty` would return true on spaces always, independent of the parameter + - `types.to_bool` will now compare case-insensitive for the extra passed strings + - `app.require_here` will now properly handle an absolute base path + - `stringx.split` will no longer append an empty match if the number of requested + elements has already been reached [#295](https://github.com/lunarmodules/Penlight/pull/295) + - `path.common_prefix` and `path.relpath` return the result in the original casing + (only impacted Windows) [#297](https://github.com/lunarmodules/Penlight/pull/297) + - `dir.copyfile`, `dir.movefile`, and `dir.makepath` create the new file/path with + the requested casing, and no longer force lowercase (only impacted Windows) + [#297](https://github.com/lunarmodules/Penlight/pull/297) + - added a missing assertion on `path.getmtime` [#291](https://github.com/lunarmodules/Penlight/pull/291) + - `stringx.rpartition` returned bad results on a not-found [#299](https://github.com/lunarmodules/Penlight/pull/299) + +## 1.6.0 (2018-11-23) + +### New features + + - `pl.compat` now provides `unpack` as `table.unpack` on Lua 5.1 + +### Changes + + - `utils.unpack` is now documented and respects `.n` field of its argument. + - `tablex.deepcopy` and `tablex.deepcompare` are now cycle aware (#262) + - Installing through LuaRocks will now include the full rendered documentation + +### Fixes + + - Fixed `seq.last` returning `nil` instead of an empty list when given an empty iterator (#253). + - `pl.template` now applies `tostring` when substituting values in templates, avoiding errors when they are not strings or numbers (#256). + - Fixed `pl.import_into` not importing some Penlight modules (#268). + - Fixed version number stuck at 1.5.2 (#260). + - Fixed `types.is_empty` returning `true` on tables containing `false` key (#267). + - Fixed `types.is_empty` returning `false` if not a nil/table/string + - Fixed `test.assertraise` throwing an error when passed an array with a function to call plus its arguments (#272). + - Fixed `test.assertraise` not throwing an error when given function does not error but instead returns a string matching given error pattern. + - Fixed placeholder expressions being evaluated with wrong precedence of binary and unary negation. + - Fixed placeholder expressions being evaluated assuming wrong binary operator associativity (e.g. `_1-(_2+_3)` was evaluated as `(_1-_2)+_3`. + - Fixed placeholder expressions being evaluated as if unary operators take precedence over power operator (e.g. `(-_1)^_2`) was evaluated as `-(_1^2)`). + - Fixed vulnerable backtracking pattern in `pl.stringx.strip` (#275) + +## 1.5.4 (2017-07-17) + +### Fixes + + - Fixed `compat.execute` behaving differently on Lua 5.1 and 5.1+. + - Fixed `lapp.process_options_string` setting global `success` variable. + +## 1.5.3 (2017-07-16) + +### Changes + + - Added `template.compile` function that allows caching compiled template and rendering it multiple times. + - Added special `_debug` field to environment table argument in `template.substitute` for printing generated template code upon render error. + +### Fixes + + - Fixed error (`attempt to concatenate a nil value (local 'vtype')`) in `lapp.process_options_string`. + +## 1.5.2 (2017-04-08) + +### Fixes + + - Removed leftover debug pring in `lapp.process_options_string`. + +## 1.5.1 (2017-04-02) + +### Fixes + + - Fixed `dir.getfiles` matching given pattern against full paths from base directory instead of file names. + +## 1.5.0 (2017-04-01) + +### Changes + + - `stringx.splitlines` considers `\r\n` a single line ending. + - `stringx.splitlines` returns an empty list for an empty string. + +### Fixes + + - `tablex.count_map` no longer raises an error. + - `strict.module` correctly handles existing `__index` metamethod returning `false`. + - `app.parse_args` accepts colon as a separator between option name and value, as advertised. + - `pretty.load` handles case where a C hook is present. + ' `os.execute` had issue with LuaJIT in 5.2 compat mode. + +### Features + + - `template` supports customizing inline escape character and chunk name. + - `seq` constructor supports iterators with a state object as the second argument. + - `stringx.splitlines` has `keep_ends` argument. + - `tablex.reduce` can take an optional initial value. + +## 1.4.1 (2016-08-16) + +### Changes + + - All functions that return instances of `pl.List`, `pl.Map` and `pl.Set` now require corresponding modules, + so that their methods always work right away. + +### Fixes + + - Fixed `dir.getallfiles` returning an empty array when called without `pattern` argument. + +### Features + +## 1.4.0 (2016-08-14) + +### Changes + +### Fixes + + - `pl.path` covers edge cases better (e.g `path.normpath` was broken) + - `p.dir` shell patterns fixed + - `os.tmpname` broken on modern Windows/MSVC14 + - (likewise for `utils.executeex` which depends on it) + - `pretty.write` more robust and does not lose floating-point precision; + saves and restores debug hooks when loading. + - `pl.lexer` fixes: `cpp` lexer now filters space by default + - `tablex.sortv` no longer assumes that the values are all unique + - `stringx.center` is now consistent with Python; `stringx.rfind` and + `string.quote_string` fixed. + - `data.write` had a problem with default delimiter, properly returns error now. + - `pl.Set` `+` and `-` now have correct semantics + +### Features + + - `pl.tablex` has `union` and `merge` convenience functions + - `pl.lapp` understands '--' meaning end of parsed arguments + - `utils.quote_arg` quotes command arguments for `os.execute`, + correctly handling all special characters. + - `utils.writefile` has optional `is_bin` argument + - 'pl.lexer' supports line numbers with string argument + - `stringx.endswith` may be passed an array of possible suffixes. + - `data.read` - in CSV mode, assume empty fields are numerical zero + + +## 1.3.2 (2015-05-10) + +### Changes + + - now works and passes tests with Lua 5.3 + - utils.import will NOT override global symbols (import 'math' caused global type() to be clobbered) + - Updated pl.dir.file_op to return true on success and false on failure... + - workaround for issues with pl.lapp with amalg.lua - will look at global LAPP_SCRIPT if arg[0] is nil + +### Fixes + + - func was broken: do NOT use ipairs to iterate if __index is overriden! + - issue #133 pretty.read (naively) confused by unbalanced brackets + - xml attribute underscore fix for simple parser + - Fix path.normpath + - lexer: fix parsing block comments/string. fix hang on empty string. + - Fixed utils.execute returning different values for Lua 5.1 and Lua 5.2 + - Issue #97; fixed attempt to put a month into a day + - problem with tablex.count_map with custom comparison + - tablex.pairmap overwrites result if key already exists; instead, upon detection that key already exists + for a returned value, we modify the key's value to be a table and insert values into that table + +### Features + + - Add Python style url module for quote and unquote. + - stringx.quote_string, which scans for embedded long-string quote matches and escapes them by creating a long-string quote. + - issue #117: tablex.range now works with decreasing numbers, consistent with numerical for loop + - utils.import will NOT override global symbols (import 'math' caused global type() to be clobbered) + - issue #125: DOCTYPE ignored in xml documents as well + - Allow XML tostring() function to customize the default prefacing with `<?xml...>` + - More Robust Quoted Strings + - lapp: improved detection of unsupported short flags + +## 1.3.1 (2013-09-24) + +## 1.3.0 (2013-09-14) + +### Changes + + - class: RIP base method - not possible to implement correctly + - lapp: short flags can now always be followed directly by their value, for instance, +`-I/usr/include/lua/5.1` + - Date: new explicit `Date.Interval` class; `toUTC/toLocal` return new object; `Date.__tostring` +always returns ISO 8601 times for exact serialization. `+/-` explicit operators. Date objects +are explicitly flagged as being UTC or not. + +### Fixes + + - class: super method fixed. + - Date: DST is now accounted for properly. + - Date: weekday calculation borked. + +### Features + + - All tests pass with no-5.1-compatible Lua 5.2; now always uses `utils.load` and +`utils.unpack` is always available. + - types: new module containing `utils.is_xxx` methods plus new `to_bool`. + - class: can be passed methods in a table (see `test=klass.lua`). This is +particularly convenient for using from Moonscript. + - general documentation improvements, e.g `class` + +## 1.2.1 (2013-06-21) + +### Changes + + - utils.set(get)fenv always defined (_not_ set as globals for 5.2 anymore!). + These are defined in new module pl.compat, but still available through utils. + - class.Frodo now puts 'Frodo' in _current environment_ + +### Fixes + + - lapp.add_type was broken (Pete Kazmier) + - class broke with classes that redefined __newindex + - Set.isdisjoint was broken because of misspelling; default ctor Set() now works as expected + - tablex.transform was broken; result now has same keys as original (CoolistheName007) + - xml match not handling empty matches (royalbee) + - pl.strict: assigning nil to global declares it, as God intended. (Pierre Chapuis) + - tests all work with pl.strict + - 5.2 compatible load now respects mode + - tablex.difference thought that a value of `false` meant 'not present' (Andrew Starke) + +### Features + + - tablex.sort(t) iterates over sorted keys, tablex.sortv(t) iterates over sorted values (Pete Kazmier) + - tablex.readonly(t) creates a read-only proxy for a table (John Schember) + - utils.is_empty(o) true if o==nil, o is an empty table, or o is an empty string (John Schember) + - utils.executeex(cmd,bin) returns true if successful, return code, plus stdout and stderr output as strings. (tieske) + - class method base for calling inherited methods (theypsilon) + - class supports pre-constructor _create for making a custom self (used in pl.List) + - xml HTML mode improvements - can parse non-trivial well-formed HTML documents. + xml.parsehtml is a parse function, no longer a flag + - if a LOM document has ordered attributes, use these when stringifying + - xml.tostring has yet another extra parm to force prefacing with `<?xml...>` + - lapp boolean flags may have `true` default + - lapp slack mode where 'short' flags can be multi-char + - test.asserteq etc take extra arg, which is extra level where error must be reported at + - path.currentdir,chdir,rmdir,mkdir and dir as alias to lfs are exported; no dependencies on luafilesystem outside pl.path, making it easier to plug in different implementations. + +## 1.2.0 (2013-05-28) + +## 1.1.1 (2013-05-14) + +## 1.1.0 (2013-03-18) + +## 1.0.3 (2012-12-07) + +## 1.0.2 (2012-05-12) + +## 1.0.1 (2012-05-26) + +## 1.0.0 (2012-04-26) + +## 0.9.8 (2011-11-27) + +## 0.9.7 (2011-11-27) + +### Lua 5.2 compatibility + +(These are all now defined in pl.utils) + +- setfenv, getfenv defined for Lua 5.2 (by Sergey Rozhenko) + +### Changes + +- array2d.flatten is new +- OrderedMap:insert is new + +### Fixes + +- seq.reduce re-implemented to give correct order (Carl 脜dahl) +- seq.unique was broken: new test +- tablex.icopy broken for last argument; new test +- utils.function_arg last parm 'msg' was missing +- array2d.product was broken; more sensible implementation +- array2d.range, .slice, .write were broken +- text optional operator % overload broken for 'fmt % fun'; new tests +- a few occurances of non-existent function utils.error removed + + +## 0.9.6 (2011-09-11) + +### Lua 5.2 compatibility + +- Bad string escape in tests fixed + +### Changes + +- LuaJIT FFI used on Windows for Copy/MoveFile functionality + +### Fixes + +- Issue 13 seq.sort now calls seq.copy +- issue 14 bad pattern to escape trailing separators in path.abspath +- lexer: string tokens broken with some combinations +- lexer: long comments broken for Lua and C +- stringx.split behaves according to Python spec; extra parm meaning 'max splits' +- stringx.title behaves according to Python spec +- stringx.endswith broken for 2nd arg being table of postfixes +- OrderedMap.set broken when value was nil and key did not exist in map; ctor throws + error if unhappy + +## 0.9.5 (2011-07-05) + +### Lua 5.2 compatibility + + - defines Lua 5.2 beta compatible load() + - defines table.pack() + +### New functions + + - stringx.title(): translates "a dog's day" to "A Dog's Day" + - path.normpath(): translates 'A//B','A/./B' and 'A/C/../B' to 'A/B' + - utils.execute(): returns ok,return-code: compatible with 5.1 and 5.2 + +### Fixes + + - pretty.write() _always_ returns a string, but will return also an error string +if the argument is not a table. Non-integer indices between 1 and #t are no longer falsely considered part of the array + - stringx.expandtabs() now works like the Python string method; it will expand each field up to the next tab stop + - path.normcase() was broken, because of a misguided attempt to normalize the path. + - UNC specific fix to path.abspath() + - UNC paths recognized as absolute; dir.makedir() works here + - utils.quit() varargs broken, e.g. utils.quit("answer was %d",42) + - some stray globals caused trouble with 'strict' + +## 0.9.4 (2011-04-08) + +## 0.9.3 (2011-03-05) + +## 0.9.2 (2011-02-16) + +## 0.9.1 (2011-02-12) + +## 0.9.0 (2010-12-20) + +## 0.8.5 (2010-12-16) + +### What's new with 0.8b ? + +#### Features: + +pl.app provides useful stuff like simple command-line argument parsing and require_here(), which +makes subsequent require() calls look in the local directory by preference. + +p.file provides useful functions like copy(),move(), read() and write(). (These are aliases to +dir.copyfile(),movefile(),utils.readfile(),writefile()) + +Custom error trace will only show the functions in user code. + +More robust argument checking. + +In function arguments, now supports 'string lambdas', e.g. `'|x| 2*x'` + +utils.readfile,writefile now insist on being given filenames. This will cause less confusion. + +tablex.search() is new: will look recursively in an arbitrary table; can specify tables not to follow. +tablex.move() will work with source and destination tables the same, with overlapping ranges. + +#### Bug Fixes: + +dir.copyfile() now works fine without Alien on Windows + +dir.makepath() and rmtree() had problems. + +tablex.compare_no_order() is now O(NlogN), as expected. +tablex.move() had a problem with source size + +### What's New with 0.7.0b? + +#### Features: + +utils.is_type(v,tp) can say is_type(s,'string') and is_type(l,List). +utils.is_callable(v) either a function, or has a `__call` metamethod. + +Sequence wrappers: can write things like this: + +seq(s):last():filter('<'):copy() + +seq:mapmethod(s,name) - map using a named method over a sequence. + +seq:enum(s) If s is a simple sequence, then + for i,v in seq.enum(s) do print(i,v) end + +seq:take(s,n) Grab the next n values from a (possibly infinite) +sequence. + +In a related change suggested by Flemming Madsden, the in-place List +methods like reverse() and sort() return the list, allowing for +method chaining. + +list.join() explicitly converts using tostring first. + +tablex.count_map() like seq.count_map(), but takes an equality function. + +tablex.difference() set difference +tablex.set() explicit set generator given a list of values + +Template.indent_substitute() is a new Template method which adjusts +for indentation and can also substitute templates themselves. + +pretty.read(). This reads a Lua table (as dumped by pretty.write) +and attempts to be paranoid about its contents. + +sip.match_at_start(). Convenience function for anchored SIP matches. + +#### Bug Fixes: + +tablex.deepcompare() was confused by false boolean values, which +it thought were synonymous with being nil. + +pretty.write() did not handle cycles, and could not display tables +with 'holes' properly (Flemming Madsden) + +The SIP pattern '$(' was not escaped properly. +sip.match() did not pass on options table. + +seq.map() was broken for double-valued sequences. +seq.copy_tuples() did not use default_iter(), so did not e.g. like +table arguments. + +dir.copyfile() returns the wrong result for \*nix operations. +dir.makepath() was broken for non-Windows paths. + +### What's New with 0.6.3? + +The map and reduce functions now take the function first, as Nature intended. + +The Python-like overloading of '\*' for strings has been dropped, since it +is silly. Also, strings are no longer callable; use 's:at(1)' instead of +'s(1)' - this tended to cause Obscure Error messages. + +Wherever a function argument is expected, you can use the operator strings +like '+','==',etc as well as pl.operator.add, pl.operator.eq, etc. +(see end of pl/operator.lua for the full list.) + +tablex now has compare() and compare_no_order(). An explicit set() +function has been added which constructs a table with the specified +keys, all set to a value of true. + +List has reduce() and partition() (This is a cool function which +separates out elements of a list depending on a classifier function.) + +There is a new array module which generalizes tablex operations like +map and reduce for two-dimensional arrays. + +The famous iterator over permutations from PiL 9.3 has been included. + +David Manura's list comprehension library has been included. + +Also, utils now contains his memoize function, plus a useful function +args which captures the case where varargs contains nils. + +There was a bug with dir.copyfile() where the flag was the wrong way round. + +config.lines() had a problem with continued lines. + +Some operators were missing in pl.operator; have renamed them to be +consistent with the Lua metamethod names. + + diff --git a/Data/Libraries/Penlight/CONTRIBUTING.md b/Data/Libraries/Penlight/CONTRIBUTING.md new file mode 100644 index 0000000..74cde11 --- /dev/null +++ b/Data/Libraries/Penlight/CONTRIBUTING.md @@ -0,0 +1,60 @@ +Contributing to Penlight +======================== + +So you want to contribute to Penlight? Fantastic! Here's a brief overview on +how best to do so. + +## What to change + +Here's some examples of things you might want to make a pull request for: + +* New features +* Bugfixes +* Inefficient blocks of code + +If you have a more deeply-rooted problem with how the library is built or some +of the stylistic decisions made in the code, it's best to +[create an issue](https://github.com/lunarmodules/Penlight/issues) before putting +the effort into a pull request. The same goes for new features - it might be +best to check the project's direction, existing pull requests, and currently open +and closed issues first. + +## Using Git appropriately + +Here's how to go about contributing to Penlight: + +1. [Fork the repository](https://github.com/lunarmodules/Penlight/fork) to +your Github account. +2. Create a *topical branch* - a branch whose name is succint but explains what +you're doing, such as _"added-klingon-cloacking-device"_ - from `master` branch. +3. Make your changes, committing at logical breaks. +4. Push your branch to your personal account +5. [Create a pull request](https://help.github.com/articles/using-pull-requests) +6. Watch for comments or acceptance + +If you wanna be a rockstar; + +1. Update the [CHANGELOG.md](https://github.com/lunarmodules/Penlight/blob/master/CHANGELOG.md) file +2. [Add tests](https://github.com/lunarmodules/Penlight/tree/master/tests) that show the defect your fix repairs, or that tests your new feature + +Please note - if you want to change multiple things that don't depend on each +other, make sure you check out the `master` branch again and create a different topical branch +before making more changes - that way we can take in each change separately. + +## Release instructions for a new version + + - create a new release branch + - update `./lua/pl/utils.lua` (the `_VERSION` constant) + - update `./config.ld` with the new version number + - create a new rockspec file for the version in `./rockspecs` + - check the `./CHANGELOG.md` files for completeness + - commit the release related changes with `release x.y.z` + - render the documentation using `ldoc .` + - commit the documentation as a separate commit with `release x.y.z docs` + - push the release branch and create a PR + - merge the PR + - tag the release as `x.y.z` and push the tag to the github repo + - upload the rockspec, and source rock files to LuaRocks + - test installing through LuaRocks + - announce the release on the Lua mailing list + diff --git a/Data/Libraries/Penlight/LICENSE.md b/Data/Libraries/Penlight/LICENSE.md new file mode 100644 index 0000000..8acf619 --- /dev/null +++ b/Data/Libraries/Penlight/LICENSE.md @@ -0,0 +1,21 @@ +Copyright (C) 2009-2016 Steve Donovan, David Manura. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Data/Libraries/Penlight/README.md b/Data/Libraries/Penlight/README.md new file mode 100644 index 0000000..91699d0 --- /dev/null +++ b/Data/Libraries/Penlight/README.md @@ -0,0 +1,127 @@ +# Penlight Lua Libraries + +[](https://travis-ci.org/lunarmodules/Penlight) +[](https://ci.appveyor.com/project/Tieske/penlight-ta1gi/branch/master) +[](https://coveralls.io/github/lunarmodules/Penlight) +[](https://github.com/lunarmodules/Penlight/actions) +[](CHANGELOG.md) + +## Why a new set of libraries? + +Penlight brings together a set of generally useful pure Lua modules, +focusing on input data handling (such as reading configuration files), +functional programming (such as map, reduce, placeholder expressions, etc), +and OS path management. Much of the functionality is inspired by the +Python standard libraries. + +## Module Overview + +### Paths, Files and Directories + + * `path`: queries like `isdir`,`isfile`,`exists`, splitting paths like `dirname` and `basename` + * `dir`: listing files in directories (`getfiles`,`getallfiles`) and creating/removing directory paths + * `file`: `copy`,`move`; read/write contents with `read` and `write` + +### Application Support + + * `app`: `require_here` to rebase `require` to work with main script path; simple argument parsing `parse_args` + * `lapp`: sophisticated usage-text-driven argument parsing for applications + * `config`: flexibly read Unix config files and Windows INI files + * `strict`: check for undefined global variables - can use `strict.module` for modules + * `utils`,`compat`: Penlight support for unified Lua 5.1/5.2 codebases + * `types`: predicates like `is_callable` and `is_integer`; extended `type` function. + +### Extra String Operations + + * `utils`: can split a string with a delimiter using `utils.split` + * `stringx`: extended string functions covering the Python `string` type + * `stringio`: open strings for reading, and creating strings using standard Lua IO methods + * `lexer`: lexical scanner for splitting text into tokens; special cases for Lua and C + * `text`: indenting and dedenting text, wrapping paragraphs; optionally make `%` work as in Python + * `template`: small but powerful template expansion engine + * `sip`: Simple Input Patterns - higher-level string patterns for parsing text + +### Extra Table Operations + + * `tablex`: copying, comparing and mapping over + * `pretty`: pretty-printing Lua tables, and various safe ways to load Lua as data + * `List`: implementation of Python 'list' type - slices, concatenation and partitioning + * `Map`, `Set`, `OrderedMap`: classes for specialized kinds of tables + * `data`: reading tabular data into 2D arrays and efficient queries + * `array2d`: operations on 2D arrays + * `permute`: generate permutations + +### Iterators, OOP and Functional + + * `seq`: working with iterator pipelines; collecting iterators as tables + * `class`: a simple reusable class framework + * `func`: symbolic manipulation of expressions and lambda expressions + * `utils`: `utils.string_lambda` converts short strings like '|x| x^2' into functions + * `comprehension`: list comprehensions: `C'x for x=1,4'()=={1,2,3,4}` + +## Versioning + +Penlight is strictly versioned according to [Semantic Versioning](https://semver.org/). + +In scope of the version: + * functionality provided by Penlight modules/classes + * based on stock Lua PuC-Rio or LuaJIT + +Not in scope of the version: + * Documentation + * Error messages (textual changes) + * Deprecation warnings (by default to `stderr`) + +### Deprecating functionality + +Any version may deprecate functionality. So new deprecation notices may appear +in major, minor, and patch releases. Final removal of functionality (assuming it +is a breaking change) will only be done in a major version. + +It is strongly suggested to use the deprecation warning mechanism to test usage +of deprecated functionalities when upgrading. This is done by enabling the +warning system (in Lua 5.4, or the Penlight compatibility function for earlier +versions): + +```lua +require "pl.compat" +warn "@on" +``` + +See `pl.utils.raise_deprecation` for more info. + +## License + +Penlight is distributed under the [MIT license](LICENSE.md). + +## Installation + +Using [LuaRocks](https://luarocks.org): simply run `luarocks install penlight`. + +Manually: copy `lua/pl` directory into your Lua module path. It's typically +`/usr/local/share/lua/5.x` on a Linux system and `C:\Program Files\Lua\5.x\lua` +for Lua for Windows. + +## Dependencies + +The file and directory functions depend on [LuaFileSystem](https://keplerproject.github.io/luafilesystem/), +which is installed automatically if you are using LuaRocks. Additionally, if you want `dir.copyfile` to work +elegantly on Windows, then you need [Alien](http://mascarenhas.github.io/alien/). Both libraries are present +in Lua for Windows. + +## Building the Documentation + +Requires [ldoc](https://github.com/stevedonovan/LDoc), which is available +through LuaRocks. Then it's a simple matter of running `ldoc .` from the repo. + +## Contributing + +Contributions are most welcome, please check the [contribution guidelines](CONTRIBUTING.md). + +## Running tests + +Execute `lua run.lua tests` to run the tests. Execute `lua run.lua examples` to run examples. + +## History + +For a complete history of the development of Penlight, please check the [changelog](CHANGELOG.md). diff --git a/Data/Libraries/Penlight/appveyor.yml b/Data/Libraries/Penlight/appveyor.yml new file mode 100644 index 0000000..6b6a855 --- /dev/null +++ b/Data/Libraries/Penlight/appveyor.yml @@ -0,0 +1,36 @@ +shallow_clone: true + +environment: + COVERALLS_REPO_TOKEN: + secure: /0zDn4cz8xWKQuHfzvAUwyeFSQfLSP+toDR3lRApwWoIeg9O1OvJTnXurB8cssW0 + matrix: + - LUA: "lua 5.1" + - LUA: "lua 5.2" + - LUA: "lua 5.3" + - LUA: "lua 5.4" + - LUA: "luajit 2.0" + - LUA: "luajit 2.0 --compat 5.2" + - LUA: "luajit 2.1" + - LUA: "luajit 2.1 --compat 5.2" + +before_build: + - set PATH=C:\Python27\Scripts;%PATH% + - pip install hererocks + - hererocks here --%LUA% -rlatest + - call here\bin\activate + - luarocks install luacov-coveralls + - luarocks install busted + +build_script: + - luarocks make + +test_script: + - busted -c -v + - lua run.lua tests --luacov + - lua run.lua examples + +on_success: + # secure coveralls token not available on PR builds, only BRANCH builds + - "if not \"%COVERALLS_REPO_TOKEN%\"==\"\" ( + luacov-coveralls + )" diff --git a/Data/Libraries/Penlight/config.ld b/Data/Libraries/Penlight/config.ld new file mode 100644 index 0000000..07c4772 --- /dev/null +++ b/Data/Libraries/Penlight/config.ld @@ -0,0 +1,18 @@ +project = 'Penlight' +description = 'Penlight Lua Libraries 1.11.0' +full_description = 'Penlight is a set of pure Lua libraries for making it easier to work with common tasks like iterating over directories, reading configuration files and the like. Provides functional operations on tables and sequences. Visit the <a href="https://github.com/lunarmodules/Penlight">GitHub project</a> to review the code or file issues. Skip to the @{01-introduction.md|introduction}.' +title = 'Penlight Documentation' +dir = 'docs' +style = '!fixed' +template = true +use_markdown_titles = true +topics = 'docs_topics' +examples = {'./examples','./tests/test-data.lua'} +package = 'pl' +format = 'discount' +sort_modules=true +file = './lua/pl' +kind_names={topic='Manual',module='Libraries'} +tparam_alias('array','array') +tparam_alias('array2d','array') +alias('ret',{'return',modifiers={type="$1"}}) diff --git a/Data/Libraries/Penlight/docs/classes/pl.Date.html b/Data/Libraries/Penlight/docs/classes/pl.Date.html new file mode 100644 index 0000000..47d3d65 --- /dev/null +++ b/Data/Libraries/Penlight/docs/classes/pl.Date.html @@ -0,0 +1,1049 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +<li><a href="#Methods">Methods</a></li> +</ul> + + +<h2>Classes</h2> +<ul class="nowrap"> + <li><strong>pl.Date</strong></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Class <code>pl.Date</code></h1> +<p>Date and Date Format classes.</p> +<p> See <a href="../manual/05-dates.md.html#">the Guide</a>.</p> + +<p> NOTE: the date module is deprecated! see + https://github.com/lunarmodules/Penlight/issues/285</p> + +<p> Dependencies: <a href="../libraries/pl.class.html#">pl.class</a>, <a href="../libraries/pl.stringx.html#">pl.stringx</a>, <a href="../libraries/pl.utils.html#">pl.utils</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#Date:set">Date:set (t)</a></td> + <td class="summary">set the current time of this Date object.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date.tzone">Date.tzone (ts)</a></td> + <td class="summary">get the time zone offset from UTC.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:toUTC">Date:toUTC ()</a></td> + <td class="summary">convert this date to UTC.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:toLocal">Date:toLocal ()</a></td> + <td class="summary">convert this UTC date to local.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:year">Date:year (y)</a></td> + <td class="summary">set the year.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:month">Date:month (m)</a></td> + <td class="summary">set the month.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:day">Date:day (d)</a></td> + <td class="summary">set the day.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:hour">Date:hour (h)</a></td> + <td class="summary">set the hour.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:min">Date:min (min)</a></td> + <td class="summary">set the minutes.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:sec">Date:sec (sec)</a></td> + <td class="summary">set the seconds.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:yday">Date:yday (yday)</a></td> + <td class="summary">set the day of year.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:year">Date:year (y)</a></td> + <td class="summary">get the year.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:month">Date:month ()</a></td> + <td class="summary">get the month.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:day">Date:day ()</a></td> + <td class="summary">get the day.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:hour">Date:hour ()</a></td> + <td class="summary">get the hour.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:min">Date:min ()</a></td> + <td class="summary">get the minutes.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:sec">Date:sec ()</a></td> + <td class="summary">get the seconds.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:yday">Date:yday ()</a></td> + <td class="summary">get the day of year.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:weekday_name">Date:weekday_name (full)</a></td> + <td class="summary">name of day of week.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:month_name">Date:month_name (full)</a></td> + <td class="summary">name of month.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:is_weekend">Date:is_weekend ()</a></td> + <td class="summary">is this day on a weekend?.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:add">Date:add (t)</a></td> + <td class="summary">add to a date object.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:last_day">Date:last_day ()</a></td> + <td class="summary">last day of the month.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:diff">Date:diff (other)</a></td> + <td class="summary">difference between two Date objects.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:__tostring">Date:__tostring ()</a></td> + <td class="summary">long numerical ISO data format version of this date.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:__eq">Date:__eq (other)</a></td> + <td class="summary">equality between Date objects.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:__lt">Date:__lt (other)</a></td> + <td class="summary">ordering between Date objects.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:__sub">Date:__sub ()</a></td> + <td class="summary">difference between Date objects.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date:__add">Date:__add (other)</a></td> + <td class="summary">add a date and an interval.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date.Interval">Date.Interval (t)</a></td> + <td class="summary">Date.Interval constructor</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date.Interval:__tostring">Date.Interval:__tostring ()</a></td> + <td class="summary">If it's an interval then the format is '2 hours 29 sec' etc.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date.Format">Date.Format (fmt.)</a></td> + <td class="summary">Date.Format constructor.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date.Format:parse">Date.Format:parse (str)</a></td> + <td class="summary">parse a string into a Date object.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date.Format:tostring">Date.Format:tostring (d)</a></td> + <td class="summary">convert a Date object into a string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Date.Format:US_order">Date.Format:US_order (yesno)</a></td> + <td class="summary">force US order in dates like 9/11/2001</td> + </tr> +</table> +<h2><a href="#Methods">Methods</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#pl.Date:Date">pl.Date:Date (t, ...)</a></td> + <td class="summary">Date constructor.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "Date:set"></a> + <strong>Date:set (t)</strong> + </dt> + <dd> + set the current time of this Date object. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">int</span></span> + seconds since epoch + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date.tzone"></a> + <strong>Date.tzone (ts)</strong> + </dt> + <dd> + get the time zone offset from UTC. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">ts</span> + <span class="types"><span class="type">int</span></span> + seconds ahead of UTC + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date:toUTC"></a> + <strong>Date:toUTC ()</strong> + </dt> + <dd> + convert this date to UTC. + + + + + + + +</dd> + <dt> + <a name = "Date:toLocal"></a> + <strong>Date:toLocal ()</strong> + </dt> + <dd> + convert this UTC date to local. + + + + + + + +</dd> + <dt> + <a name = "Date:year"></a> + <strong>Date:year (y)</strong> + </dt> + <dd> + set the year. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">y</span> + <span class="types"><span class="type">int</span></span> + Four-digit year + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date:month"></a> + <strong>Date:month (m)</strong> + </dt> + <dd> + set the month. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">m</span> + <span class="types"><span class="type">int</span></span> + month + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date:day"></a> + <strong>Date:day (d)</strong> + </dt> + <dd> + set the day. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">d</span> + <span class="types"><span class="type">int</span></span> + day + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date:hour"></a> + <strong>Date:hour (h)</strong> + </dt> + <dd> + set the hour. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">h</span> + <span class="types"><span class="type">int</span></span> + hour + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date:min"></a> + <strong>Date:min (min)</strong> + </dt> + <dd> + set the minutes. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">min</span> + <span class="types"><span class="type">int</span></span> + minutes + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date:sec"></a> + <strong>Date:sec (sec)</strong> + </dt> + <dd> + set the seconds. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">sec</span> + <span class="types"><span class="type">int</span></span> + seconds + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date:yday"></a> + <strong>Date:yday (yday)</strong> + </dt> + <dd> + set the day of year. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">yday</span> + <span class="types"><span class="type">int</span></span> + day of year + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date:year"></a> + <strong>Date:year (y)</strong> + </dt> + <dd> + get the year. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">y</span> + <span class="types"><span class="type">int</span></span> + Four-digit year + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date:month"></a> + <strong>Date:month ()</strong> + </dt> + <dd> + get the month. + + + + + + + +</dd> + <dt> + <a name = "Date:day"></a> + <strong>Date:day ()</strong> + </dt> + <dd> + get the day. + + + + + + + +</dd> + <dt> + <a name = "Date:hour"></a> + <strong>Date:hour ()</strong> + </dt> + <dd> + get the hour. + + + + + + + +</dd> + <dt> + <a name = "Date:min"></a> + <strong>Date:min ()</strong> + </dt> + <dd> + get the minutes. + + + + + + + +</dd> + <dt> + <a name = "Date:sec"></a> + <strong>Date:sec ()</strong> + </dt> + <dd> + get the seconds. + + + + + + + +</dd> + <dt> + <a name = "Date:yday"></a> + <strong>Date:yday ()</strong> + </dt> + <dd> + get the day of year. + + + + + + + +</dd> + <dt> + <a name = "Date:weekday_name"></a> + <strong>Date:weekday_name (full)</strong> + </dt> + <dd> + name of day of week. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">full</span> + <span class="types"><span class="type">bool</span></span> + abbreviated if true, full otherwise. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + name + </ol> + + + + +</dd> + <dt> + <a name = "Date:month_name"></a> + <strong>Date:month_name (full)</strong> + </dt> + <dd> + name of month. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">full</span> + <span class="types"><span class="type">int</span></span> + abbreviated if true, full otherwise. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + name + </ol> + + + + +</dd> + <dt> + <a name = "Date:is_weekend"></a> + <strong>Date:is_weekend ()</strong> + </dt> + <dd> + is this day on a weekend?. + + + + + + + +</dd> + <dt> + <a name = "Date:add"></a> + <strong>Date:add (t)</strong> + </dt> + <dd> + add to a date object. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + a table containing one of the following keys and a value: + one of <a href="../classes/pl.Date.html#Date:year">year</a>,<a href="../classes/pl.Date.html#Date:month">month</a>,<a href="../classes/pl.Date.html#Date:day">day</a>,<a href="../classes/pl.Date.html#Date:hour">hour</a>,<a href="../classes/pl.Date.html#Date:min">min</a>,<a href="../classes/pl.Date.html#Date:sec">sec</a> + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + this date + </ol> + + + + +</dd> + <dt> + <a name = "Date:last_day"></a> + <strong>Date:last_day ()</strong> + </dt> + <dd> + last day of the month. + + + + <h3>Returns:</h3> + <ol> + + int day + </ol> + + + + +</dd> + <dt> + <a name = "Date:diff"></a> + <strong>Date:diff (other)</strong> + </dt> + <dd> + difference between two Date objects. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">other</span> + <span class="types"><a class="type" href="../classes/pl.Date.html">Date</a></span> + Date object + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="../classes/pl.Date.html#Date.Interval">Date.Interval</a></span> + object + </ol> + + + + +</dd> + <dt> + <a name = "Date:__tostring"></a> + <strong>Date:__tostring ()</strong> + </dt> + <dd> + long numerical ISO data format version of this date. + + + + + + + +</dd> + <dt> + <a name = "Date:__eq"></a> + <strong>Date:__eq (other)</strong> + </dt> + <dd> + equality between Date objects. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">other</span> + + + + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date:__lt"></a> + <strong>Date:__lt (other)</strong> + </dt> + <dd> + ordering between Date objects. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">other</span> + + + + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date:__sub"></a> + <strong>Date:__sub ()</strong> + </dt> + <dd> + difference between Date objects. + + + + + + + +</dd> + <dt> + <a name = "Date:__add"></a> + <strong>Date:__add (other)</strong> + </dt> + <dd> + add a date and an interval. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">other</span> + either a <a href="../classes/pl.Date.html#Date.Interval">Date.Interval</a> object or a table such as + passed to <a href="../classes/pl.Date.html#Date:add">Date:add</a> + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date.Interval"></a> + <strong>Date.Interval (t)</strong> + </dt> + <dd> + Date.Interval constructor + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">int</span></span> + an interval in seconds + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Date.Interval:__tostring"></a> + <strong>Date.Interval:__tostring ()</strong> + </dt> + <dd> + If it's an interval then the format is '2 hours 29 sec' etc. + + + + + + + +</dd> + <dt> + <a name = "Date.Format"></a> + <strong>Date.Format (fmt.)</strong> + </dt> + <dd> + Date.Format constructor. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fmt.</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A string where the following fields are significant:</p> + +<ul> + <li>d day (either d or dd)</li> + <li>y year (either yy or yyy)</li> + <li>m month (either m or mm)</li> + <li>H hour (either H or HH)</li> + <li>M minute (either M or MM)</li> + <li>S second (either S or SS)</li> +</ul> + +<p> Alternatively, if fmt is nil then this returns a flexible date parser + that tries various date/time schemes in turn:</p> + +<ul> + <li><a href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>, like <code>2010-05-10 12:35:23Z</code> or <code>2008-10-03T14:30+02</code></li> + <li>times like 15:30 or 8.05pm (assumed to be today's date)</li> + <li>dates like 28/10/02 (European order!) or 5 Feb 2012</li> + <li>month name like march or Mar (case-insensitive, first 3 letters); here the + day will be 1 and the year this current year</li> +</ul> + +<p> A date in format 3 can be optionally followed by a time in format 2. + Please see test-date.lua in the tests folder for more examples. + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">df = Date.Format(<span class="string">"yyyy-mm-dd HH:MM:SS"</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "Date.Format:parse"></a> + <strong>Date.Format:parse (str)</strong> + </dt> + <dd> + parse a string into a Date object. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">str</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a date string + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + date object + </ol> + + + + +</dd> + <dt> + <a name = "Date.Format:tostring"></a> + <strong>Date.Format:tostring (d)</strong> + </dt> + <dd> + convert a Date object into a string. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">d</span> + a date object, or a time value as returned by <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.time">os.time</a> + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + string + </ol> + + + + +</dd> + <dt> + <a name = "Date.Format:US_order"></a> + <strong>Date.Format:US_order (yesno)</strong> + </dt> + <dd> + force US order in dates like 9/11/2001 + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">yesno</span> + + + + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Methods"></a>Methods</h2> + + <dl class="function"> + <dt> + <a name = "pl.Date:Date"></a> + <strong>pl.Date:Date (t, ...)</strong> + </dt> + <dd> + Date constructor. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + +<p> this can be either</p> + +<ul> + <li><code>nil</code> or empty - use current date and time</li> + <li>number - seconds since epoch (as returned by <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.time">os.time</a>). Resulting time is UTC</li> + <li><a href="../classes/pl.Date.html">Date</a> - make a copy of this date</li> + <li>table - table containing year, month, etc as for <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.time">os.time</a>. You may leave out year, month or day, + in which case current values will be used.</li> + <li>year (will be followed by month, day etc)</li> +</ul> + + + </li> + <li><span class="parameter">...</span> + true if Universal Coordinated Time, or two to five numbers: month,day,hour,min,sec + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/classes/pl.List.html b/Data/Libraries/Penlight/docs/classes/pl.List.html new file mode 100644 index 0000000..b84209f --- /dev/null +++ b/Data/Libraries/Penlight/docs/classes/pl.List.html @@ -0,0 +1,1443 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +<li><a href="#metamethods">metamethods</a></li> +</ul> + + +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><strong>pl.List</strong></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Class <code>pl.List</code></h1> +<p>Python-style list class.</p> +<p> <strong>Please Note</strong>: methods that change the list will return the list. + This is to allow for method chaining, but please note that <code>ls = ls:sort()</code> + does not mean that a new copy of the list is made. In-place (mutable) methods + are marked as returning 'the list' in this documentation.</p> + +<p> See the Guide for further <a href="../manual/02-arrays.md.html#Python_style_Lists">discussion</a></p> + +<p> See <a href="http://www.python.org/doc/current/tut/tut.html">http://www.python.org/doc/current/tut/tut.html</a>, section 5.1</p> + +<p> <strong>Note</strong>: The comments before some of the functions are from the Python docs + and contain Python code.</p> + +<p> Written for Lua version Nick Trout 4.0; Redone for Lua 5.1, Steve Donovan.</p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.tablex.html#">pl.tablex</a>, <a href="../libraries/pl.class.html#">pl.class</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#List.new">List.new ([t])</a></td> + <td class="summary">Create a new list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:clone">List:clone ()</a></td> + <td class="summary">Make a copy of an existing list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:append">List:append (i)</a></td> + <td class="summary">Add an item to the end of the list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:extend">List:extend (L)</a></td> + <td class="summary">Extend the list by appending all the items in the given list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:insert">List:insert (i, x)</a></td> + <td class="summary">Insert an item at a given position.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:put">List:put (x)</a></td> + <td class="summary">Insert an item at the begining of the list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:remove">List:remove (i)</a></td> + <td class="summary">Remove an element given its index.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:remove_value">List:remove_value (x)</a></td> + <td class="summary">Remove the first item from the list whose value is given.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:pop">List:pop ([i])</a></td> + <td class="summary">Remove the item at the given position in the list, and return it.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:index">List:index (x[, idx=1])</a></td> + <td class="summary">Return the index in the list of the first item whose value is given.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:contains">List:contains (x)</a></td> + <td class="summary">Does this list contain the value?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:count">List:count (x)</a></td> + <td class="summary">Return the number of times value appears in the list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:sort">List:sort ([cmp='<'])</a></td> + <td class="summary">Sort the items of the list, in place.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:sorted">List:sorted ([cmp='<'])</a></td> + <td class="summary">Return a sorted copy of this list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:reverse">List:reverse ()</a></td> + <td class="summary">Reverse the elements of the list, in place.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:minmax">List:minmax ()</a></td> + <td class="summary">Return the minimum and the maximum value of the list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:slice">List:slice (first, last)</a></td> + <td class="summary">Emulate list slicing.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:clear">List:clear ()</a></td> + <td class="summary">Empty the list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List.range">List.range (start[, finish[, incr=1]])</a></td> + <td class="summary">Emulate Python's range(x) function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:len">List:len ()</a></td> + <td class="summary">list:len() is the same as #list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:chop">List:chop (i1, i2)</a></td> + <td class="summary">Remove a subrange of elements.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:splice">List:splice (idx, list)</a></td> + <td class="summary">Insert a sublist into a list + equivalent to 's[idx:idx] = list' in Python</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:slice_assign">List:slice_assign (i1, i2, seq)</a></td> + <td class="summary">General slice assignment s[i1:i2] = seq.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:join">List:join ([delim=''])</a></td> + <td class="summary">Join the elements of a list using a delimiter.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:concat">List:concat ([delim=''])</a></td> + <td class="summary">Join a list of strings.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:foreach">List:foreach (fun, ...)</a></td> + <td class="summary">Call the function on each element of the list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:foreachm">List:foreachm (name, ...)</a></td> + <td class="summary">Call the named method on each element of the list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:filter">List:filter (fun[, arg])</a></td> + <td class="summary">Create a list of all elements which match a function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List.split">List.split (s[, delim])</a></td> + <td class="summary">Split a string using a delimiter.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:map">List:map (fun, ...)</a></td> + <td class="summary">Apply a function to all elements.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:transform">List:transform (fun, ...)</a></td> + <td class="summary">Apply a function to all elements, in-place.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:map2">List:map2 (fun, ls, ...)</a></td> + <td class="summary">Apply a function to elements of two lists.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:mapm">List:mapm (name, ...)</a></td> + <td class="summary">apply a named method to all elements.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:reduce">List:reduce (fun)</a></td> + <td class="summary">'reduce' a list using a binary function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:partition">List:partition (fun, ...)</a></td> + <td class="summary">Partition a list using a classifier function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:iter">List:iter ()</a></td> + <td class="summary">return an iterator over all values.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List.iterate">List.iterate (seq)</a></td> + <td class="summary">Create an iterator over a seqence.</td> + </tr> +</table> +<h2><a href="#metamethods">metamethods</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#List:__concat">List:__concat (L)</a></td> + <td class="summary">Concatenation operator.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:__eq">List:__eq (L)</a></td> + <td class="summary">Equality operator ==.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#List:__tostring">List:__tostring ()</a></td> + <td class="summary">How our list should be rendered as a string.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "List.new"></a> + <strong>List.new ([t])</strong> + </dt> + <dd> + Create a new list. Can optionally pass a table; + passing another instance of List will cause a copy to be created; + this will return a plain table with an appropriate metatable. + we pass anything which isn't a simple table to iterate() to work out + an appropriate iterator + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + An optional list-like table + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new List + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../classes/pl.List.html#List.iterate">List.iterate</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example">ls = List(); ls = List {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}</pre> + </ul> + +</dd> + <dt> + <a name = "List:clone"></a> + <strong>List:clone ()</strong> + </dt> + <dd> + Make a copy of an existing list. + The difference from a plain 'copy constructor' is that this returns + the actual List subtype. + + + + + + + +</dd> + <dt> + <a name = "List:append"></a> + <strong>List:append (i)</strong> + </dt> + <dd> + Add an item to the end of the list. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">i</span> + An item + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + +</dd> + <dt> + <a name = "List:extend"></a> + <strong>List:extend (L)</strong> + </dt> + <dd> + Extend the list by appending all the items in the given list. + equivalent to 'a[len(a):] = L'. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">L</span> + <span class="types"><a class="type" href="../classes/pl.List.html">List</a></span> + Another List + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + +</dd> + <dt> + <a name = "List:insert"></a> + <strong>List:insert (i, x)</strong> + </dt> + <dd> + Insert an item at a given position. i is the index of the + element before which to insert. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">i</span> + <span class="types"><span class="type">int</span></span> + index of element before whichh to insert + </li> + <li><span class="parameter">x</span> + A data item + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + +</dd> + <dt> + <a name = "List:put"></a> + <strong>List:put (x)</strong> + </dt> + <dd> + Insert an item at the begining of the list. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">x</span> + a data item + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + +</dd> + <dt> + <a name = "List:remove"></a> + <strong>List:remove (i)</strong> + </dt> + <dd> + Remove an element given its index. + (equivalent of Python's del s[i]) + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">i</span> + <span class="types"><span class="type">int</span></span> + the index + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + +</dd> + <dt> + <a name = "List:remove_value"></a> + <strong>List:remove_value (x)</strong> + </dt> + <dd> + Remove the first item from the list whose value is given. + (This is called 'remove' in Python; renamed to avoid confusion + with table.remove) + Return nil if there is no such item. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">x</span> + A data value + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + +</dd> + <dt> + <a name = "List:pop"></a> + <strong>List:pop ([i])</strong> + </dt> + <dd> + Remove the item at the given position in the list, and return it. + If no index is specified, a:pop() returns the last item in the list. + The item is also removed from the list. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">i</span> + <span class="types"><span class="type">int</span></span> + An index + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the item + </ol> + + + + +</dd> + <dt> + <a name = "List:index"></a> + <strong>List:index (x[, idx=1])</strong> + </dt> + <dd> + Return the index in the list of the first item whose value is given. + Return nil if there is no such item. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">x</span> + A data value + </li> + <li><span class="parameter">idx</span> + <span class="types"><span class="type">int</span></span> + where to start search + (<em>default</em> 1) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the index, or nil if not found. + </ol> + + + + +</dd> + <dt> + <a name = "List:contains"></a> + <strong>List:contains (x)</strong> + </dt> + <dd> + Does this list contain the value? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">x</span> + A data value + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + +</dd> + <dt> + <a name = "List:count"></a> + <strong>List:count (x)</strong> + </dt> + <dd> + Return the number of times value appears in the list. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">x</span> + A data value + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + number of times x appears + </ol> + + + + +</dd> + <dt> + <a name = "List:sort"></a> + <strong>List:sort ([cmp='<'])</strong> + </dt> + <dd> + Sort the items of the list, in place. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">cmp</span> + <span class="types"><span class="type">func</span></span> + an optional comparison function + (<em>default</em> '<') + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + +</dd> + <dt> + <a name = "List:sorted"></a> + <strong>List:sorted ([cmp='<'])</strong> + </dt> + <dd> + Return a sorted copy of this list. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">cmp</span> + <span class="types"><span class="type">func</span></span> + an optional comparison function + (<em>default</em> '<') + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new list + </ol> + + + + +</dd> + <dt> + <a name = "List:reverse"></a> + <strong>List:reverse ()</strong> + </dt> + <dd> + Reverse the elements of the list, in place. + + + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + +</dd> + <dt> + <a name = "List:minmax"></a> + <strong>List:minmax ()</strong> + </dt> + <dd> + Return the minimum and the maximum value of the list. + + + + <h3>Returns:</h3> + <ol> + <li> + minimum value</li> + <li> + maximum value</li> + </ol> + + + + +</dd> + <dt> + <a name = "List:slice"></a> + <strong>List:slice (first, last)</strong> + </dt> + <dd> + Emulate list slicing. like 'list[first:last]' in Python. + If first or last are negative then they are relative to the end of the list + eg. slice(-2) gives last 2 entries in a list, and + slice(-4,-2) gives from -4th to -2nd + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">first</span> + An index + </li> + <li><span class="parameter">last</span> + An index + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new List + </ol> + + + + +</dd> + <dt> + <a name = "List:clear"></a> + <strong>List:clear ()</strong> + </dt> + <dd> + Empty the list. + + + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + +</dd> + <dt> + <a name = "List.range"></a> + <strong>List.range (start[, finish[, incr=1]])</strong> + </dt> + <dd> + Emulate Python's range(x) function. + Include it in List table for tidiness + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">start</span> + <span class="types"><span class="type">int</span></span> + A number + </li> + <li><span class="parameter">finish</span> + <span class="types"><span class="type">int</span></span> + A number greater than start; if absent, + then start is 1 and finish is start + (<em>optional</em>) + </li> + <li><span class="parameter">incr</span> + <span class="types"><span class="type">int</span></span> + an increment (may be less than 1) + (<em>default</em> 1) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a List from start .. finish + </ol> + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">List.range(<span class="number">0</span>,<span class="number">3</span>) == List{<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}</pre></li> + <li><pre class="example">List.range(<span class="number">4</span>) = List{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}</pre></li> + <li><pre class="example">List.range(<span class="number">5</span>,<span class="number">1</span>,-<span class="number">1</span>) == List{<span class="number">5</span>,<span class="number">4</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">1</span>}</pre></li> + </ul> + +</dd> + <dt> + <a name = "List:len"></a> + <strong>List:len ()</strong> + </dt> + <dd> + list:len() is the same as #list. + + + + + + + +</dd> + <dt> + <a name = "List:chop"></a> + <strong>List:chop (i1, i2)</strong> + </dt> + <dd> + Remove a subrange of elements. + equivalent to 'del s[i1:i2]' in Python. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + start of range + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + end of range + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + +</dd> + <dt> + <a name = "List:splice"></a> + <strong>List:splice (idx, list)</strong> + </dt> + <dd> + Insert a sublist into a list + equivalent to 's[idx:idx] = list' in Python + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">idx</span> + <span class="types"><span class="type">int</span></span> + index + </li> + <li><span class="parameter">list</span> + <span class="types"><a class="type" href="../classes/pl.List.html">List</a></span> + list to insert + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">l = List{<span class="number">10</span>,<span class="number">20</span>}; l:splice(<span class="number">2</span>,{<span class="number">21</span>,<span class="number">22</span>}); <span class="global">assert</span>(l == List{<span class="number">10</span>,<span class="number">21</span>,<span class="number">22</span>,<span class="number">20</span>})</pre> + </ul> + +</dd> + <dt> + <a name = "List:slice_assign"></a> + <strong>List:slice_assign (i1, i2, seq)</strong> + </dt> + <dd> + General slice assignment s[i1:i2] = seq. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + start index + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + end index + </li> + <li><span class="parameter">seq</span> + <span class="types"><a class="type" href="../classes/pl.List.html">List</a></span> + a list + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the list + </ol> + + + + +</dd> + <dt> + <a name = "List:join"></a> + <strong>List:join ([delim=''])</strong> + </dt> + <dd> + Join the elements of a list using a delimiter. + This method uses tostring on all elements. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">delim</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a delimiter string, can be empty. + (<em>default</em> '') + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a string + </ol> + + + + +</dd> + <dt> + <a name = "List:concat"></a> + <strong>List:concat ([delim=''])</strong> + </dt> + <dd> + Join a list of strings. <br> + Uses <a href="https://www.lua.org/manual/5.1/manual.html#pdf-table.concat">table.concat</a> directly. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">delim</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a delimiter + (<em>default</em> '') + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a string + </ol> + + + + +</dd> + <dt> + <a name = "List:foreach"></a> + <strong>List:foreach (fun, ...)</strong> + </dt> + <dd> + Call the function on each element of the list. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function or callable object + </li> + <li><span class="parameter">...</span> + optional values to pass to function + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "List:foreachm"></a> + <strong>List:foreachm (name, ...)</strong> + </dt> + <dd> + Call the named method on each element of the list. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the method name + </li> + <li><span class="parameter">...</span> + optional values to pass to function + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "List:filter"></a> + <strong>List:filter (fun[, arg])</strong> + </dt> + <dd> + Create a list of all elements which match a function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a boolean function + </li> + <li><span class="parameter">arg</span> + optional argument to be passed as second argument of the predicate + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new filtered list. + </ol> + + + + +</dd> + <dt> + <a name = "List.split"></a> + <strong>List.split (s[, delim])</strong> + </dt> + <dd> + Split a string using a delimiter. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">delim</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the delimiter (default spaces) + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a List of strings + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.utils.html#split">pl.utils.split</a> + </ul> + + +</dd> + <dt> + <a name = "List:map"></a> + <strong>List:map (fun, ...)</strong> + </dt> + <dd> + Apply a function to all elements. + Any extra arguments will be passed to the function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function of at least one argument + </li> + <li><span class="parameter">...</span> + arbitrary extra arguments. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new list: {f(x) for x in self} + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#imap">pl.tablex.imap</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example">List{<span class="string">'one'</span>,<span class="string">'two'</span>}:map(<span class="global">string</span>.upper) == {<span class="string">'ONE'</span>,<span class="string">'TWO'</span>}</pre> + </ul> + +</dd> + <dt> + <a name = "List:transform"></a> + <strong>List:transform (fun, ...)</strong> + </dt> + <dd> + Apply a function to all elements, in-place. + Any extra arguments are passed to the function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + A function that takes at least one argument + </li> + <li><span class="parameter">...</span> + arbitrary extra arguments. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the list. + </ol> + + + + +</dd> + <dt> + <a name = "List:map2"></a> + <strong>List:map2 (fun, ls, ...)</strong> + </dt> + <dd> + Apply a function to elements of two lists. + Any extra arguments will be passed to the function + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function of at least two arguments + </li> + <li><span class="parameter">ls</span> + <span class="types"><a class="type" href="../classes/pl.List.html">List</a></span> + another list + </li> + <li><span class="parameter">...</span> + arbitrary extra arguments. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new list: {f(x,y) for x in self, for x in arg1} + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#imap2">pl.tablex.imap2</a> + </ul> + + +</dd> + <dt> + <a name = "List:mapm"></a> + <strong>List:mapm (name, ...)</strong> + </dt> + <dd> + apply a named method to all elements. + Any extra arguments will be passed to the method. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + name of method + </li> + <li><span class="parameter">...</span> + extra arguments + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new list of the results + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.seq.html#mapmethod">pl.seq.mapmethod</a> + </ul> + + +</dd> + <dt> + <a name = "List:reduce"></a> + <strong>List:reduce (fun)</strong> + </dt> + <dd> + 'reduce' a list using a binary function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function of two arguments + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + result of the function + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#reduce">pl.tablex.reduce</a> + </ul> + + +</dd> + <dt> + <a name = "List:partition"></a> + <strong>List:partition (fun, ...)</strong> + </dt> + <dd> + Partition a list using a classifier function. + The function may return nil, but this will be converted to the string key '<nil>'. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function of at least one argument + </li> + <li><span class="parameter">...</span> + will also be passed to the function + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="../classes/pl.MultiMap.html">MultiMap</a></span> + a table where the keys are the returned values, and the values are Lists + of values where the function returned that key. + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../classes/pl.MultiMap.html#">pl.MultiMap</a> + </ul> + + +</dd> + <dt> + <a name = "List:iter"></a> + <strong>List:iter ()</strong> + </dt> + <dd> + return an iterator over all values. + + + + + + + +</dd> + <dt> + <a name = "List.iterate"></a> + <strong>List.iterate (seq)</strong> + </dt> + <dd> + Create an iterator over a seqence. + This captures the Python concept of 'sequence'. + For tables, iterates over all values with integer indices. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">seq</span> + a sequence; a string (over characters), a table, a file object (over lines) or an iterator function + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example"><span class="keyword">for</span> x <span class="keyword">in</span> iterate {<span class="number">1</span>,<span class="number">10</span>,<span class="number">22</span>,<span class="number">55</span>} <span class="keyword">do</span> <span class="global">io</span>.write(x,<span class="string">','</span>) <span class="keyword">end</span> ==> <span class="number">1</span>,<span class="number">10</span>,<span class="number">22</span>,<span class="number">55</span></pre></li> + <li><pre class="example"><span class="keyword">for</span> ch <span class="keyword">in</span> iterate <span class="string">'help'</span> <span class="keyword">do</span> <span class="keyword">do</span> <span class="global">io</span>.write(ch,<span class="string">' '</span>) <span class="keyword">end</span> ==> h e l p</pre></li> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="metamethods"></a>metamethods</h2> + + <dl class="function"> + <dt> + <a name = "List:__concat"></a> + <strong>List:__concat (L)</strong> + </dt> + <dd> + Concatenation operator. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">L</span> + <span class="types"><a class="type" href="../classes/pl.List.html">List</a></span> + another List + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new list consisting of the list with the elements of the new list appended + </ol> + + + + +</dd> + <dt> + <a name = "List:__eq"></a> + <strong>List:__eq (L)</strong> + </dt> + <dd> + Equality operator ==. True iff all elements of two lists are equal. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">L</span> + <span class="types"><a class="type" href="../classes/pl.List.html">List</a></span> + another List + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + +</dd> + <dt> + <a name = "List:__tostring"></a> + <strong>List:__tostring ()</strong> + </dt> + <dd> + How our list should be rendered as a string. Uses join(). + + + + + + <h3>See also:</h3> + <ul> + <a href="../classes/pl.List.html#List:join">List:join</a> + </ul> + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/classes/pl.Map.html b/Data/Libraries/Penlight/docs/classes/pl.Map.html new file mode 100644 index 0000000..72b4b40 --- /dev/null +++ b/Data/Libraries/Penlight/docs/classes/pl.Map.html @@ -0,0 +1,454 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Fields">Fields</a></li> +<li><a href="#Methods">Methods</a></li> +<li><a href="#Metamethods">Metamethods</a></li> +</ul> + + +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><strong>pl.Map</strong></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Class <code>pl.Map</code></h1> +<p>A Map class.</p> +<p> + + + +<pre> +> Map = <span class="global">require</span> <span class="string">'pl.Map'</span> +> m = Map{one=<span class="number">1</span>,two=<span class="number">2</span>} +> m:update {three=<span class="number">3</span>,four=<span class="number">4</span>,two=<span class="number">20</span>} +> = m == M{one=<span class="number">1</span>,two=<span class="number">20</span>,three=<span class="number">3</span>,four=<span class="number">4</span>} +<span class="keyword">true</span> +</pre> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.class.html#">pl.class</a>, <a href="../libraries/pl.tablex.html#">pl.tablex</a>, <a href="../libraries/pl.pretty.html#">pl.pretty</a></p> +</p> + + +<h2><a href="#Fields">Fields</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#pl.Map.keys">pl.Map.keys</a></td> + <td class="summary">list of keys.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Map.values">pl.Map.values</a></td> + <td class="summary">list of values.</td> + </tr> +</table> +<h2><a href="#Methods">Methods</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#pl.Map:iter">pl.Map:iter ()</a></td> + <td class="summary">return an iterator over all key-value pairs.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Map:items">pl.Map:items ()</a></td> + <td class="summary">return a List of all key-value pairs, sorted by the keys.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Map:setdefault">pl.Map:setdefault (key, default)</a></td> + <td class="summary">set a value in the map if it doesn't exist yet.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Map:len">pl.Map:len ()</a></td> + <td class="summary">size of map.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Map:set">pl.Map:set (key, val)</a></td> + <td class="summary">put a value into the map.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Map:get">pl.Map:get (key)</a></td> + <td class="summary">get a value from the map.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Map:getvalues">pl.Map:getvalues (keys)</a></td> + <td class="summary">get a list of values indexed by a list of keys.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Map:update">pl.Map:update (table)</a></td> + <td class="summary">update the map using key/value pairs from another table.</td> + </tr> +</table> +<h2><a href="#Metamethods">Metamethods</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#pl.Map:__eq">pl.Map:__eq (m)</a></td> + <td class="summary">equality between maps.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Map:__tostring">pl.Map:__tostring ()</a></td> + <td class="summary">string representation of a map.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Fields"></a>Fields</h2> + + <dl class="function"> + <dt> + <a name = "pl.Map.keys"></a> + <strong>pl.Map.keys</strong> + </dt> + <dd> + list of keys. + + + + + + + +</dd> + <dt> + <a name = "pl.Map.values"></a> + <strong>pl.Map.values</strong> + </dt> + <dd> + list of values. + + + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Methods"></a>Methods</h2> + + <dl class="function"> + <dt> + <a name = "pl.Map:iter"></a> + <strong>pl.Map:iter ()</strong> + </dt> + <dd> + return an iterator over all key-value pairs. + + + + + + + +</dd> + <dt> + <a name = "pl.Map:items"></a> + <strong>pl.Map:items ()</strong> + </dt> + <dd> + return a List of all key-value pairs, sorted by the keys. + + + + + + + +</dd> + <dt> + <a name = "pl.Map:setdefault"></a> + <strong>pl.Map:setdefault (key, default)</strong> + </dt> + <dd> + set a value in the map if it doesn't exist yet. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + the key + </li> + <li><span class="parameter">default</span> + value to set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the value stored in the map (existing value, or the new value) + </ol> + + + + +</dd> + <dt> + <a name = "pl.Map:len"></a> + <strong>pl.Map:len ()</strong> + </dt> + <dd> + size of map. + note: this is a relatively expensive operation! + + + + + + + +</dd> + <dt> + <a name = "pl.Map:set"></a> + <strong>pl.Map:set (key, val)</strong> + </dt> + <dd> + put a value into the map. + This will remove the key if the value is <code>nil</code> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + the key + </li> + <li><span class="parameter">val</span> + the value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "pl.Map:get"></a> + <strong>pl.Map:get (key)</strong> + </dt> + <dd> + get a value from the map. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + the key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the value, or nil if not found. + </ol> + + + + +</dd> + <dt> + <a name = "pl.Map:getvalues"></a> + <strong>pl.Map:getvalues (keys)</strong> + </dt> + <dd> + get a list of values indexed by a list of keys. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">keys</span> + a list-like table of keys + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new list + </ol> + + + + +</dd> + <dt> + <a name = "pl.Map:update"></a> + <strong>pl.Map:update (table)</strong> + </dt> + <dd> + update the map using key/value pairs from another table. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">table</span> + <span class="types"><span class="type">tab</span></span> + + + + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Metamethods"></a>Metamethods</h2> + + <dl class="function"> + <dt> + <a name = "pl.Map:__eq"></a> + <strong>pl.Map:__eq (m)</strong> + </dt> + <dd> + equality between maps. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">m</span> + <span class="types"><a class="type" href="../classes/pl.Map.html">Map</a></span> + another map. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "pl.Map:__tostring"></a> + <strong>pl.Map:__tostring ()</strong> + </dt> + <dd> + string representation of a map. + + + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/classes/pl.MultiMap.html b/Data/Libraries/Penlight/docs/classes/pl.MultiMap.html new file mode 100644 index 0000000..b711b54 --- /dev/null +++ b/Data/Libraries/Penlight/docs/classes/pl.MultiMap.html @@ -0,0 +1,207 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Methods">Methods</a></li> +</ul> + + +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><strong>pl.MultiMap</strong></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Class <code>pl.MultiMap</code></h1> +<p>MultiMap, a Map which has multiple values per key.</p> +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.class.html#">pl.class</a>, <a href="../classes/pl.List.html#">pl.List</a>, <a href="../classes/pl.Map.html#">pl.Map</a></p> + + +<h2><a href="#Methods">Methods</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#pl.MultiMap:update">pl.MultiMap:update (t)</a></td> + <td class="summary">update a MultiMap using a table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.MultiMap:set">pl.MultiMap:set (key, val)</a></td> + <td class="summary">add a new value to a key.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Methods"></a>Methods</h2> + + <dl class="function"> + <dt> + <a name = "pl.MultiMap:update"></a> + <strong>pl.MultiMap:update (t)</strong> + </dt> + <dd> + update a MultiMap using a table. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + either a Multimap or a map-like table. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the map + </ol> + + + + +</dd> + <dt> + <a name = "pl.MultiMap:set"></a> + <strong>pl.MultiMap:set (key, val)</strong> + </dt> + <dd> + add a new value to a key. Setting a nil value removes the key. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + the key + </li> + <li><span class="parameter">val</span> + the value + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the map + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/classes/pl.OrderedMap.html b/Data/Libraries/Penlight/docs/classes/pl.OrderedMap.html new file mode 100644 index 0000000..537af37 --- /dev/null +++ b/Data/Libraries/Penlight/docs/classes/pl.OrderedMap.html @@ -0,0 +1,417 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Methods">Methods</a></li> +<li><a href="#Metamethods">Metamethods</a></li> +</ul> + + +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><strong>pl.OrderedMap</strong></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Class <code>pl.OrderedMap</code></h1> +<p>OrderedMap, a map which preserves ordering.</p> +<p> Derived from <a href="../classes/pl.Map.html#">pl.Map</a>.</p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.tablex.html#">pl.tablex</a>, <a href="../libraries/pl.class.html#">pl.class</a>, <a href="../classes/pl.List.html#">pl.List</a>, <a href="../classes/pl.Map.html#">pl.Map</a></p> + + +<h2><a href="#Methods">Methods</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#pl.OrderedMap:_init">pl.OrderedMap:_init (t)</a></td> + <td class="summary">construct an OrderedMap.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.OrderedMap:update">pl.OrderedMap:update (t)</a></td> + <td class="summary">update an OrderedMap using a table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.OrderedMap:set">pl.OrderedMap:set (key, val)</a></td> + <td class="summary">set the key's value.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.OrderedMap:insert">pl.OrderedMap:insert (pos, key, val)</a></td> + <td class="summary">insert a key/value pair before a given position.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.OrderedMap:keys">pl.OrderedMap:keys ()</a></td> + <td class="summary">return the keys in order.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.OrderedMap:values">pl.OrderedMap:values ()</a></td> + <td class="summary">return the values in order.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.OrderedMap:sort">pl.OrderedMap:sort (cmp)</a></td> + <td class="summary">sort the keys.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.OrderedMap:iter">pl.OrderedMap:iter ()</a></td> + <td class="summary">iterate over key-value pairs in order.</td> + </tr> +</table> +<h2><a href="#Metamethods">Metamethods</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#pl.OrderedMap:__pairs">pl.OrderedMap:__pairs ()</a></td> + <td class="summary">iterate over an ordered map (5.2).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.OrderedMap:__tostring">pl.OrderedMap:__tostring ()</a></td> + <td class="summary">string representation of an ordered map.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Methods"></a>Methods</h2> + + <dl class="function"> + <dt> + <a name = "pl.OrderedMap:_init"></a> + <strong>pl.OrderedMap:_init (t)</strong> + </dt> + <dd> + construct an OrderedMap. + Will throw an error if the argument is bad. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + optional initialization table, same as for <a href="../classes/pl.OrderedMap.html#pl.OrderedMap:update">OrderedMap:update</a> + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "pl.OrderedMap:update"></a> + <strong>pl.OrderedMap:update (t)</strong> + </dt> + <dd> + update an OrderedMap using a table. + If the table is itself an OrderedMap, then its entries will be appended. + if it s a table of the form <code>{{key1=val1},{key2=val2},...}</code> these will be appended.</p> + +<p> Otherwise, it is assumed to be a map-like table, and order of extra entries is arbitrary. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + a table. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + the map, or nil in case of error</li> + <li> + the error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "pl.OrderedMap:set"></a> + <strong>pl.OrderedMap:set (key, val)</strong> + </dt> + <dd> + set the key's value. This key will be appended at the end of the map.</p> + +<p> If the value is nil, then the key is removed. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + the key + </li> + <li><span class="parameter">val</span> + the value + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the map + </ol> + + + + +</dd> + <dt> + <a name = "pl.OrderedMap:insert"></a> + <strong>pl.OrderedMap:insert (pos, key, val)</strong> + </dt> + <dd> + insert a key/value pair before a given position. + Note: if the map already contains the key, then this effectively + moves the item to the new position by first removing at the old position. + Has no effect if the key does not exist and val is nil + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">pos</span> + <span class="types"><span class="type">int</span></span> + a position starting at 1 + </li> + <li><span class="parameter">key</span> + the key + </li> + <li><span class="parameter">val</span> + the value; if nil use the old value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "pl.OrderedMap:keys"></a> + <strong>pl.OrderedMap:keys ()</strong> + </dt> + <dd> + return the keys in order. + (Not a copy!) + + + + <h3>Returns:</h3> + <ol> + + List + </ol> + + + + +</dd> + <dt> + <a name = "pl.OrderedMap:values"></a> + <strong>pl.OrderedMap:values ()</strong> + </dt> + <dd> + return the values in order. + this is relatively expensive. + + + + <h3>Returns:</h3> + <ol> + + List + </ol> + + + + +</dd> + <dt> + <a name = "pl.OrderedMap:sort"></a> + <strong>pl.OrderedMap:sort (cmp)</strong> + </dt> + <dd> + sort the keys. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">cmp</span> + <span class="types"><span class="type">func</span></span> + a comparison function as for <a href="https://www.lua.org/manual/5.1/manual.html#pdf-table.sort">table.sort</a> + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the map + </ol> + + + + +</dd> + <dt> + <a name = "pl.OrderedMap:iter"></a> + <strong>pl.OrderedMap:iter ()</strong> + </dt> + <dd> + iterate over key-value pairs in order. + + + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Metamethods"></a>Metamethods</h2> + + <dl class="function"> + <dt> + <a name = "pl.OrderedMap:__pairs"></a> + <strong>pl.OrderedMap:__pairs ()</strong> + </dt> + <dd> + iterate over an ordered map (5.2). + + + + + + + +</dd> + <dt> + <a name = "pl.OrderedMap:__tostring"></a> + <strong>pl.OrderedMap:__tostring ()</strong> + </dt> + <dd> + string representation of an ordered map. + + + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/classes/pl.Set.html b/Data/Libraries/Penlight/docs/classes/pl.Set.html new file mode 100644 index 0000000..31cfcee --- /dev/null +++ b/Data/Libraries/Penlight/docs/classes/pl.Set.html @@ -0,0 +1,655 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Methods">Methods</a></li> +<li><a href="#Metamethods">Metamethods</a></li> +</ul> + + +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><strong>pl.Set</strong></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Class <code>pl.Set</code></h1> +<p>A Set class.</p> +<p> + + + +<pre> +> Set = <span class="global">require</span> <span class="string">'pl.Set'</span> +> = Set{<span class="string">'one'</span>,<span class="string">'two'</span>} == Set{<span class="string">'two'</span>,<span class="string">'one'</span>} +<span class="keyword">true</span> +> fruit = Set{<span class="string">'apple'</span>,<span class="string">'banana'</span>,<span class="string">'orange'</span>} +> = fruit[<span class="string">'banana'</span>] +<span class="keyword">true</span> +> = fruit[<span class="string">'hazelnut'</span>] +<span class="keyword">nil</span> +> colours = Set{<span class="string">'red'</span>,<span class="string">'orange'</span>,<span class="string">'green'</span>,<span class="string">'blue'</span>} +> = fruit,colours +[apple,orange,banana] [blue,green,orange,red] +> = fruit+colours +[blue,green,apple,red,orange,banana] +[orange] +> more_fruits = fruit + <span class="string">'apricot'</span> +> = fruit*colours + = more_fruits, fruit +banana,apricot,apple,orange] [banana,apple,orange] +</pre> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.tablex.html#">pl.tablex</a>, <a href="../libraries/pl.class.html#">pl.class</a>, <a href="../classes/pl.Map.html#">pl.Map</a>, (<a href="../classes/pl.List.html#">pl.List</a> if __tostring is used)</p> +</p> + + +<h2><a href="#Methods">Methods</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#pl.Set:Set">pl.Set:Set (t)</a></td> + <td class="summary">create a set.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:values">pl.Set:values (self)</a></td> + <td class="summary">get a list of the values in a set.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:map">pl.Set:map (self, fn, ...)</a></td> + <td class="summary">map a function over the values of a set.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:union">pl.Set:union (self, set)</a></td> + <td class="summary">union of two sets (also +).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:intersection">pl.Set:intersection (self, set)</a></td> + <td class="summary">intersection of two sets (also *).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:difference">pl.Set:difference (self, set)</a></td> + <td class="summary">new set with elements in the set that are not in the other (also -).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:issubset">pl.Set:issubset (self, set)</a></td> + <td class="summary">is the first set a subset of the second (also <)?.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:isempty">pl.Set:isempty (self)</a></td> + <td class="summary">is the set empty?.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:isdisjoint">pl.Set:isdisjoint (s1, s2)</a></td> + <td class="summary">are the sets disjoint?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:len">pl.Set:len (s)</a></td> + <td class="summary">size of this set (also # for 5.2).</td> + </tr> +</table> +<h2><a href="#Metamethods">Metamethods</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#pl.Set:__tostring">pl.Set:__tostring ()</a></td> + <td class="summary">string representation of a set.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:__add">pl.Set:__add ()</a></td> + <td class="summary">union of sets.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:__mul">pl.Set:__mul ()</a></td> + <td class="summary">intersection of sets.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:__sub">pl.Set:__sub ()</a></td> + <td class="summary">difference of sets.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:__pow">pl.Set:__pow ()</a></td> + <td class="summary">symmetric difference of sets.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:__lt">pl.Set:__lt ()</a></td> + <td class="summary">first set subset of second?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:__len">pl.Set:__len ()</a></td> + <td class="summary">cardinality of set (5.2).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pl.Set:__eq">pl.Set:__eq (s1, s2)</a></td> + <td class="summary">equality between sets.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Methods"></a>Methods</h2> + + <dl class="function"> + <dt> + <a name = "pl.Set:Set"></a> + <strong>pl.Set:Set (t)</strong> + </dt> + <dd> + create a set. <br> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + may be a Set, Map or list-like table. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "pl.Set:values"></a> + <strong>pl.Set:values (self)</strong> + </dt> + <dd> + get a list of the values in a set. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a list + </ol> + + + + +</dd> + <dt> + <a name = "pl.Set:map"></a> + <strong>pl.Set:map (self, fn, ...)</strong> + </dt> + <dd> + map a function over the values of a set. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + <li><span class="parameter">fn</span> + a function + </li> + <li><span class="parameter">...</span> + extra arguments to pass to the function. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new set + </ol> + + + + +</dd> + <dt> + <a name = "pl.Set:union"></a> + <strong>pl.Set:union (self, set)</strong> + </dt> + <dd> + union of two sets (also +). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + <li><span class="parameter">set</span> + another set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new set + </ol> + + + + +</dd> + <dt> + <a name = "pl.Set:intersection"></a> + <strong>pl.Set:intersection (self, set)</strong> + </dt> + <dd> + intersection of two sets (also *). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + <li><span class="parameter">set</span> + another set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new set + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">> s = Set{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>} +> t = Set{<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>} +> = t +[<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>] +> = Set.intersection(s,t) +[<span class="number">30</span>,<span class="number">20</span>] +> = s*t +[<span class="number">30</span>,<span class="number">20</span>]</pre> + </ul> + +</dd> + <dt> + <a name = "pl.Set:difference"></a> + <strong>pl.Set:difference (self, set)</strong> + </dt> + <dd> + new set with elements in the set that are not in the other (also -). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + <li><span class="parameter">set</span> + another set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new set + </ol> + + + + +</dd> + <dt> + <a name = "pl.Set:issubset"></a> + <strong>pl.Set:issubset (self, set)</strong> + </dt> + <dd> + is the first set a subset of the second (also <)?. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + <li><span class="parameter">set</span> + another set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + +</dd> + <dt> + <a name = "pl.Set:isempty"></a> + <strong>pl.Set:isempty (self)</strong> + </dt> + <dd> + is the set empty?. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + +</dd> + <dt> + <a name = "pl.Set:isdisjoint"></a> + <strong>pl.Set:isdisjoint (s1, s2)</strong> + </dt> + <dd> + are the sets disjoint? (no elements in common). + Uses naive definition, i.e. that intersection is empty + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s1</span> + a Set + </li> + <li><span class="parameter">s2</span> + another set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + +</dd> + <dt> + <a name = "pl.Set:len"></a> + <strong>pl.Set:len (s)</strong> + </dt> + <dd> + size of this set (also # for 5.2). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + a Set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + size + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Metamethods"></a>Metamethods</h2> + + <dl class="function"> + <dt> + <a name = "pl.Set:__tostring"></a> + <strong>pl.Set:__tostring ()</strong> + </dt> + <dd> + string representation of a set. + + + + + + + +</dd> + <dt> + <a name = "pl.Set:__add"></a> + <strong>pl.Set:__add ()</strong> + </dt> + <dd> + union of sets. + + + + + + + +</dd> + <dt> + <a name = "pl.Set:__mul"></a> + <strong>pl.Set:__mul ()</strong> + </dt> + <dd> + intersection of sets. + + + + + + + +</dd> + <dt> + <a name = "pl.Set:__sub"></a> + <strong>pl.Set:__sub ()</strong> + </dt> + <dd> + difference of sets. + + + + + + + +</dd> + <dt> + <a name = "pl.Set:__pow"></a> + <strong>pl.Set:__pow ()</strong> + </dt> + <dd> + symmetric difference of sets. + + + + + + + +</dd> + <dt> + <a name = "pl.Set:__lt"></a> + <strong>pl.Set:__lt ()</strong> + </dt> + <dd> + first set subset of second? + + + + + + + +</dd> + <dt> + <a name = "pl.Set:__len"></a> + <strong>pl.Set:__len ()</strong> + </dt> + <dd> + cardinality of set (5.2). + + + + + + + +</dd> + <dt> + <a name = "pl.Set:__eq"></a> + <strong>pl.Set:__eq (s1, s2)</strong> + </dt> + <dd> + equality between sets. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s1</span> + + + + </li> + <li><span class="parameter">s2</span> + + + + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/seesubst.lua.html b/Data/Libraries/Penlight/docs/examples/seesubst.lua.html new file mode 100644 index 0000000..e850122 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/seesubst.lua.html @@ -0,0 +1,174 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><strong>seesubst.lua</strong></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>seesubst.lua</h2> +<pre> +<span class="comment">-- shows how replacing '@see module' in the Markdown documentation +</span><span class="comment">-- can be done more elegantly using PL. +</span><span class="comment">-- We either have something like 'pl.config' (a module reference) +</span><span class="comment">-- or 'pl.seq.map' (a function reference); these cases must be distinguished +</span><span class="comment">-- and a Markdown link generated pointing to the LuaDoc file. +</span> +<span class="keyword">local</span> sip = <span class="global">require</span> <span class="string">'pl.sip'</span> +<span class="keyword">local</span> stringx = <span class="global">require</span> <span class="string">'pl.stringx'</span> + +<span class="keyword">local</span> res = {} +<span class="keyword">local</span> s = <span class="string">[[ +(@see pl.bonzo.dog) +remember about @see pl.bonzo + +]]</span> + +<span class="keyword">local</span> _gsub_patterns = {} + +<span class="keyword">local</span> <span class="keyword">function</span> gsub (s,pat,subst,start) + <span class="keyword">local</span> fpat = _gsub_patterns[pat] + <span class="keyword">if</span> <span class="keyword">not</span> fpat <span class="keyword">then</span> + <span class="comment">-- use SIP to generate a proper string pattern. +</span> <span class="comment">-- the _whole thing_ is a capture, to get the whole match +</span> <span class="comment">-- and the unnamed capture. +</span> fpat = <span class="string">'('</span>..sip.create_pattern(pat)..<span class="string">')'</span> + _gsub_patterns[pat] = fpat + <span class="keyword">end</span> + <span class="keyword">return</span> s:gsub(fpat,subst,start) +<span class="keyword">end</span> + + +<span class="keyword">local</span> mod = sip.compile <span class="string">'$v.$v'</span> +<span class="keyword">local</span> fun = sip.compile <span class="string">'$v.$v.$v'</span> + +<span class="keyword">for</span> line <span class="keyword">in</span> stringx.lines(s) <span class="keyword">do</span> + line = gsub(line,<span class="string">'@see $p'</span>,<span class="keyword">function</span>(see,path) + <span class="keyword">if</span> fun(path,res) <span class="keyword">or</span> mod(path,res) <span class="keyword">then</span> + <span class="keyword">local</span> ret = (<span class="string">'[see %s](%s.%s.html'</span>):format(path,res[<span class="number">1</span>],res[<span class="number">2</span>]) + <span class="keyword">if</span> res[<span class="number">3</span>] <span class="keyword">then</span> + <span class="keyword">return</span> ret..<span class="string">'#'</span>..res[<span class="number">3</span>]..<span class="string">')'</span> + <span class="keyword">else</span> + <span class="keyword">return</span> ret..<span class="string">')'</span> + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">end</span>) + <span class="global">print</span>(line) +<span class="keyword">end</span></pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/sipscan.lua.html b/Data/Libraries/Penlight/docs/examples/sipscan.lua.html new file mode 100644 index 0000000..8848423 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/sipscan.lua.html @@ -0,0 +1,161 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><strong>sipscan.lua</strong></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>sipscan.lua</h2> +<pre> +<span class="comment">-- another SIP example, shows how an awkward log file format +</span><span class="comment">-- can be parsed. It also prints out the actual Lua string +</span><span class="comment">-- pattern generated: +</span><span class="comment">-- SYNC%s*%[([+%-%d]%d*)%]%s*([+%-%d]%d*)%s*([+%-%d]%d*) +</span> +<span class="keyword">local</span> sip = <span class="global">require</span> <span class="string">'pl.sip'</span> +<span class="keyword">local</span> stringx = <span class="global">require</span> <span class="string">'pl.stringx'</span> + +<span class="keyword">local</span> s = <span class="string">[[ +SYNC [1] 0 547 (14679 sec) +SYNC [2] 0 555 (14679 sec) +SYNC [3] 0 563 (14679 sec) +SYNC [4] 0 571 (14679 sec) +SYNC [5] -1 580 (14679 sec) +SYNC [6] 0 587 (14679 sec) +]]</span> + + +<span class="keyword">local</span> first = <span class="keyword">true</span> +<span class="keyword">local</span> expected +<span class="keyword">local</span> res = {} +<span class="keyword">local</span> pat = <span class="string">'SYNC [$i{seq}] $i{diff} $i{val}'</span> +<span class="global">print</span>(sip.create_pattern(pat)) +<span class="keyword">local</span> match = sip.compile(pat) +<span class="keyword">for</span> line <span class="keyword">in</span> stringx.lines(s) <span class="keyword">do</span> + <span class="keyword">if</span> match(line,res) <span class="keyword">then</span> + <span class="keyword">if</span> first <span class="keyword">then</span> + expected = res.val + first = <span class="keyword">false</span> + <span class="keyword">end</span> + <span class="global">print</span>(res.val,expected - res.val) + expected = expected + <span class="number">8</span> + <span class="keyword">end</span> +<span class="keyword">end</span></pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/symbols.lua.html b/Data/Libraries/Penlight/docs/examples/symbols.lua.html new file mode 100644 index 0000000..0dafb21 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/symbols.lua.html @@ -0,0 +1,347 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><strong>symbols.lua</strong></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>symbols.lua</h2> +<pre> +<span class="global">require</span> <span class="string">'pl'</span> +utils.import <span class="string">'pl.func'</span> +<span class="keyword">local</span> ops = <span class="global">require</span> <span class="string">'pl.operator'</span> +<span class="keyword">local</span> List = <span class="global">require</span> <span class="string">'pl.List'</span> +<span class="keyword">local</span> append,concat = <span class="global">table</span>.insert,<span class="global">table</span>.concat +<span class="keyword">local</span> compare,find_if,compare_no_order,imap,reduce,count_map = tablex.compare,tablex.find_if,tablex.compare_no_order,tablex.imap,tablex.reduce,tablex.count_map +<span class="keyword">local</span> <span class="global">unpack</span> = <span class="global">table</span>.<span class="global">unpack</span> + +<span class="keyword">function</span> bindval (self,val) + <span class="global">rawset</span>(self,<span class="string">'value'</span>,val) +<span class="keyword">end</span> + +<span class="keyword">local</span> optable = ops.optable + +<span class="keyword">function</span> sexpr (e) + <span class="keyword">if</span> isPE(e) <span class="keyword">then</span> + <span class="keyword">if</span> e.op ~= <span class="string">'X'</span> <span class="keyword">then</span> + <span class="keyword">local</span> args = tablex.imap(sexpr,e) + <span class="keyword">return</span> <span class="string">'('</span>..e.op..<span class="string">' '</span>..<span class="global">table</span>.concat(args,<span class="string">' '</span>)..<span class="string">')'</span> + <span class="keyword">else</span> + <span class="keyword">return</span> e.repr + <span class="keyword">end</span> + <span class="keyword">else</span> + <span class="keyword">return</span> <span class="global">tostring</span>(e) + <span class="keyword">end</span> +<span class="keyword">end</span> + + +psexpr = compose(<span class="global">print</span>,sexpr) + + + +<span class="keyword">function</span> equals (e1,e2) + <span class="keyword">local</span> p1,p2 = isPE(e1),isPE(e2) + <span class="keyword">if</span> p1 ~= p2 <span class="keyword">then</span> <span class="keyword">return</span> <span class="keyword">false</span> <span class="keyword">end</span> <span class="comment">-- different kinds of animals! +</span> <span class="keyword">if</span> p1 <span class="keyword">and</span> p2 <span class="keyword">then</span> <span class="comment">-- both PEs +</span> <span class="comment">-- operators must be the same +</span> <span class="keyword">if</span> e1.op ~= e2.op <span class="keyword">then</span> <span class="keyword">return</span> <span class="keyword">false</span> <span class="keyword">end</span> + <span class="comment">-- PHs are equal if their representations are equal +</span> <span class="keyword">if</span> e1.op == <span class="string">'X'</span> <span class="keyword">then</span> <span class="keyword">return</span> e1.repr == e2.repr + <span class="comment">-- commutative operators +</span> <span class="keyword">elseif</span> e1.op == <span class="string">'+'</span> <span class="keyword">or</span> e1.op == <span class="string">'*'</span> <span class="keyword">then</span> + <span class="keyword">return</span> compare_no_order(e1,e2,equals) + <span class="keyword">else</span> + <span class="comment">-- arguments must be the same +</span> <span class="keyword">return</span> compare(e1,e2,equals) + <span class="keyword">end</span> + <span class="keyword">else</span> <span class="comment">-- fall back on simple equality for non PEs +</span> <span class="keyword">return</span> e1 == e2 + <span class="keyword">end</span> +<span class="keyword">end</span> + +<span class="comment">-- run down an unbalanced operator chain (like a+b+c) and return the arguments {a,b,c} +</span><span class="keyword">function</span> tcollect (op,e,ls) + <span class="keyword">if</span> isPE(e) <span class="keyword">and</span> e.op == op <span class="keyword">then</span> + <span class="keyword">for</span> i = <span class="number">1</span>,#e <span class="keyword">do</span> + tcollect(op,e[i],ls) + <span class="keyword">end</span> + <span class="keyword">else</span> + ls:append(e) + <span class="keyword">return</span> + <span class="keyword">end</span> +<span class="keyword">end</span> + +<span class="keyword">function</span> rcollect (e) + <span class="keyword">local</span> res = List() + tcollect(e.op,e,res) + <span class="keyword">return</span> res +<span class="keyword">end</span> + + +<span class="comment">-- balance ensures that +/* chains are collected together, operates in-place. +</span><span class="comment">-- thus (+(+ a b) c) or (+ a (+ b c)) becomes (+ a b c), order immaterial +</span><span class="keyword">function</span> balance (e) + <span class="keyword">if</span> isPE(e) <span class="keyword">and</span> e.op ~= <span class="string">'X'</span> <span class="keyword">then</span> + <span class="keyword">local</span> op,args = e.op + <span class="keyword">if</span> op == <span class="string">'+'</span> <span class="keyword">or</span> op == <span class="string">'*'</span> <span class="keyword">then</span> + args = rcollect(e) + <span class="keyword">else</span> + args = imap(balance,e) + <span class="keyword">end</span> + <span class="keyword">for</span> i = <span class="number">1</span>,#args <span class="keyword">do</span> + e[i] = args[i] + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">return</span> e +<span class="keyword">end</span> + +<span class="comment">-- fold constants in an expression +</span><span class="keyword">function</span> fold (e) + <span class="keyword">if</span> isPE(e) <span class="keyword">then</span> + <span class="keyword">if</span> e.op == <span class="string">'X'</span> <span class="keyword">then</span> + <span class="comment">-- there could be _bound values_! +</span> <span class="keyword">local</span> val = <span class="global">rawget</span>(e,<span class="string">'value'</span>) + <span class="keyword">return</span> val <span class="keyword">and</span> val <span class="keyword">or</span> e + <span class="keyword">else</span> + <span class="keyword">local</span> op = e.op + <span class="keyword">local</span> addmul = op == <span class="string">'*'</span> <span class="keyword">or</span> op == <span class="string">'+'</span> + <span class="comment">-- first fold all arguments +</span> <span class="keyword">local</span> args = imap(fold,e) + <span class="keyword">if</span> <span class="keyword">not</span> addmul <span class="keyword">and</span> <span class="keyword">not</span> find_if(args,isPE) <span class="keyword">then</span> + <span class="comment">-- no placeholders in these args, we can fold the expression. +</span> <span class="keyword">local</span> opfn = optable[op] + <span class="keyword">if</span> opfn <span class="keyword">then</span> + <span class="keyword">return</span> opfn(<span class="global">unpack</span>(args)) + <span class="keyword">else</span> + <span class="keyword">return</span> <span class="string">'?'</span> + <span class="keyword">end</span> + <span class="keyword">elseif</span> addmul <span class="keyword">then</span> + <span class="comment">-- enforce a few rules for + and * +</span> <span class="comment">-- split the args into two classes, PE args and non-PE args. +</span> <span class="keyword">local</span> classes = List.partition(args,isPE) + <span class="keyword">local</span> pe,npe = classes[<span class="keyword">true</span>],classes[<span class="keyword">false</span>] + <span class="keyword">if</span> npe <span class="keyword">then</span> <span class="comment">-- there's at least one non PE argument +</span> <span class="comment">-- so fold them +</span> <span class="keyword">if</span> #npe == <span class="number">1</span> <span class="keyword">then</span> npe = npe[<span class="number">1</span>] + <span class="keyword">else</span> npe = npe:reduce(optable[op]) + <span class="keyword">end</span> + <span class="comment">-- if the result is a constant, return it +</span> <span class="keyword">if</span> <span class="keyword">not</span> pe <span class="keyword">then</span> <span class="keyword">return</span> npe <span class="keyword">end</span> + + <span class="comment">-- either (* 1 x) => x or (* 1 x y ...) => (* x y ...) +</span> <span class="keyword">if</span> op == <span class="string">'*'</span> <span class="keyword">then</span> + <span class="keyword">if</span> npe == <span class="number">0</span> <span class="keyword">then</span> <span class="keyword">return</span> <span class="number">0</span> + <span class="keyword">elseif</span> npe == <span class="number">1</span> <span class="keyword">then</span> <span class="comment">-- identity +</span> <span class="keyword">if</span> #pe == <span class="number">1</span> <span class="keyword">then</span> <span class="keyword">return</span> pe[<span class="number">1</span>] <span class="keyword">else</span> npe = <span class="keyword">nil</span> <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">else</span> <span class="comment">-- special cases for + +</span> <span class="keyword">if</span> npe == <span class="number">0</span> <span class="keyword">then</span> <span class="comment">-- identity +</span> <span class="keyword">if</span> #pe == <span class="number">1</span> <span class="keyword">then</span> <span class="keyword">return</span> pe[<span class="number">1</span>] <span class="keyword">else</span> npe = <span class="keyword">nil</span> <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="comment">-- build up the final arguments +</span> <span class="keyword">local</span> res = {} + <span class="keyword">if</span> npe <span class="keyword">then</span> append(res,npe) <span class="keyword">end</span> + <span class="keyword">for</span> val,count <span class="keyword">in</span> <span class="global">pairs</span>(count_map(pe,equals)) <span class="keyword">do</span> + <span class="keyword">if</span> count > <span class="number">1</span> <span class="keyword">then</span> + <span class="keyword">if</span> op == <span class="string">'*'</span> <span class="keyword">then</span> val = val ^ count + <span class="keyword">else</span> val = val * count + <span class="keyword">end</span> + <span class="keyword">end</span> + append(res,val) + <span class="keyword">end</span> + <span class="keyword">if</span> #res == <span class="number">1</span> <span class="keyword">then</span> <span class="keyword">return</span> res[<span class="number">1</span>] <span class="keyword">end</span> + <span class="keyword">return</span> PE{op=op,<span class="global">unpack</span>(res)} + <span class="keyword">elseif</span> op == <span class="string">'^'</span> <span class="keyword">then</span> + <span class="keyword">if</span> args[<span class="number">2</span>] == <span class="number">1</span> <span class="keyword">then</span> <span class="keyword">return</span> args[<span class="number">1</span>] <span class="keyword">end</span> <span class="comment">-- identity +</span> <span class="keyword">if</span> args[<span class="number">2</span>] == <span class="number">0</span> <span class="keyword">then</span> <span class="keyword">return</span> <span class="number">1</span> <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">return</span> PE{op=op,<span class="global">unpack</span>(args)} + <span class="keyword">end</span> + <span class="keyword">else</span> + <span class="keyword">return</span> e + <span class="keyword">end</span> +<span class="keyword">end</span> + +<span class="keyword">function</span> expand (e) + <span class="keyword">if</span> isPE(e) <span class="keyword">and</span> e.op == <span class="string">'*'</span> <span class="keyword">and</span> isPE(e[<span class="number">2</span>]) <span class="keyword">and</span> e[<span class="number">2</span>].op == <span class="string">'+'</span> <span class="keyword">then</span> + <span class="keyword">local</span> a,b = e[<span class="number">1</span>],e[<span class="number">2</span>] + <span class="keyword">return</span> expand(b[<span class="number">1</span>]*a) + expand(b[<span class="number">2</span>]*a) + <span class="keyword">else</span> + <span class="keyword">return</span> e + <span class="keyword">end</span> +<span class="keyword">end</span> + +<span class="keyword">function</span> isnumber (x) + <span class="keyword">return</span> <span class="global">type</span>(x) == <span class="string">'number'</span> +<span class="keyword">end</span> + +<span class="comment">-- does this PE contain a reference to x? +</span><span class="keyword">function</span> references (e,x) + <span class="keyword">if</span> isPE(e) <span class="keyword">then</span> + <span class="keyword">if</span> e.op == <span class="string">'X'</span> <span class="keyword">then</span> <span class="keyword">return</span> x.repr == e.repr + <span class="keyword">else</span> + <span class="keyword">return</span> find_if(e,references,x) + <span class="keyword">end</span> + <span class="keyword">else</span> + <span class="keyword">return</span> <span class="keyword">false</span> + <span class="keyword">end</span> +<span class="keyword">end</span> + +<span class="keyword">local</span> <span class="keyword">function</span> muli (args) + <span class="keyword">return</span> PE{op=<span class="string">'*'</span>,<span class="global">unpack</span>(args)} +<span class="keyword">end</span> + +<span class="keyword">local</span> <span class="keyword">function</span> addi (args) + <span class="keyword">return</span> PE{op=<span class="string">'+'</span>,<span class="global">unpack</span>(args)} +<span class="keyword">end</span> + +<span class="keyword">function</span> diff (e,x) + <span class="keyword">if</span> isPE(e) <span class="keyword">and</span> references(e,x) <span class="keyword">then</span> + <span class="keyword">local</span> op = e.op + <span class="keyword">if</span> op == <span class="string">'X'</span> <span class="keyword">then</span> + <span class="keyword">return</span> <span class="number">1</span> + <span class="keyword">else</span> + <span class="keyword">local</span> a,b = e[<span class="number">1</span>],e[<span class="number">2</span>] + <span class="keyword">if</span> op == <span class="string">'+'</span> <span class="keyword">then</span> <span class="comment">-- differentiation is linear +</span> <span class="keyword">local</span> args = imap(diff,e,x) + <span class="keyword">return</span> balance(addi(args)) + <span class="keyword">elseif</span> op == <span class="string">'*'</span> <span class="keyword">then</span> <span class="comment">-- product rule +</span> <span class="keyword">local</span> res,d,ee = {} + <span class="keyword">for</span> i = <span class="number">1</span>,#e <span class="keyword">do</span> + d = fold(diff(e[i],x)) + <span class="keyword">if</span> d ~= <span class="number">0</span> <span class="keyword">then</span> + ee = {<span class="global">unpack</span>(e)} + ee[i] = d + append(res,balance(muli(ee))) + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">if</span> #res > <span class="number">1</span> <span class="keyword">then</span> <span class="keyword">return</span> addi(res) + <span class="keyword">else</span> <span class="keyword">return</span> res[<span class="number">1</span>] <span class="keyword">end</span> + <span class="keyword">elseif</span> op == <span class="string">'^'</span> <span class="keyword">and</span> isnumber(b) <span class="keyword">then</span> <span class="comment">-- power rule +</span> <span class="keyword">return</span> b*x^(b-<span class="number">1</span>) + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">else</span> + <span class="keyword">return</span> <span class="number">0</span> + <span class="keyword">end</span> +<span class="keyword">end</span></pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/test-cmp.lua.html b/Data/Libraries/Penlight/docs/examples/test-cmp.lua.html new file mode 100644 index 0000000..ac6e44a --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/test-cmp.lua.html @@ -0,0 +1,130 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><strong>test-cmp.lua</strong></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>test-cmp.lua</h2> +<pre> +<span class="keyword">local</span> A = <span class="global">require</span> <span class="string">'pl.tablex'</span> +<span class="global">print</span>(A.compare_no_order({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>},{<span class="number">2</span>,<span class="number">1</span>,<span class="number">3</span>})) +<span class="global">print</span>(A.compare_no_order({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>},{<span class="number">2</span>,<span class="number">1</span>,<span class="number">3</span>},<span class="string">'=='</span>))</pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/test-data.lua.html b/Data/Libraries/Penlight/docs/examples/test-data.lua.html new file mode 100644 index 0000000..8128411 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/test-data.lua.html @@ -0,0 +1,372 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><strong>test-data.lua</strong></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>test-data.lua</h2> +<pre> +<span class="keyword">local</span> data = <span class="global">require</span> <span class="string">'pl.data'</span> +<span class="keyword">local</span> List = <span class="global">require</span> <span class="string">'pl.List'</span> +<span class="keyword">local</span> array = <span class="global">require</span> <span class="string">'pl.array2d'</span> +<span class="keyword">local</span> func = <span class="global">require</span> <span class="string">'pl.func'</span> +<span class="keyword">local</span> seq = <span class="global">require</span> <span class="string">'pl.seq'</span> +<span class="keyword">local</span> stringio = <span class="global">require</span> <span class="string">'pl.stringio'</span> +<span class="keyword">local</span> open = stringio. open +<span class="keyword">local</span> asserteq = <span class="global">require</span> <span class="string">'pl.test'</span> . asserteq +<span class="keyword">local</span> T = <span class="global">require</span> <span class="string">'pl.test'</span>. tuple + +<span class="comment">--[=[ +dat,err = data.read(open [[ +1.0 0.1 +0.2 1.3 +]]) + +if err then print(err) end + +require 'pl.pretty'.dump(dat) +os.exit(0) +--]=]</span> + +<span class="comment">-- tab-separated data, explicit column names +</span><span class="keyword">local</span> t1f = open <span class="string">[[ +EventID Magnitude LocationX LocationY LocationZ LocationError EventDate DataFile +981124001 2.0 18988.4 10047.1 4149.7 33.8 24/11/1998 11:18:05 981124DF.AAB +981125001 0.8 19104.0 9970.4 5088.7 3.0 25/11/1998 05:44:54 981125DF.AAB +981127003 0.5 19012.5 9946.9 3831.2 46.0 27/11/1998 17:15:17 981127DF.AAD +981127005 0.6 18676.4 10606.2 3761.9 4.4 27/11/1998 17:46:36 981127DF.AAF +981127006 0.2 19109.9 9716.5 3612.0 11.8 27/11/1998 19:29:51 981127DF.AAG +]]</span> + +<span class="keyword">local</span> t1 = data.read (t1f) +<span class="comment">-- column_by_name returns a List +</span>asserteq(t1:column_by_name <span class="string">'Magnitude'</span>,List{<span class="number">2</span>,<span class="number">0.8</span>,<span class="number">0.5</span>,<span class="number">0.6</span>,<span class="number">0.2</span>}) +<span class="comment">-- can use array.column as well +</span>asserteq(array.column(t1,<span class="number">2</span>),{<span class="number">2</span>,<span class="number">0.8</span>,<span class="number">0.5</span>,<span class="number">0.6</span>,<span class="number">0.2</span>}) + +<span class="comment">-- only numerical columns (deduced from first data row) are converted by default +</span><span class="comment">-- can look up indices in the list fieldnames. +</span><span class="keyword">local</span> EDI = t1.fieldnames:index <span class="string">'EventDate'</span> +<span class="global">assert</span>(<span class="global">type</span>(t1[<span class="number">1</span>][EDI]) == <span class="string">'string'</span>) + +<span class="comment">-- select method returns a sequence, in this case single-valued. +</span><span class="comment">-- (Note that seq.copy returns a List) +</span>asserteq(seq(t1:<span class="global">select</span> <span class="string">'LocationX where Magnitude > 0.5'</span>):copy(),List{<span class="number">18988.4</span>,<span class="number">19104</span>,<span class="number">18676.4</span>}) + +<span class="comment">--[[ +--a common select usage pattern: +for event,mag in t1:select 'EventID,Magnitude sort by Magnitude desc' do + print(event,mag) +end +--]]</span> + +<span class="comment">-- space-separated, but with last field containing spaces. +</span><span class="keyword">local</span> t2f = open <span class="string">[[ +USER PID %MEM %CPU COMMAND +sdonovan 2333 0.3 0.1 background --n=2 +root 2332 0.4 0.2 fred --start=yes +root 2338 0.2 0.1 backyard-process +]]</span> + +<span class="keyword">local</span> t2,err = data.read(t2f,{last_field_collect=<span class="keyword">true</span>}) +<span class="keyword">if</span> <span class="keyword">not</span> t2 <span class="keyword">then</span> <span class="keyword">return</span> <span class="global">print</span> (err) <span class="keyword">end</span> + +<span class="comment">-- the last_field_collect option is useful with space-delimited data where the last +</span><span class="comment">-- field may contain spaces. Otherwise, a record count mismatch should be an error! +</span><span class="keyword">local</span> lt2 = List(t2[<span class="number">2</span>]) +asserteq(lt2:join <span class="string">','</span>,<span class="string">'root,2332,0.4,0.2,fred --start=yes'</span>) + +<span class="comment">-- fieldnames are converted into valid identifiers by substituting _ +</span><span class="comment">-- (we do this to make select queries parseable by Lua) +</span>asserteq(t2.fieldnames,List{<span class="string">'USER'</span>,<span class="string">'PID'</span>,<span class="string">'_MEM'</span>,<span class="string">'_CPU'</span>,<span class="string">'COMMAND'</span>}) + +<span class="comment">-- select queries are NOT SQL so remember to use == ! (and no 'between' operator, sorry) +</span><span class="comment">--s,err = t2:select('_MEM where USER="root"') +</span><span class="comment">--assert(err == [[[string "tmp"]:9: unexpected symbol near '=']]) +</span> +<span class="keyword">local</span> s = t2:<span class="global">select</span>(<span class="string">'_MEM where USER=="root"'</span>) +<span class="global">assert</span>(s() == <span class="number">0.4</span>) +<span class="global">assert</span>(s() == <span class="number">0.2</span>) +<span class="global">assert</span>(s() == <span class="keyword">nil</span>) + +<span class="comment">-- CSV, Excel style. Double-quoted fields are allowed, and they may contain commas! +</span><span class="keyword">local</span> t3f = open <span class="string">[[ +"Department Name","Employee ID",Project,"Hours Booked" +sales,1231,overhead,4 +sales,1255,overhead,3 +engineering,1501,development,5 +engineering,1501,maintenance,3 +engineering,1433,maintenance,10 +]]</span> + +<span class="keyword">local</span> t3 = data.read(t3f,{csv=<span class="keyword">true</span>}) + +<span class="comment">-- although fieldnames are turned in valid Lua identifiers, there is always <code>original_fieldnames</code> +</span>asserteq(t3.fieldnames,List{<span class="string">'Department_Name'</span>,<span class="string">'Employee_ID'</span>,<span class="string">'Project'</span>,<span class="string">'Hours_Booked'</span>}) +asserteq(t3.original_fieldnames,List{<span class="string">'Department Name'</span>,<span class="string">'Employee ID'</span>,<span class="string">'Project'</span>,<span class="string">'Hours Booked'</span>}) + +<span class="comment">-- a common operation is to select using a given list of columns, and each row +</span><span class="comment">-- on some explicit condition. The select() method can take a table with these +</span><span class="comment">-- parameters +</span><span class="keyword">local</span> keepcols = {<span class="string">'Employee_ID'</span>,<span class="string">'Hours_Booked'</span>} + +<span class="keyword">local</span> q = t3:<span class="global">select</span> { fields = keepcols, + where = <span class="keyword">function</span>(row) <span class="keyword">return</span> row[<span class="number">1</span>]==<span class="string">'engineering'</span> <span class="keyword">end</span> + } + +asserteq(seq.copy2(q),{{<span class="number">1501</span>,<span class="number">5</span>},{<span class="number">1501</span>,<span class="number">3</span>},{<span class="number">1433</span>,<span class="number">10</span>}}) + +<span class="comment">-- another pattern is doing a select to restrict rows & columns, process some +</span><span class="comment">-- fields and write out the modified rows. +</span> +<span class="keyword">local</span> outf = stringio.create() + +<span class="keyword">local</span> names = {[<span class="number">1501</span>]=<span class="string">'don'</span>,[<span class="number">1433</span>]=<span class="string">'dilbert'</span>} + +t3:write_row (outf,{<span class="string">'Employee'</span>,<span class="string">'Hours_Booked'</span>}) +q = t3:select_row {fields=keepcols,where=func.Eq(func._1[<span class="number">1</span>],<span class="string">'engineering'</span>)} +<span class="keyword">for</span> row <span class="keyword">in</span> q <span class="keyword">do</span> + row[<span class="number">1</span>] = names[row[<span class="number">1</span>]] + t3:write_row(outf,row) +<span class="keyword">end</span> + +asserteq(outf:value(), +<span class="string">[[ +Employee,Hours_Booked +don,5 +don,3 +dilbert,10 +]]</span>) + +<span class="comment">-- data may not always have column headers. When creating a data object +</span><span class="comment">-- from a two-dimensional array, may specify the fieldnames, as a list or a string. +</span><span class="comment">-- The delimiter is deduced from the fieldname string, so a string just containing +</span><span class="comment">-- the delimiter will set it, and the fieldnames will be empty. +</span><span class="keyword">local</span> dat = List() +<span class="keyword">local</span> row = List.range(<span class="number">1</span>,<span class="number">10</span>) +<span class="keyword">for</span> i = <span class="number">1</span>,<span class="number">10</span> <span class="keyword">do</span> + dat:append(row:map(<span class="string">'*'</span>,i)) +<span class="keyword">end</span> +dat = data.new(dat,<span class="string">','</span>) +<span class="keyword">local</span> out = stringio.create() +dat:write(out,<span class="string">','</span>) +asserteq(out:value(), <span class="string">[[ +1,2,3,4,5,6,7,8,9,10 +2,4,6,8,10,12,14,16,18,20 +3,6,9,12,15,18,21,24,27,30 +4,8,12,16,20,24,28,32,36,40 +5,10,15,20,25,30,35,40,45,50 +6,12,18,24,30,36,42,48,54,60 +7,14,21,28,35,42,49,56,63,70 +8,16,24,32,40,48,56,64,72,80 +9,18,27,36,45,54,63,72,81,90 +10,20,30,40,50,60,70,80,90,100 +]]</span>) + +<span class="comment">-- you can always use numerical field indices, AWK-style; +</span><span class="comment">-- note how the copy_select method gives you a data object instead of an +</span><span class="comment">-- iterator over the fields +</span><span class="keyword">local</span> res = dat:copy_select <span class="string">'$1,$3 where $1 > 5'</span> +<span class="keyword">local</span> L = List +asserteq(L(res),L{ + L{<span class="number">6</span>, <span class="number">18</span>}, + L{<span class="number">7</span>,<span class="number">21</span>}, + L{<span class="number">8</span>,<span class="number">24</span>}, + L{<span class="number">9</span>,<span class="number">27</span>}, + L{<span class="number">10</span>,<span class="number">30</span>}, +}) + +<span class="comment">-- the column_by_name method may take a fieldname or an index +</span>asserteq(dat:column_by_name(<span class="number">2</span>), L{<span class="number">2</span>,<span class="number">4</span>,<span class="number">6</span>,<span class="number">8</span>,<span class="number">10</span>,<span class="number">12</span>,<span class="number">14</span>,<span class="number">16</span>,<span class="number">18</span>,<span class="number">20</span>}) + +<span class="comment">-- the field list may contain expressions or even constants +</span><span class="keyword">local</span> q = dat:<span class="global">select</span> <span class="string">'$3,2*$4 where $1 == 8'</span> +asserteq(T(q()),T(<span class="number">24</span>,<span class="number">64</span>)) + +dat,err = data.read(open <span class="string">[[ +1.0 0.1 +0.2 1.3 +]]</span>) + +<span class="keyword">if</span> err <span class="keyword">then</span> <span class="global">print</span>(err) <span class="keyword">end</span> + +<span class="comment">-- if a method cannot be found, then we look up in array2d +</span><span class="comment">-- array2d.flatten(t) makes a 1D list out of a 2D array, +</span><span class="comment">-- and then List.minmax() gets the extrema. +</span> +asserteq(T(dat:flatten():minmax()),T(<span class="number">0.1</span>,<span class="number">1.3</span>)) + +<span class="keyword">local</span> f = open <span class="string">[[ +Time Message +1266840760 +# EE7C0600006F0D00C00F06010302054000000308010A00002B00407B00 +1266840760 closure data 0.000000 1972 1972 0 +1266840760 ++ 1266840760 EE 1 +1266840760 +# EE7C0600006F0D00C00F06010302054000000408020A00002B00407B00 +1266840764 closure data 0.000000 1972 1972 0 +1266840764 ++ 1266840764 EE 1 +1266840764 +# EE7C0600006F0D00C00F06010302054000000508030A00002B00407B00 +1266840768 duplicate? +1266840768 +# EE7C0600006F0D00C00F06010302054000000508030A00002B00407B00 +1266840768 closure data 0.000000 1972 1972 0 +]]</span> + +<span class="comment">-- the <code>convert</code> option provides custom converters for each specified column. +</span><span class="comment">-- Here we convert the timestamps into Date objects and collect everything +</span><span class="comment">-- else into one field +</span><span class="keyword">local</span> Date = <span class="global">require</span> <span class="string">'pl.Date'</span> + +<span class="keyword">local</span> <span class="keyword">function</span> date_convert (ds) + <span class="keyword">return</span> Date(<span class="global">tonumber</span>(ds)) +<span class="keyword">end</span> + +<span class="keyword">local</span> d = data.read(f,{convert={[<span class="number">1</span>]=date_convert},last_field_collect=<span class="keyword">true</span>}) + +asserteq(#d[<span class="number">1</span>],<span class="number">2</span>) +asserteq(d[<span class="number">2</span>][<span class="number">1</span>]:year(),<span class="number">2010</span>) + +d = {{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>},{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>}} +out = stringio.create() +data.write(d,out,{<span class="string">'A'</span>,<span class="string">'B'</span>,<span class="string">'C'</span>},<span class="string">','</span>) +asserteq(out:value(), +<span class="string">[[ +A,B,C +1,2,3 +10,20,30 +]]</span>) + +out = stringio.create() +d.fieldnames = {<span class="string">'A'</span>,<span class="string">'B'</span>,<span class="string">'C'</span>} +data.write(d,out) + +asserteq(out:value(), +<span class="string">[[ +A B C +1 2 3 +10 20 30 +]]</span>) + + +d = data.read(stringio.open <span class="string">'One,Two\n1,\n,20\n'</span>,{csv=<span class="keyword">true</span>}) +asserteq(d,{ + {<span class="number">1</span>,<span class="number">0</span>},{<span class="number">0</span>,<span class="number">20</span>}, + original_fieldnames={<span class="string">"One"</span>,<span class="string">"Two"</span>},fieldnames={<span class="string">"One"</span>,<span class="string">"Two"</span>},delim=<span class="string">","</span> +})</pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/test-listcallbacks.lua.html b/Data/Libraries/Penlight/docs/examples/test-listcallbacks.lua.html new file mode 100644 index 0000000..4416532 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/test-listcallbacks.lua.html @@ -0,0 +1,138 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><strong>test-listcallbacks.lua</strong></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>test-listcallbacks.lua</h2> +<pre> +<span class="comment">-- demonstrates how to use a list of callbacks +</span><span class="keyword">local</span> List = <span class="global">require</span> <span class="string">'pl.List'</span> +<span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">'pl.utils'</span> +<span class="keyword">local</span> actions = List() +<span class="keyword">local</span> L = utils.string_lambda + +actions:append(<span class="keyword">function</span>() <span class="global">print</span> <span class="string">'hello'</span> <span class="keyword">end</span>) +actions:append(L <span class="string">'|| print "yay"'</span>) + +<span class="comment">-- '()' is a shortcut for operator.call or function(x) return x() end +</span>actions:foreach <span class="string">'()'</span></pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/test-pretty.lua.html b/Data/Libraries/Penlight/docs/examples/test-pretty.lua.html new file mode 100644 index 0000000..a7493f3 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/test-pretty.lua.html @@ -0,0 +1,140 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><strong>test-pretty.lua</strong></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>test-pretty.lua</h2> +<pre> +<span class="keyword">local</span> pretty = <span class="global">require</span> <span class="string">'pl.pretty'</span> + +<span class="keyword">local</span> tb = { + <span class="string">'one'</span>,<span class="string">'two'</span>,<span class="string">'three'</span>,{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}, + alpha=<span class="number">1</span>,beta=<span class="number">2</span>,gamma=<span class="number">3</span>,[<span class="string">'&'</span>]=<span class="keyword">true</span>,[<span class="number">0</span>]=<span class="keyword">false</span>, + _fred = {<span class="keyword">true</span>,<span class="keyword">true</span>}, + s = <span class="string">[[ +hello dolly +you're so fine +]]</span> +} + +<span class="global">print</span>(pretty.write(tb))</pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/test-symbols.lua.html b/Data/Libraries/Penlight/docs/examples/test-symbols.lua.html new file mode 100644 index 0000000..a847ab9 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/test-symbols.lua.html @@ -0,0 +1,209 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><strong>test-symbols.lua</strong></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>test-symbols.lua</h2> +<pre> +<span class="global">require</span> <span class="string">'pl'</span> +<span class="comment">-- force us to look in the script's directory when requiring... +</span>app.require_here() +<span class="global">require</span> <span class="string">'symbols'</span> + +<span class="keyword">local</span> MT = <span class="global">getmetatable</span>(_1) + +add = MT.__add +mul = MT.__mul +pow = MT.__pow + + +<span class="keyword">function</span> testeq (e1,e2) + <span class="keyword">if</span> <span class="keyword">not</span> equals(e1,e2) <span class="keyword">then</span> + <span class="global">print</span> (<span class="string">'Not equal'</span>,repr(e1),repr(e2)) + <span class="keyword">end</span> +<span class="keyword">end</span> + +sin = register(<span class="global">math</span>.sin,<span class="string">'sin'</span>) + +f = register(<span class="keyword">function</span>(x,y,z) <span class="keyword">end</span>) + +<span class="comment">--[[ +testeq (_1,_1) +testeq (_1+_2,_1+_2) +testeq (_1 + 3*_2,_1 + 3*_2) +testeq (_2+_1,_1+_2) +testeq (sin(_1),sin(_1)) +testeq (1+f(10,20,'ok'),f(10,20,'ok')+1) +--]]</span> + + +<span class="keyword">function</span> testexpand (e) + <span class="global">print</span>(repr(fold(expand(e)))) <span class="comment">--fold +</span><span class="keyword">end</span> + +<span class="comment">--[[ +testexpand (a*(a+1)) + +testexpand ((x+2)*(b+1)) +]]</span><span class="comment">-- +</span> +<span class="keyword">function</span> testfold (e) + <span class="global">print</span>(repr(fold(e))) +<span class="keyword">end</span> + +a,b,c,x,y = Var <span class="string">'a,b,c,x,y'</span> + +<span class="comment">--~ testfold(_1 + _2) +</span><span class="comment">--~ testfold(add(10,20)) +</span><span class="comment">--~ testfold(add(mul(2,_1),mul(3,_2))) +</span><span class="comment">--[[ +testfold(sin(a)) +e = a^(b+2) +testfold(e) +bindval(b,1) +testfold(e) +bindval(a,2) +testfold(e) + +bindval(a) +bindval(b) +]]</span> + + + +<span class="keyword">function</span> testdiff (e) + balance(e) + e = diff(e,x) + balance(e) + <span class="global">print</span>(<span class="string">'+ '</span>,e) + e = fold(e) + <span class="global">print</span>(<span class="string">'- '</span>,e) +<span class="keyword">end</span> + + +testdiff(x^<span class="number">2</span>+<span class="number">1</span>) +testdiff(<span class="number">3</span>*x^<span class="number">2</span>) +testdiff(x^<span class="number">2</span> + <span class="number">2</span>*x^<span class="number">3</span>) +testdiff(x^<span class="number">2</span> + <span class="number">2</span>*a*x^<span class="number">3</span> + x^<span class="number">4</span>) +testdiff(<span class="number">2</span>*a*x^<span class="number">3</span>) +testdiff(x*x*x)</pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/testapp.lua.html b/Data/Libraries/Penlight/docs/examples/testapp.lua.html new file mode 100644 index 0000000..56ae5a3 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/testapp.lua.html @@ -0,0 +1,133 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><strong>testapp.lua</strong></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.Set.html">pl.Set</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>testapp.lua</h2> +<pre> +<span class="comment">-- shows how a script can get a private file path +</span><span class="comment">-- the output on my Windows machine is: +</span><span class="comment">-- C:\Documents and Settings\steve\.testapp\test.txt +</span><span class="keyword">local</span> app = <span class="global">require</span> <span class="string">'pl.app'</span> +<span class="global">print</span>(app.appfile <span class="string">'test.txt'</span>)</pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2018-11-23 21:07:42 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/testclone.lua.html b/Data/Libraries/Penlight/docs/examples/testclone.lua.html new file mode 100644 index 0000000..1e48839 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/testclone.lua.html @@ -0,0 +1,165 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><strong>testclone.lua</strong></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>testclone.lua</h2> +<pre> +<span class="comment">--cloning a directory tree. +</span><span class="keyword">local</span> lfs = <span class="global">require</span> <span class="string">'lfs'</span> +<span class="keyword">local</span> path = <span class="global">require</span> <span class="string">'pl.path'</span> +<span class="keyword">local</span> dir = <span class="global">require</span> <span class="string">'pl.dir'</span> + +<span class="keyword">local</span> p1 = <span class="string">[[examples]]</span> +<span class="keyword">local</span> p2 = <span class="string">[[copy/of/examples]]</span> + +<span class="keyword">if</span> <span class="keyword">not</span> path.isfile <span class="string">'examples/testclone.lua'</span> <span class="keyword">then</span> + <span class="keyword">return</span> <span class="global">print</span> <span class="string">'please run this in the penlight folder (below examples)'</span> +<span class="keyword">end</span> + +<span class="comment">-- make a copy of the examples folder +</span>dir.clonetree(p1,p2,dir.copyfile) + +<span class="global">assert</span>(path.isdir <span class="string">'copy'</span>) + +<span class="global">print</span> <span class="string">'---'</span> +<span class="keyword">local</span> t = <span class="global">os</span>.time() +<span class="global">print</span>(lfs.touch(<span class="string">'examples/testclone.lua'</span>,t,t+<span class="number">10</span>)) + +<span class="comment">-- this should only update this file +</span>dir.clonetree(p1,p2, +<span class="keyword">function</span>(f1,f2) + <span class="keyword">local</span> t1 = path.getmtime(f1) + <span class="keyword">local</span> t2 = path.getmtime(f2) + <span class="comment">--print(f1,t1,f2,t2) +</span> <span class="keyword">if</span> t1 > t2 <span class="keyword">then</span> + dir.copyfile(f1,f2) + <span class="global">print</span>(f1,f2,t1,t2) + <span class="keyword">end</span> + <span class="keyword">return</span> <span class="keyword">true</span> +<span class="keyword">end</span>) + +<span class="comment">-- and get rid of the whole copy directory, with subdirs +</span>dir.rmtree <span class="string">'copy'</span> + +<span class="global">assert</span>(<span class="keyword">not</span> path.exists <span class="string">'copy'</span>)</pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/testconfig.lua.html b/Data/Libraries/Penlight/docs/examples/testconfig.lua.html new file mode 100644 index 0000000..4ba947f --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/testconfig.lua.html @@ -0,0 +1,176 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><strong>testconfig.lua</strong></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>testconfig.lua</h2> +<pre> +<span class="keyword">local</span> stringio = <span class="global">require</span> <span class="string">'pl.stringio'</span> +<span class="keyword">local</span> config = <span class="global">require</span> <span class="string">'pl.config'</span> + +<span class="keyword">local</span> <span class="keyword">function</span> dump(t,indent) + <span class="keyword">if</span> <span class="global">type</span>(t) == <span class="string">'table'</span> <span class="keyword">then</span> + <span class="global">io</span>.write(indent,<span class="string">'{\n'</span>) + <span class="keyword">local</span> newindent = indent..<span class="string">' '</span> + <span class="keyword">for</span> k,v <span class="keyword">in</span> <span class="global">pairs</span>(t) <span class="keyword">do</span> + <span class="global">io</span>.write(newindent,k,<span class="string">'='</span>) + dump(v,indent) + <span class="global">io</span>.write(<span class="string">'\n'</span>) + <span class="keyword">end</span> + <span class="global">io</span>.write(newindent,<span class="string">'},\n'</span>) + <span class="keyword">else</span> + <span class="global">io</span>.write(indent,t,<span class="string">'('</span>,<span class="global">type</span>(t),<span class="string">')'</span>) + <span class="keyword">end</span> +<span class="keyword">end</span> + + +<span class="keyword">local</span> <span class="keyword">function</span> testconfig(test) + <span class="keyword">local</span> f = stringio.open(test) + <span class="keyword">local</span> c = config.read(f) + f:close() + dump(c,<span class="string">' '</span>) + <span class="global">print</span> <span class="string">'-----'</span> +<span class="keyword">end</span> + +testconfig <span class="string">[[ + ; comment 2 (an ini file) +[section!] +bonzo.dog=20,30 +config_parm=here we go again +depth = 2 +[another] +felix="cat" +]]</span> + +testconfig <span class="string">[[ +# this is a more Unix-y config file +fred = 1 +alice = 2 +home = /bonzo/dog/etc +]]</span> + +testconfig <span class="string">[[ +# this is just a set of comma-separated values +1000,444,222 +44,555,224 +]]</span></pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/testglobal.lua.html b/Data/Libraries/Penlight/docs/examples/testglobal.lua.html new file mode 100644 index 0000000..e05a1f8 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/testglobal.lua.html @@ -0,0 +1,153 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><strong>testglobal.lua</strong></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>testglobal.lua</h2> +<pre> +<span class="comment">-- very simple lexer program which looks at all identifiers in a Lua +</span><span class="comment">-- file and checks whether they're in the global namespace. +</span><span class="comment">-- At the end, we dump out the result of count_map, which will give us +</span><span class="comment">-- unique identifiers with their usage count. +</span><span class="comment">-- (an example of a program which itself needs to be careful about what +</span><span class="comment">-- goes into the global namespace) +</span> +<span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">'pl.utils'</span> +<span class="keyword">local</span> file = <span class="global">require</span> <span class="string">'pl.file'</span> +<span class="keyword">local</span> lexer = <span class="global">require</span> <span class="string">'pl.lexer'</span> +<span class="keyword">local</span> List = <span class="global">require</span> <span class="string">'pl.List'</span> +<span class="keyword">local</span> pretty = <span class="global">require</span> <span class="string">'pl.pretty'</span> +<span class="keyword">local</span> seq = <span class="global">require</span> <span class="string">'pl.seq'</span> +<span class="keyword">local</span> path = <span class="global">require</span> <span class="string">'pl.path'</span> + +utils.on_error <span class="string">'quit'</span> + +<span class="keyword">local</span> txt = file.read(arg[<span class="number">1</span>] <span class="keyword">or</span> path.normpath(<span class="string">'examples/testglobal.lua'</span>)) +<span class="keyword">local</span> globals = List() +<span class="keyword">for</span> t,v <span class="keyword">in</span> lexer.lua(txt) <span class="keyword">do</span> + <span class="keyword">if</span> t == <span class="string">'iden'</span> <span class="keyword">and</span> <span class="global">rawget</span>(_G,v) <span class="keyword">then</span> + globals:append(v) + <span class="keyword">end</span> +<span class="keyword">end</span> + +pretty.dump(seq.count_map(globals))</pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/testinputfields.lua.html b/Data/Libraries/Penlight/docs/examples/testinputfields.lua.html new file mode 100644 index 0000000..0fa63e4 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/testinputfields.lua.html @@ -0,0 +1,140 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><strong>testinputfields.lua</strong></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>testinputfields.lua</h2> +<pre> +<span class="keyword">local</span> input = <span class="global">require</span> <span class="string">'pl.input'</span> +<span class="keyword">local</span> sum = <span class="number">0.0</span> +<span class="keyword">local</span> count = <span class="number">0</span> +<span class="keyword">local</span> text = <span class="string">[[ + 981124001 2.0 18988.4 10047.1 4149.7 + 981125001 0.8 19104.0 9970.4 5088.7 + 981127003 0.5 19012.5 9946.9 3831.2 +]]</span> +<span class="keyword">for</span> id,magn,x <span class="keyword">in</span> input.fields(<span class="number">3</span>,<span class="string">' '</span>,text) <span class="keyword">do</span> + sum = sum + x + count = count + <span class="number">1</span> +<span class="keyword">end</span> +<span class="global">print</span>(<span class="string">'average x coord is '</span>,sum/count)</pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/testinputfields2.lua.html b/Data/Libraries/Penlight/docs/examples/testinputfields2.lua.html new file mode 100644 index 0000000..51a5acf --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/testinputfields2.lua.html @@ -0,0 +1,136 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><strong>testinputfields2.lua</strong></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>testinputfields2.lua</h2> +<pre> +<span class="keyword">local</span> input = <span class="global">require</span> <span class="string">'pl.input'</span> +<span class="keyword">local</span> seq = <span class="global">require</span> <span class="string">'pl.seq'</span> +<span class="keyword">local</span> text = <span class="string">[[ + 981124001 2.0 18988.4 10047.1 4149.7 + 981125001 0.8 19104.0 9970.4 5088.7 + 981127003 0.5 19012.5 9946.9 3831.2 +]]</span> +<span class="keyword">local</span> sum,count = seq.sum(input.fields ({<span class="number">3</span>},<span class="string">' '</span>,text)) +<span class="global">print</span>(sum/count)</pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/testxml.lua.html b/Data/Libraries/Penlight/docs/examples/testxml.lua.html new file mode 100644 index 0000000..c01fe3b --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/testxml.lua.html @@ -0,0 +1,209 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><strong>testxml.lua</strong></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>testxml.lua</h2> +<pre> +<span class="comment">-- an example showing 'pl.lexer' doing some serious work. +</span><span class="comment">-- The resulting Lua table is in the same LOM format used by luaexpat. +</span><span class="comment">-- This is (clearly) not a professional XML parser, so don't use it +</span><span class="comment">-- on your homework! +</span> +<span class="keyword">local</span> lexer = <span class="global">require</span> <span class="string">'pl.lexer'</span> +<span class="keyword">local</span> pretty = <span class="global">require</span> <span class="string">'pl.pretty'</span> + +<span class="keyword">local</span> append = <span class="global">table</span>.insert +<span class="keyword">local</span> skipws,expecting = lexer.skipws,lexer.expecting + +<span class="keyword">local</span> <span class="keyword">function</span> parse_element (tok,tag) + <span class="keyword">local</span> tbl,t,v,attrib + tbl = {} + tbl.tag = tag <span class="comment">-- LOM 'tag' is the element tag +</span> t,v = skipws(tok) + <span class="keyword">while</span> v ~= <span class="string">'/'</span> <span class="keyword">and</span> v ~= <span class="string">'>'</span> <span class="keyword">do</span> + <span class="keyword">if</span> t ~= <span class="string">'iden'</span> <span class="keyword">then</span> <span class="global">error</span>(<span class="string">'expecting attribute identifier'</span>) <span class="keyword">end</span> + attrib = v + expecting(tok,<span class="string">'='</span>) + v = expecting(tok,<span class="string">'string'</span>) + <span class="comment">-- LOM: 'attr' subtable contains attrib/value pairs and an ordered list of attribs +</span> <span class="keyword">if</span> <span class="keyword">not</span> tbl.attr <span class="keyword">then</span> tbl.attr = {} <span class="keyword">end</span> + tbl.attr[attrib] = v + append(tbl.attr,attrib) + t,v = skipws(tok) + <span class="keyword">end</span> + <span class="keyword">if</span> v == <span class="string">'/'</span> <span class="keyword">then</span> + expecting(tok,<span class="string">'>'</span>) + <span class="keyword">return</span> tbl + <span class="keyword">end</span> + <span class="comment">-- pick up element data +</span> t,v = tok() + <span class="keyword">while</span> <span class="keyword">true</span> <span class="keyword">do</span> + <span class="keyword">if</span> t == <span class="string">'<'</span> <span class="keyword">then</span> + t,v = skipws(tok) + <span class="keyword">if</span> t == <span class="string">'/'</span> <span class="keyword">then</span> <span class="comment">-- element end tag +</span> t,v = tok() + <span class="keyword">if</span> t == <span class="string">'>'</span> <span class="keyword">then</span> <span class="keyword">return</span> tbl <span class="keyword">end</span> + <span class="keyword">if</span> t == <span class="string">'iden'</span> <span class="keyword">and</span> v == tag <span class="keyword">then</span> + <span class="keyword">if</span> tok() == <span class="string">'>'</span> <span class="keyword">then</span> <span class="keyword">return</span> tbl <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="global">error</span>(<span class="string">'expecting end tag '</span>..tag) + <span class="keyword">else</span> + append(tbl,parse_element(tok,v)) <span class="comment">-- LOM: child elements added to table +</span> t,v = skipws(tok) + <span class="keyword">end</span> + <span class="keyword">else</span> + append(tbl,v) <span class="comment">-- LOM: text added to table +</span> t,v = skipws(tok) + <span class="keyword">end</span> + <span class="keyword">end</span> +<span class="keyword">end</span> + +<span class="keyword">local</span> <span class="keyword">function</span> parse_xml (tok) + <span class="keyword">local</span> t = skipws(tok) + <span class="keyword">local</span> v + <span class="keyword">while</span> t == <span class="string">'<'</span> <span class="keyword">do</span> + t,v = tok() + <span class="keyword">if</span> t == <span class="string">'?'</span> <span class="keyword">or</span> t == <span class="string">'!'</span> <span class="keyword">then</span> + <span class="comment">-- skip meta stuff and commentary +</span> <span class="keyword">repeat</span> t = tok() <span class="keyword">until</span> t == <span class="string">'>'</span> + t = expecting(tok,<span class="string">'<'</span>) + <span class="keyword">else</span> + <span class="keyword">return</span> parse_element(tok,v) + <span class="keyword">end</span> + <span class="keyword">end</span> +<span class="keyword">end</span> + +<span class="keyword">local</span> s = <span class="string">[[ +<?xml version="1.0" encoding="UTF-8"?> +<sensor name="closure-meter-2" id="7D7D0600006F0D00" loc="100,100,0" device="closure-meter" init="true"> +<detector name="closure-meter" phenomenon="closure" units="mm" id="1" + vmin="0" vmax="5000" device="closure-meter" calib="0,0;5000,5000" + sampling_interval="25000" measurement_interval="600000" +/> +</sensor> +]]</span> + +<span class="keyword">local</span> tok = lexer.scan(s,<span class="keyword">nil</span>,{space=<span class="keyword">false</span>},{<span class="global">string</span>=<span class="keyword">true</span>}) +<span class="keyword">local</span> res = parse_xml(tok) +<span class="global">print</span>(pretty.write(res))</pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/examples/which.lua.html b/Data/Libraries/Penlight/docs/examples/which.lua.html new file mode 100644 index 0000000..d5e8961 --- /dev/null +++ b/Data/Libraries/Penlight/docs/examples/which.lua.html @@ -0,0 +1,155 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><strong>which.lua</strong></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> + +</div> + +<div id="content"> + + <h2>which.lua</h2> +<pre> +<span class="comment">-- a simple implementation of the which command. This looks for +</span><span class="comment">-- the given file on the path. On windows, it will assume an extension +</span><span class="comment">-- of .exe if no extension is given. +</span><span class="keyword">local</span> List = <span class="global">require</span> <span class="string">'pl.List'</span> +<span class="keyword">local</span> path = <span class="global">require</span> <span class="string">'pl.path'</span> +<span class="keyword">local</span> app = <span class="global">require</span> <span class="string">'pl.app'</span> + +<span class="keyword">local</span> pathl = List.split(<span class="global">os</span>.getenv <span class="string">'PATH'</span>,path.dirsep) + +<span class="keyword">local</span> <span class="keyword">function</span> which (file) + <span class="keyword">local</span> res = pathl:map(path.join,file) + res = res:filter(path.exists) + <span class="keyword">if</span> res <span class="keyword">then</span> <span class="keyword">return</span> res[<span class="number">1</span>] <span class="keyword">end</span> +<span class="keyword">end</span> + +<span class="keyword">local</span> _,lua = app.lua() +<span class="keyword">local</span> file = arg[<span class="number">1</span>] <span class="keyword">or</span> lua <span class="comment">-- i.e. location of lua executable +</span><span class="keyword">local</span> try + +<span class="keyword">if</span> <span class="keyword">not</span> file <span class="keyword">then</span> <span class="keyword">return</span> <span class="global">print</span> <span class="string">'must provide a filename'</span> <span class="keyword">end</span> + +<span class="keyword">if</span> path.extension(file) == <span class="string">''</span> <span class="keyword">and</span> path.is_windows <span class="keyword">then</span> + try = which(file..<span class="string">'.exe'</span>) +<span class="keyword">else</span> + try = which(file) +<span class="keyword">end</span> + +<span class="keyword">if</span> try <span class="keyword">then</span> <span class="global">print</span>(try) <span class="keyword">else</span> <span class="global">print</span> <span class="string">'cannot find on path'</span> <span class="keyword">end</span></pre> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/index.html b/Data/Libraries/Penlight/docs/index.html new file mode 100644 index 0000000..b7e9465 --- /dev/null +++ b/Data/Libraries/Penlight/docs/index.html @@ -0,0 +1,392 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="index.html">Documentation</a></li> +</ul> + + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="libraries/pl.html">pl</a></li> + <li><a href="libraries/pl.app.html">pl.app</a></li> + <li><a href="libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="libraries/pl.class.html">pl.class</a></li> + <li><a href="libraries/pl.compat.html">pl.compat</a></li> + <li><a href="libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="libraries/pl.config.html">pl.config</a></li> + <li><a href="libraries/pl.data.html">pl.data</a></li> + <li><a href="libraries/pl.dir.html">pl.dir</a></li> + <li><a href="libraries/pl.file.html">pl.file</a></li> + <li><a href="libraries/pl.func.html">pl.func</a></li> + <li><a href="libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="libraries/pl.input.html">pl.input</a></li> + <li><a href="libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="libraries/pl.operator.html">pl.operator</a></li> + <li><a href="libraries/pl.path.html">pl.path</a></li> + <li><a href="libraries/pl.permute.html">pl.permute</a></li> + <li><a href="libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="libraries/pl.seq.html">pl.seq</a></li> + <li><a href="libraries/pl.sip.html">pl.sip</a></li> + <li><a href="libraries/pl.strict.html">pl.strict</a></li> + <li><a href="libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="libraries/pl.template.html">pl.template</a></li> + <li><a href="libraries/pl.test.html">pl.test</a></li> + <li><a href="libraries/pl.text.html">pl.text</a></li> + <li><a href="libraries/pl.types.html">pl.types</a></li> + <li><a href="libraries/pl.url.html">pl.url</a></li> + <li><a href="libraries/pl.utils.html">pl.utils</a></li> + <li><a href="libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="classes/pl.Date.html">pl.Date</a></li> + <li><a href="classes/pl.List.html">pl.List</a></li> + <li><a href="classes/pl.Map.html">pl.Map</a></li> + <li><a href="classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="manual/01-introduction.md.html">Introduction</a></li> + <li><a href="manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="manual/05-dates.md.html">Date and Time</a></li> + <li><a href="manual/06-data.md.html">Data</a></li> + <li><a href="manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + + + <h2>Penlight Lua Libraries 1.11.0</h2> + <p>Penlight is a set of pure Lua libraries for making it easier to work with common tasks like iterating over directories, reading configuration files and the like. Provides functional operations on tables and sequences. Visit the <a href="https://github.com/lunarmodules/Penlight">GitHub project</a> to review the code or file issues. Skip to the <a href="manual/01-introduction.md.html#">introduction</a>.</p> + +<h2>Libraries</h2> +<table class="module_list"> + <tr> + <td class="name" nowrap><a href="libraries/pl.html">pl</a></td> + <td class="summary">Entry point for loading all PL libraries only on demand, into the global space.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.app.html">pl.app</a></td> + <td class="summary">Application support functions.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.array2d.html">pl.array2d</a></td> + <td class="summary">Operations on two-dimensional arrays.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.class.html">pl.class</a></td> + <td class="summary">Provides a reuseable and convenient framework for creating classes in Lua.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.compat.html">pl.compat</a></td> + <td class="summary">Lua 5.1/5.2/5.3 compatibility.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.comprehension.html">pl.comprehension</a></td> + <td class="summary">List comprehensions implemented in Lua.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.config.html">pl.config</a></td> + <td class="summary">Reads configuration files into a Lua table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.data.html">pl.data</a></td> + <td class="summary">Reading and querying simple tabular data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.dir.html">pl.dir</a></td> + <td class="summary">Listing files in directories and creating/removing directory paths.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.file.html">pl.file</a></td> + <td class="summary">File manipulation functions: reading, writing, moving and copying.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.func.html">pl.func</a></td> + <td class="summary">Functional helpers like composition, binding and placeholder expressions.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.import_into.html">pl.import_into</a></td> + <td class="summary">PL loader, for loading all PL libraries, only on demand.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.input.html">pl.input</a></td> + <td class="summary">Iterators for extracting words or numbers from an input source.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.lapp.html">pl.lapp</a></td> + <td class="summary">Simple command-line parsing using human-readable specification.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.lexer.html">pl.lexer</a></td> + <td class="summary">Lexical scanner for creating a sequence of tokens from text.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.luabalanced.html">pl.luabalanced</a></td> + <td class="summary">Extract delimited Lua sequences from strings.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.operator.html">pl.operator</a></td> + <td class="summary">Lua operators available as functions.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.path.html">pl.path</a></td> + <td class="summary">Path manipulation and file queries.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.permute.html">pl.permute</a></td> + <td class="summary">Permutation operations.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.pretty.html">pl.pretty</a></td> + <td class="summary">Pretty-printing Lua tables.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.seq.html">pl.seq</a></td> + <td class="summary">Manipulating iterators as sequences.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.sip.html">pl.sip</a></td> + <td class="summary">Simple Input Patterns (SIP).</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.strict.html">pl.strict</a></td> + <td class="summary">Checks uses of undeclared global variables.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.stringio.html">pl.stringio</a></td> + <td class="summary">Reading and writing strings using file-like objects.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.stringx.html">pl.stringx</a></td> + <td class="summary">Python-style extended string library.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.tablex.html">pl.tablex</a></td> + <td class="summary">Extended operations on Lua tables.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.template.html">pl.template</a></td> + <td class="summary">A template preprocessor.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.test.html">pl.test</a></td> + <td class="summary">Useful test utilities.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.text.html">pl.text</a></td> + <td class="summary">Text processing utilities.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.types.html">pl.types</a></td> + <td class="summary">Dealing with Detailed Type Information</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.url.html">pl.url</a></td> + <td class="summary">Python-style URL quoting library.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.utils.html">pl.utils</a></td> + <td class="summary">Generally useful routines.</td> + </tr> + <tr> + <td class="name" nowrap><a href="libraries/pl.xml.html">pl.xml</a></td> + <td class="summary">XML LOM Utilities.</td> + </tr> +</table> +<h2>Classes</h2> +<table class="module_list"> + <tr> + <td class="name" nowrap><a href="classes/pl.Date.html">pl.Date</a></td> + <td class="summary">Date and Date Format classes.</td> + </tr> + <tr> + <td class="name" nowrap><a href="classes/pl.List.html">pl.List</a></td> + <td class="summary">Python-style list class.</td> + </tr> + <tr> + <td class="name" nowrap><a href="classes/pl.Map.html">pl.Map</a></td> + <td class="summary">A Map class.</td> + </tr> + <tr> + <td class="name" nowrap><a href="classes/pl.MultiMap.html">pl.MultiMap</a></td> + <td class="summary">MultiMap, a Map which has multiple values per key.</td> + </tr> + <tr> + <td class="name" nowrap><a href="classes/pl.OrderedMap.html">pl.OrderedMap</a></td> + <td class="summary">OrderedMap, a map which preserves ordering.</td> + </tr> + <tr> + <td class="name" nowrap><a href="classes/pl.Set.html">pl.Set</a></td> + <td class="summary">A Set class.</td> + </tr> +</table> +<h2>Manual</h2> +<table class="module_list"> + <tr> + <td class="name" nowrap><a href="manual/01-introduction.md.html">01-introduction.md</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="manual/02-arrays.md.html">02-arrays.md</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="manual/03-strings.md.html">03-strings.md</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="manual/04-paths.md.html">04-paths.md</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="manual/05-dates.md.html">05-dates.md</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="manual/06-data.md.html">06-data.md</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="manual/07-functional.md.html">07-functional.md</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="manual/08-additional.md.html">08-additional.md</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="manual/09-discussion.md.html">09-discussion.md</a></td> + <td class="summary"></td> + </tr> +</table> +<h2>Examples</h2> +<table class="module_list"> + <tr> + <td class="name" nowrap><a href="examples/seesubst.lua.html">seesubst.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/sipscan.lua.html">sipscan.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/symbols.lua.html">symbols.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/test-cmp.lua.html">test-cmp.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/test-data.lua.html">test-data.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/test-pretty.lua.html">test-pretty.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/test-symbols.lua.html">test-symbols.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/testclone.lua.html">testclone.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/testconfig.lua.html">testconfig.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/testglobal.lua.html">testglobal.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/testinputfields.lua.html">testinputfields.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/testinputfields2.lua.html">testinputfields2.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/testxml.lua.html">testxml.lua</a></td> + <td class="summary"></td> + </tr> + <tr> + <td class="name" nowrap><a href="examples/which.lua.html">which.lua</a></td> + <td class="summary"></td> + </tr> +</table> + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/ldoc_fixed.css b/Data/Libraries/Penlight/docs/ldoc_fixed.css new file mode 100644 index 0000000..9b0fc00 --- /dev/null +++ b/Data/Libraries/Penlight/docs/ldoc_fixed.css @@ -0,0 +1,311 @@ +/* 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 { + 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: #ffffff; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#main { + background-color:#FFFFFF; // #f0f0f0; + border-left: 1px solid #cccccc; +} + +#navigation { + position: fixed; + top: 0; + left: 0; + float: left; + width: 14em; + vertical-align: top; + background-color:#FFFFFF; // #f0f0f0; + border-right: 2px solid #cccccc; + overflow: visible; + overflow-y: scroll; + height: 100%; + padding-left: 1em; +} + +#navigation h2 { + background-color:#FFFFFF;//:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align: left; + padding:0.2em; + 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; + padding-left: 2em; + width: 700px; + border-left: 2px solid #cccccc; + // border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + padding-left: 1em; + margin-left: 14em; // avoid the damn sidebar! + border-top: 2px solid #cccccc; + border-left: 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: #f6f6ff; ; min-width: 200px; } +table.function_list td.summary { width: 100%; } + +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;} + +ul.nowrap { + overflow:auto; + whitespace:nowrap; +} + +/* 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/Data/Libraries/Penlight/docs/libraries/pl.Set.html b/Data/Libraries/Penlight/docs/libraries/pl.Set.html new file mode 100644 index 0000000..8eda41e --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.Set.html @@ -0,0 +1,650 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +<li><a href="#metamethods">metamethods</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><strong>pl.Set</strong></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testapp.lua.html">testapp.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.Set</code></h1> +<p>A Set class.</p> +<p> + +<pre> +> Set = <span class="global">require</span> <span class="string">'pl.Set'</span> +> = Set{<span class="string">'one'</span>,<span class="string">'two'</span>} == Set{<span class="string">'two'</span>,<span class="string">'one'</span>} +<span class="keyword">true</span> +> fruit = Set{<span class="string">'apple'</span>,<span class="string">'banana'</span>,<span class="string">'orange'</span>} +> = fruit[<span class="string">'banana'</span>] +<span class="keyword">true</span> +> = fruit[<span class="string">'hazelnut'</span>] +<span class="keyword">nil</span> +> colours = Set{<span class="string">'red'</span>,<span class="string">'orange'</span>,<span class="string">'green'</span>,<span class="string">'blue'</span>} +> = fruit,colours +[apple,orange,banana] [blue,green,orange,red] +> = fruit+colours +[blue,green,apple,red,orange,banana] +[orange] +> more_fruits = fruit + <span class="string">'apricot'</span> +> = fruit*colours + = more_fruits, fruit +banana,apricot,apple,orange] [banana,apple,orange] +</pre> + + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.tablex.html#">pl.tablex</a>, <a href="../libraries/pl.class.html#">pl.class</a>, <a href="../classes/pl.Map.html#">pl.Map</a>, (<a href="../classes/pl.List.html#">pl.List</a> if __tostring is used)</p></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#Set">Set (t)</a></td> + <td class="summary">create a set.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#values">values (self)</a></td> + <td class="summary">get a list of the values in a set.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#map">map (self, fn, ...)</a></td> + <td class="summary">map a function over the values of a set.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#union">union (self, set)</a></td> + <td class="summary">union of two sets (also +).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#intersection">intersection (self, set)</a></td> + <td class="summary">intersection of two sets (also *).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#difference">difference (self, set)</a></td> + <td class="summary">new set with elements in the set that are not in the other (also -).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#issubset">issubset (self, set)</a></td> + <td class="summary">is the first set a subset of the second (also <)?.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#isempty">isempty (self)</a></td> + <td class="summary">is the set empty?.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#isdisjoint">isdisjoint (s1, s2)</a></td> + <td class="summary">are the sets disjoint?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#len">len (s)</a></td> + <td class="summary">size of this set (also # for 5.2).</td> + </tr> +</table> +<h2><a href="#metamethods">metamethods</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#__tostring">__tostring ()</a></td> + <td class="summary">string representation of a set.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#__add">__add ()</a></td> + <td class="summary">union of sets.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#__mul">__mul ()</a></td> + <td class="summary">intersection of sets.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#__sub">__sub ()</a></td> + <td class="summary">difference of sets.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#__pow">__pow ()</a></td> + <td class="summary">symmetric difference of sets.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#__lt">__lt ()</a></td> + <td class="summary">first set subset of second?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#__len">__len ()</a></td> + <td class="summary">cardinality of set (5.2).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#__eq">__eq (s1, s2)</a></td> + <td class="summary">equality between sets.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "Set"></a> + <strong>Set (t)</strong> + </dt> + <dd> + create a set. <br> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + may be a Set, Map or list-like table. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "values"></a> + <strong>values (self)</strong> + </dt> + <dd> + get a list of the values in a set. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a list + </ol> + + + + +</dd> + <dt> + <a name = "map"></a> + <strong>map (self, fn, ...)</strong> + </dt> + <dd> + map a function over the values of a set. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + <li><span class="parameter">fn</span> + a function + </li> + <li><span class="parameter">...</span> + extra arguments to pass to the function. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new set + </ol> + + + + +</dd> + <dt> + <a name = "union"></a> + <strong>union (self, set)</strong> + </dt> + <dd> + union of two sets (also +). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + <li><span class="parameter">set</span> + another set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new set + </ol> + + + + +</dd> + <dt> + <a name = "intersection"></a> + <strong>intersection (self, set)</strong> + </dt> + <dd> + intersection of two sets (also *). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + <li><span class="parameter">set</span> + another set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new set + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">> s = Set{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>} +> t = Set{<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>} +> = t +[<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>] +> = Set.intersection(s,t) +[<span class="number">30</span>,<span class="number">20</span>] +> = s*t +[<span class="number">30</span>,<span class="number">20</span>]</pre> + </ul> + +</dd> + <dt> + <a name = "difference"></a> + <strong>difference (self, set)</strong> + </dt> + <dd> + new set with elements in the set that are not in the other (also -). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + <li><span class="parameter">set</span> + another set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new set + </ol> + + + + +</dd> + <dt> + <a name = "issubset"></a> + <strong>issubset (self, set)</strong> + </dt> + <dd> + is the first set a subset of the second (also <)?. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + <li><span class="parameter">set</span> + another set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + +</dd> + <dt> + <a name = "isempty"></a> + <strong>isempty (self)</strong> + </dt> + <dd> + is the set empty?. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">self</span> + a Set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + +</dd> + <dt> + <a name = "isdisjoint"></a> + <strong>isdisjoint (s1, s2)</strong> + </dt> + <dd> + are the sets disjoint? (no elements in common). + Uses naive definition, i.e. that intersection is empty + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s1</span> + a Set + </li> + <li><span class="parameter">s2</span> + another set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + +</dd> + <dt> + <a name = "len"></a> + <strong>len (s)</strong> + </dt> + <dd> + size of this set (also # for 5.2). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + a Set + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + size + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="metamethods"></a>metamethods</h2> + + <dl class="function"> + <dt> + <a name = "__tostring"></a> + <strong>__tostring ()</strong> + </dt> + <dd> + string representation of a set. + + + + + + + +</dd> + <dt> + <a name = "__add"></a> + <strong>__add ()</strong> + </dt> + <dd> + union of sets. + + + + + + + +</dd> + <dt> + <a name = "__mul"></a> + <strong>__mul ()</strong> + </dt> + <dd> + intersection of sets. + + + + + + + +</dd> + <dt> + <a name = "__sub"></a> + <strong>__sub ()</strong> + </dt> + <dd> + difference of sets. + + + + + + + +</dd> + <dt> + <a name = "__pow"></a> + <strong>__pow ()</strong> + </dt> + <dd> + symmetric difference of sets. + + + + + + + +</dd> + <dt> + <a name = "__lt"></a> + <strong>__lt ()</strong> + </dt> + <dd> + first set subset of second? + + + + + + + +</dd> + <dt> + <a name = "__len"></a> + <strong>__len ()</strong> + </dt> + <dd> + cardinality of set (5.2). + + + + + + + +</dd> + <dt> + <a name = "__eq"></a> + <strong>__eq (s1, s2)</strong> + </dt> + <dd> + equality between sets. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s1</span> + + </li> + <li><span class="parameter">s2</span> + + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2018-11-23 21:07:42 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.app.html b/Data/Libraries/Penlight/docs/libraries/pl.app.html new file mode 100644 index 0000000..4417e8f --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.app.html @@ -0,0 +1,397 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><strong>pl.app</strong></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.app</code></h1> +<p>Application support functions.</p> +<p> See <a href="../manual/01-introduction.md.html#Application_Support">the Guide</a></p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.path.html#">pl.path</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#script_name">script_name ()</a></td> + <td class="summary">return the name of the current script running.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#require_here">require_here (base)</a></td> + <td class="summary">prefixes the current script's path to the Lua module path.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#appfile">appfile (file)</a></td> + <td class="summary">return a suitable path for files private to this application.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#platform">platform ()</a></td> + <td class="summary">return string indicating operating system.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#lua">lua ()</a></td> + <td class="summary">return the full command-line used to invoke this script.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#parse_args">parse_args (args, flags_with_values, flags_valid)</a></td> + <td class="summary">parse command-line arguments into flags and parameters.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "script_name"></a> + <strong>script_name ()</strong> + </dt> + <dd> + return the name of the current script running. + The name will be the name as passed on the command line + + + + <h3>Returns:</h3> + <ol> + + string filename + </ol> + + + + +</dd> + <dt> + <a name = "require_here"></a> + <strong>require_here (base)</strong> + </dt> + <dd> + prefixes the current script's path to the Lua module path. + Applies to both the source and the binary module paths. It makes it easy for + the main file of a multi-file program to access its modules in the same directory. + <code>base</code> allows these modules to be put in a specified subdirectory, to allow for + cleaner deployment and resolve potential conflicts between a script name and its + library directory.</p> + +<p> Note: the path is prefixed, so it is searched first when requiring modules. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">base</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + optional base directory (absolute, or relative path). + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the current script's path with a trailing slash + </ol> + + + + +</dd> + <dt> + <a name = "appfile"></a> + <strong>appfile (file)</strong> + </dt> + <dd> + return a suitable path for files private to this application. + These will look like '~/.SNAME/file', with '~' as with expanduser and + SNAME is the name of the script without .lua extension. + If the directory does not exist, it will be created. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">file</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a filename (w/out path) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + a full pathname, or nil</li> + <li> + cannot create directory error</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="comment">-- when run from a script called 'testapp' (on Windows): +</span><span class="keyword">local</span> app = <span class="global">require</span> <span class="string">'pl.app'</span> +<span class="global">print</span>(app.appfile <span class="string">'test.txt'</span>) +<span class="comment">-- C:\Documents and Settings\steve\.testapp\test.txt</span></pre> + </ul> + +</dd> + <dt> + <a name = "platform"></a> + <strong>platform ()</strong> + </dt> + <dd> + return string indicating operating system. + + + + <h3>Returns:</h3> + <ol> + + 'Windows','OSX' or whatever uname returns (e.g. 'Linux') + </ol> + + + + +</dd> + <dt> + <a name = "lua"></a> + <strong>lua ()</strong> + </dt> + <dd> + return the full command-line used to invoke this script. + It will not include the scriptname itself, see <a href="../libraries/pl.app.html#script_name">app.script_name</a>. + + + + <h3>Returns:</h3> + <ol> + <li> + command-line</li> + <li> + name of Lua program used</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="comment">-- execute: lua -lluacov -e 'print(_VERSION)' myscript.lua +</span> +<span class="comment">-- myscript.lua +</span><span class="global">print</span>(<span class="global">require</span>(<span class="string">"pl.app"</span>).lua()) <span class="comment">--> "lua -lluacov -e 'print(_VERSION)'", "lua"</span></pre> + </ul> + +</dd> + <dt> + <a name = "parse_args"></a> + <strong>parse_args (args, flags_with_values, flags_valid)</strong> + </dt> + <dd> + parse command-line arguments into flags and parameters. + Understands GNU-style command-line flags; short (<code>-f</code>) and long (<code>--flag</code>).</p> + +<p> These may be given a value with either '=' or ':' (<code>-k:2</code>,<code>--alpha=3.2</code>,<code>-n2</code>), + a number value can be given without a space. If the flag is marked + as having a value, then a space-separated value is also accepted (<code>-i hello</code>), + see the <code>flags_with_values</code> argument).</p> + +<p> Multiple short args can be combined like so: ( <code>-abcd</code>).</p> + +<p> When specifying the <code>flags_valid</code> parameter, its contents can also contain + aliasses, to convert short/long flags to the same output name. See the + example below.</p> + +<p> Note: if a flag is repeated, the last value wins. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">args</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">{string}</a></span> + an array of strings (default is the global <code>arg</code>) + </li> + <li><span class="parameter">flags_with_values</span> + <span class="types"><span class="type">tab</span></span> + any flags that take values, either list or hash + table e.g. <code>{ out=true }</code> or <code>{ "out" }</code>. + </li> + <li><span class="parameter">flags_valid</span> + <span class="types"><span class="type">tab</span></span> + (optional) flags that are valid, either list or hashtable. + If not given, everything + will be accepted(everything in <code>flags_with_values</code> will automatically be allowed) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + a table of flags (flag=value pairs)</li> + <li> + an array of parameters</li> + </ol> + + <h3>Raises:</h3> + if args is nil, then the global <code>args</code> must be available! + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="comment">-- Simple form: +</span><span class="keyword">local</span> flags, params = app.parse_args(<span class="keyword">nil</span>, + { <span class="string">"hello"</span>, <span class="string">"world"</span> }, <span class="comment">-- list of flags taking values +</span> { <span class="string">"l"</span>, <span class="string">"a"</span>, <span class="string">"b"</span>}) <span class="comment">-- list of allowed flags (value ones will be added) +</span> +<span class="comment">-- More complex example using aliasses: +</span><span class="keyword">local</span> valid = { + long = <span class="string">"l"</span>, <span class="comment">-- if 'l' is specified, it is reported as 'long' +</span> new = { <span class="string">"n"</span>, <span class="string">"old"</span> }, <span class="comment">-- here both 'n' and 'old' will go into 'new' +</span>} +<span class="keyword">local</span> values = { + <span class="string">"value"</span>, <span class="comment">-- will automatically be added to the allowed set of flags +</span> <span class="string">"new"</span>, <span class="comment">-- will mark 'n' and 'old' as requiring a value as well +</span>} +<span class="keyword">local</span> flags, params = app.parse_args(<span class="keyword">nil</span>, values, valid) + +<span class="comment">-- command: myapp.lua -l --old:hello --value world param1 param2 +</span><span class="comment">-- will yield: +</span>flags = { + long = <span class="keyword">true</span>, <span class="comment">-- input from 'l' +</span> new = <span class="string">"hello"</span>, <span class="comment">-- input from 'old' +</span> value = <span class="string">"world"</span>, <span class="comment">-- allowed because it was in 'values', note: space separated! +</span>} +params = { + [<span class="number">1</span>] = <span class="string">"param1"</span> + [<span class="number">2</span>] = <span class="string">"param2"</span> +}</pre> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.array2d.html b/Data/Libraries/Penlight/docs/libraries/pl.array2d.html new file mode 100644 index 0000000..8ee905b --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.array2d.html @@ -0,0 +1,1319 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><strong>pl.array2d</strong></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.array2d</code></h1> +<p>Operations on two-dimensional arrays.</p> +<p> See <a href="../manual/02-arrays.md.html#Operations_on_two_dimensional_tables">The Guide</a></p> + +<p> The size of the arrays is determined by using the length operator <code>#</code> hence + the module is not <code>nil</code> safe, and the usual precautions apply.</p> + +<p> Note: all functions taking <code>i1,j1,i2,j2</code> as arguments will normalize the + arguments using <a href="../libraries/pl.array2d.html#default_range">default_range</a>.</p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.tablex.html#">pl.tablex</a>, <a href="../libraries/pl.types.html#">pl.types</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#size">size (a)</a></td> + <td class="summary">return the row and column size.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#column">column (a, j)</a></td> + <td class="summary">extract a column from the 2D array.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#row">row (a, i)</a></td> + <td class="summary">extract a row from the 2D array.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#map">map (f, a, arg)</a></td> + <td class="summary">map a function over a 2D array</td> + </tr> + <tr> + <td class="name" nowrap><a href="#reduce_rows">reduce_rows (f, a)</a></td> + <td class="summary">reduce the rows using a function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#reduce_cols">reduce_cols (f, a)</a></td> + <td class="summary">reduce the columns using a function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#reduce2">reduce2 (opc, opr, a)</a></td> + <td class="summary">reduce a 2D array into a scalar, using two operations.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#map2">map2 (f, ad, bd, a, b, arg)</a></td> + <td class="summary">map a function over two arrays.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#product">product (f, t1, t2)</a></td> + <td class="summary">cartesian product of two 1d arrays.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#flatten">flatten (t)</a></td> + <td class="summary">flatten a 2D array.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#reshape">reshape (t, nrows, co)</a></td> + <td class="summary">reshape a 2D array.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#transpose">transpose (t)</a></td> + <td class="summary">transpose a 2D array.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#swap_rows">swap_rows (t, i1, i2)</a></td> + <td class="summary">swap two rows of an array.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#swap_cols">swap_cols (t, j1, j2)</a></td> + <td class="summary">swap two columns of an array.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#extract_rows">extract_rows (t, ridx)</a></td> + <td class="summary">extract the specified rows.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#extract_cols">extract_cols (t, cidx)</a></td> + <td class="summary">extract the specified columns.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#remove_row">remove_row (t, i)</a></td> + <td class="summary">remove a row from an array.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#remove_col">remove_col (t, j)</a></td> + <td class="summary">remove a column from an array.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#parse_range">parse_range (s)</a></td> + <td class="summary">parse a spreadsheet range.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#range">range (t, rstr)</a></td> + <td class="summary">get a slice of a 2D array using spreadsheet range notation.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#default_range">default_range (t, i1, j1, i2, j2)</a></td> + <td class="summary">normalizes coordinates to valid positive entries and defaults.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#slice">slice (t, i1, j1, i2, j2)</a></td> + <td class="summary">get a slice of a 2D array.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#set">set (t, value, i1, j1, i2, j2)</a></td> + <td class="summary">set a specified range of an array to a value.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#write">write (t, f, fmt, i1, j1, i2, j2)</a></td> + <td class="summary">write a 2D array to a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#forall">forall (t, row_op, end_row_op, i1, j1, i2, j2)</a></td> + <td class="summary">perform an operation for all values in a 2D array.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#move">move (dest, di, dj, src, i1, j1, i2, j2)</a></td> + <td class="summary">move a block from the destination to the source.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#iter">iter (a, indices, i1, j1, i2, j2)</a></td> + <td class="summary">iterate over all elements in a 2D array, with optional indices.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#columns">columns (a)</a></td> + <td class="summary">iterate over all columns.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#rows">rows (a)</a></td> + <td class="summary">iterate over all rows.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#new">new (rows, cols, val)</a></td> + <td class="summary">new array of specified dimensions</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "size"></a> + <strong>size (a)</strong> + </dt> + <dd> + return the row and column size. + Size is calculated using the Lua length operator #, so usual precautions + regarding <code>nil</code> values apply. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + <span class="types"><span class="type">array</span></span> + a 2d array + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">int</span></span> + number of rows (<code>#a</code>)</li> + <li> + <span class="types"><span class="type">int</span></span> + number of cols (<code>#a[1]</code>)</li> + </ol> + + + + +</dd> + <dt> + <a name = "column"></a> + <strong>column (a, j)</strong> + </dt> + <dd> + extract a column from the 2D array. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + <span class="types"><span class="type">array</span></span> + 2d array + </li> + <li><span class="parameter">j</span> + column index + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + 1d array + </ol> + + + + +</dd> + <dt> + <a name = "row"></a> + <strong>row (a, i)</strong> + </dt> + <dd> + extract a row from the 2D array. + Added in line with <a href="../libraries/pl.array2d.html#column">column</a>, for read-only purposes directly + accessing a[i] is more performant. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + <span class="types"><span class="type">array</span></span> + 2d array + </li> + <li><span class="parameter">i</span> + row index + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + 1d array (copy of the row) + </ol> + + + + +</dd> + <dt> + <a name = "map"></a> + <strong>map (f, a, arg)</strong> + </dt> + <dd> + map a function over a 2D array + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + <span class="types"><span class="type">func</span></span> + a function of at least one argument + </li> + <li><span class="parameter">a</span> + <span class="types"><span class="type">array</span></span> + 2d array + </li> + <li><span class="parameter">arg</span> + an optional extra argument to be passed to the function. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + 2d array + </ol> + + + + +</dd> + <dt> + <a name = "reduce_rows"></a> + <strong>reduce_rows (f, a)</strong> + </dt> + <dd> + reduce the rows using a function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + <span class="types"><span class="type">func</span></span> + a binary function + </li> + <li><span class="parameter">a</span> + <span class="types"><span class="type">array</span></span> + 2d array + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + 1d array + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#reduce">pl.tablex.reduce</a> + </ul> + + +</dd> + <dt> + <a name = "reduce_cols"></a> + <strong>reduce_cols (f, a)</strong> + </dt> + <dd> + reduce the columns using a function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + <span class="types"><span class="type">func</span></span> + a binary function + </li> + <li><span class="parameter">a</span> + <span class="types"><span class="type">array</span></span> + 2d array + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + 1d array + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#reduce">pl.tablex.reduce</a> + </ul> + + +</dd> + <dt> + <a name = "reduce2"></a> + <strong>reduce2 (opc, opr, a)</strong> + </dt> + <dd> + reduce a 2D array into a scalar, using two operations. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">opc</span> + <span class="types"><span class="type">func</span></span> + operation to reduce the final result + </li> + <li><span class="parameter">opr</span> + <span class="types"><span class="type">func</span></span> + operation to reduce the rows + </li> + <li><span class="parameter">a</span> + 2D array + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "map2"></a> + <strong>map2 (f, ad, bd, a, b, arg)</strong> + </dt> + <dd> + map a function over two arrays. + They can be both or either 2D arrays + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + <span class="types"><span class="type">func</span></span> + function of at least two arguments + </li> + <li><span class="parameter">ad</span> + <span class="types"><span class="type">int</span></span> + order of first array (<code>1</code> if <code>a</code> is a list/array, <code>2</code> if it is a 2d array) + </li> + <li><span class="parameter">bd</span> + <span class="types"><span class="type">int</span></span> + order of second array (<code>1</code> if <code>b</code> is a list/array, <code>2</code> if it is a 2d array) + </li> + <li><span class="parameter">a</span> + <span class="types"><span class="type">tab</span></span> + 1d or 2d array + </li> + <li><span class="parameter">b</span> + <span class="types"><span class="type">tab</span></span> + 1d or 2d array + </li> + <li><span class="parameter">arg</span> + optional extra argument to pass to function + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + 2D array, unless both arrays are 1D + </ol> + + + + +</dd> + <dt> + <a name = "product"></a> + <strong>product (f, t1, t2)</strong> + </dt> + <dd> + cartesian product of two 1d arrays. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + <span class="types"><span class="type">func</span></span> + a function of 2 arguments + </li> + <li><span class="parameter">t1</span> + <span class="types"><span class="type">array</span></span> + a 1d table + </li> + <li><span class="parameter">t2</span> + <span class="types"><span class="type">array</span></span> + a 1d table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + 2d table + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">product(<span class="string">'..'</span>,{<span class="number">1</span>,<span class="number">2</span>},{<span class="string">'a'</span>,<span class="string">'b'</span>}) == {{<span class="string">'1a'</span>,<span class="string">'2a'</span>},{<span class="string">'1b'</span>,<span class="string">'2b'</span>}}</pre> + </ul> + +</dd> + <dt> + <a name = "flatten"></a> + <strong>flatten (t)</strong> + </dt> + <dd> + flatten a 2D array. + (this goes over columns first.) + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + 2d table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a 1d table + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">flatten {{<span class="number">1</span>,<span class="number">2</span>},{<span class="number">3</span>,<span class="number">4</span>},{<span class="number">5</span>,<span class="number">6</span>}} == {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>}</pre> + </ul> + +</dd> + <dt> + <a name = "reshape"></a> + <strong>reshape (t, nrows, co)</strong> + </dt> + <dd> + reshape a 2D array. Reshape the aray by specifying a new nr of rows. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + 2d array + </li> + <li><span class="parameter">nrows</span> + <span class="types"><span class="type">int</span></span> + new number of rows + </li> + <li><span class="parameter">co</span> + <span class="types"><span class="type">bool</span></span> + use column-order (Fortran-style) (default false) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new 2d array + </ol> + + + + +</dd> + <dt> + <a name = "transpose"></a> + <strong>transpose (t)</strong> + </dt> + <dd> + transpose a 2D array. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + 2d array + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new 2d array + </ol> + + + + +</dd> + <dt> + <a name = "swap_rows"></a> + <strong>swap_rows (t, i1, i2)</strong> + </dt> + <dd> + swap two rows of an array. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a 2d array + </li> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + a row index + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + a row index + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + t (same, modified 2d array) + </ol> + + + + +</dd> + <dt> + <a name = "swap_cols"></a> + <strong>swap_cols (t, j1, j2)</strong> + </dt> + <dd> + swap two columns of an array. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a 2d array + </li> + <li><span class="parameter">j1</span> + <span class="types"><span class="type">int</span></span> + a column index + </li> + <li><span class="parameter">j2</span> + <span class="types"><span class="type">int</span></span> + a column index + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + t (same, modified 2d array) + </ol> + + + + +</dd> + <dt> + <a name = "extract_rows"></a> + <strong>extract_rows (t, ridx)</strong> + </dt> + <dd> + extract the specified rows. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + 2d array + </li> + <li><span class="parameter">ridx</span> + <span class="types"><span class="type">{int}</span></span> + a table of row indices + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new 2d array with the extracted rows + </ol> + + + + +</dd> + <dt> + <a name = "extract_cols"></a> + <strong>extract_cols (t, cidx)</strong> + </dt> + <dd> + extract the specified columns. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + 2d array + </li> + <li><span class="parameter">cidx</span> + <span class="types"><span class="type">{int}</span></span> + a table of column indices + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new 2d array with the extracted colums + </ol> + + + + +</dd> + <dt> + <a name = "remove_row"></a> + <strong>remove_row (t, i)</strong> + </dt> + <dd> + remove a row from an array. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a 2d array + </li> + <li><span class="parameter">i</span> + <span class="types"><span class="type">int</span></span> + a row index + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "remove_col"></a> + <strong>remove_col (t, j)</strong> + </dt> + <dd> + remove a column from an array. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a 2d array + </li> + <li><span class="parameter">j</span> + <span class="types"><span class="type">int</span></span> + a column index + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "parse_range"></a> + <strong>parse_range (s)</strong> + </dt> + <dd> + parse a spreadsheet range. + The range can be specified either as 'A1:B2' or 'R1C1:R2C2'; + a special case is a single element (e.g 'A1' or 'R1C1') + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a range (case insensitive). + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">int</span></span> + start row</li> + <li> + <span class="types"><span class="type">int</span></span> + start col</li> + <li> + <span class="types"><span class="type">int</span></span> + end row</li> + <li> + <span class="types"><span class="type">int</span></span> + end col</li> + </ol> + + + + +</dd> + <dt> + <a name = "range"></a> + <strong>range (t, rstr)</strong> + </dt> + <dd> + get a slice of a 2D array using spreadsheet range notation. @see parse_range + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a 2D array + </li> + <li><span class="parameter">rstr</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + range expression + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a slice + </ol> + + + <h3>See also:</h3> + <ul> + <li><a href="../libraries/pl.array2d.html#parse_range">array2d.parse_range</a></li> + <li><a href="../libraries/pl.array2d.html#slice">array2d.slice</a></li> + </ul> + + +</dd> + <dt> + <a name = "default_range"></a> + <strong>default_range (t, i1, j1, i2, j2)</strong> + </dt> + <dd> + normalizes coordinates to valid positive entries and defaults. + Negative indices will be counted from the end, too low, or too high + will be limited by the array sizes. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a 2D array + </li> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + start row (default 1) + </li> + <li><span class="parameter">j1</span> + <span class="types"><span class="type">int</span></span> + start col (default 1) + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + end row (default N) + </li> + <li><span class="parameter">j2</span> + <span class="types"><span class="type">int</span></span> + end col (default M) + return i1, j1, i2, j2 + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "slice"></a> + <strong>slice (t, i1, j1, i2, j2)</strong> + </dt> + <dd> + get a slice of a 2D array. Note that if the specified range has + a 1D result, the rank of the result will be 1. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a 2D array + </li> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + start row (default 1) + </li> + <li><span class="parameter">j1</span> + <span class="types"><span class="type">int</span></span> + start col (default 1) + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + end row (default N) + </li> + <li><span class="parameter">j2</span> + <span class="types"><span class="type">int</span></span> + end col (default M) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + an array, 2D in general but 1D in special cases. + </ol> + + + + +</dd> + <dt> + <a name = "set"></a> + <strong>set (t, value, i1, j1, i2, j2)</strong> + </dt> + <dd> + set a specified range of an array to a value. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a 2D array + </li> + <li><span class="parameter">value</span> + the value (may be a function, called as <code>val(i,j)</code>) + </li> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + start row (default 1) + </li> + <li><span class="parameter">j1</span> + <span class="types"><span class="type">int</span></span> + start col (default 1) + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + end row (default N) + </li> + <li><span class="parameter">j2</span> + <span class="types"><span class="type">int</span></span> + end col (default M) + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#set">tablex.set</a> + </ul> + + +</dd> + <dt> + <a name = "write"></a> + <strong>write (t, f, fmt, i1, j1, i2, j2)</strong> + </dt> + <dd> + write a 2D array to a file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a 2D array + </li> + <li><span class="parameter">f</span> + a file object (default stdout) + </li> + <li><span class="parameter">fmt</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a format string (default is just to use tostring) + </li> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + start row (default 1) + </li> + <li><span class="parameter">j1</span> + <span class="types"><span class="type">int</span></span> + start col (default 1) + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + end row (default N) + </li> + <li><span class="parameter">j2</span> + <span class="types"><span class="type">int</span></span> + end col (default M) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "forall"></a> + <strong>forall (t, row_op, end_row_op, i1, j1, i2, j2)</strong> + </dt> + <dd> + perform an operation for all values in a 2D array. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + 2D array + </li> + <li><span class="parameter">row_op</span> + <span class="types"><span class="type">func</span></span> + function to call on each value; <code>row_op(row,j)</code> + </li> + <li><span class="parameter">end_row_op</span> + <span class="types"><span class="type">func</span></span> + function to call at end of each row; <code>end_row_op(i)</code> + </li> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + start row (default 1) + </li> + <li><span class="parameter">j1</span> + <span class="types"><span class="type">int</span></span> + start col (default 1) + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + end row (default N) + </li> + <li><span class="parameter">j2</span> + <span class="types"><span class="type">int</span></span> + end col (default M) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "move"></a> + <strong>move (dest, di, dj, src, i1, j1, i2, j2)</strong> + </dt> + <dd> + move a block from the destination to the source. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">dest</span> + <span class="types"><span class="type">array</span></span> + a 2D array + </li> + <li><span class="parameter">di</span> + <span class="types"><span class="type">int</span></span> + start row in dest + </li> + <li><span class="parameter">dj</span> + <span class="types"><span class="type">int</span></span> + start col in dest + </li> + <li><span class="parameter">src</span> + <span class="types"><span class="type">array</span></span> + a 2D array + </li> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + start row (default 1) + </li> + <li><span class="parameter">j1</span> + <span class="types"><span class="type">int</span></span> + start col (default 1) + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + end row (default N) + </li> + <li><span class="parameter">j2</span> + <span class="types"><span class="type">int</span></span> + end col (default M) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "iter"></a> + <strong>iter (a, indices, i1, j1, i2, j2)</strong> + </dt> + <dd> + iterate over all elements in a 2D array, with optional indices. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + <span class="types"><span class="type">array</span></span> + 2D array + </li> + <li><span class="parameter">indices</span> + <span class="types"><span class="type">bool</span></span> + with indices (default false) + </li> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + start row (default 1) + </li> + <li><span class="parameter">j1</span> + <span class="types"><span class="type">int</span></span> + start col (default 1) + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + end row (default N) + </li> + <li><span class="parameter">j2</span> + <span class="types"><span class="type">int</span></span> + end col (default M) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + either <code>value</code> or <code>i,j,value</code> depending on the value of <code>indices</code> + </ol> + + + + +</dd> + <dt> + <a name = "columns"></a> + <strong>columns (a)</strong> + </dt> + <dd> + iterate over all columns. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + <span class="types"><span class="type">array</span></span> + a 2D array + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + column, column-index + </ol> + + + + +</dd> + <dt> + <a name = "rows"></a> + <strong>rows (a)</strong> + </dt> + <dd> + iterate over all rows. + Returns a copy of the row, for read-only purrposes directly iterating + is more performant; <code>ipairs(a)</code> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + <span class="types"><span class="type">array</span></span> + a 2D array + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + row, row-index + </ol> + + + + +</dd> + <dt> + <a name = "new"></a> + <strong>new (rows, cols, val)</strong> + </dt> + <dd> + new array of specified dimensions + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">rows</span> + <span class="types"><span class="type">int</span></span> + number of rows + </li> + <li><span class="parameter">cols</span> + <span class="types"><span class="type">int</span></span> + number of cols + </li> + <li><span class="parameter">val</span> + initial value; if it's a function then use <code>val(i,j)</code> + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + new 2d array + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.class.html b/Data/Libraries/Penlight/docs/libraries/pl.class.html new file mode 100644 index 0000000..b623e94 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.class.html @@ -0,0 +1,332 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><strong>pl.class</strong></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.class</code></h1> +<p>Provides a reuseable and convenient framework for creating classes in Lua.</p> +<p> Two possible notations:</p> + + +<pre> +B = class(A) +class.B(A) +</pre> + +<p> The latter form creates a named class within the current environment. Note + that this implicitly brings in <a href="../libraries/pl.utils.html#">pl.utils</a> as a dependency.</p> + +<p> See the Guide for further <a href="../manual/01-introduction.md.html#Simplifying_Object_Oriented_Programming_in_Lua">discussion</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#_init">_init (...)</a></td> + <td class="summary">initializes an <strong>instance</strong> upon creation.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:is_a">instance:is_a (some_class)</a></td> + <td class="summary">checks whether an <strong>instance</strong> is derived from some class.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#some_class:class_of">some_class:class_of (some_instance)</a></td> + <td class="summary">checks whether an <strong>instance</strong> is derived from some class.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#some_class:cast">some_class:cast (some_instance)</a></td> + <td class="summary">cast an object to another class.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#class">class (base, c_arg, c)</a></td> + <td class="summary">create a new class, derived from a given base class.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "_init"></a> + <strong>_init (...)</strong> + </dt> + <dd> + initializes an <strong>instance</strong> upon creation. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + parameters passed to the constructor + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> Cat = class() +<span class="keyword">function</span> Cat:_init(name) + <span class="comment">--self:super(name) -- call the ancestor initializer if needed +</span> self.name = name +<span class="keyword">end</span> + +<span class="keyword">local</span> pussycat = Cat(<span class="string">"pussycat"</span>) +<span class="global">print</span>(pussycat.name) <span class="comment">--> pussycat</span></pre> + </ul> + +</dd> + <dt> + <a name = "instance:is_a"></a> + <strong>instance:is_a (some_class)</strong> + </dt> + <dd> + checks whether an <strong>instance</strong> is derived from some class. + Works the other way around as <a href="../libraries/pl.class.html#some_class:class_of">class_of</a>. It has two ways of using; + 1) call with a class to check against, 2) call without params. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">some_class</span> + class to check against, or <code>nil</code> to return the class + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <code>true</code> if <code>instance</code> is derived from <code>some_class</code>, or if <code>some_class == nil</code> then + it returns the class table of the instance + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> pussycat = Lion() <span class="comment">-- assuming Lion derives from Cat +</span><span class="keyword">if</span> pussycat:is_a(Cat) <span class="keyword">then</span> + <span class="comment">-- it's true, it is a Lion, but also a Cat +</span><span class="keyword">end</span> + +<span class="keyword">if</span> pussycat:is_a() == Lion <span class="keyword">then</span> + <span class="comment">-- It's true +</span><span class="keyword">end</span></pre> + </ul> + +</dd> + <dt> + <a name = "some_class:class_of"></a> + <strong>some_class:class_of (some_instance)</strong> + </dt> + <dd> + checks whether an <strong>instance</strong> is derived from some class. + Works the other way around as <a href="../libraries/pl.class.html#instance:is_a">is_a</a>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">some_instance</span> + instance to check against + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <code>true</code> if <code>some_instance</code> is derived from <code>some_class</code> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> pussycat = Lion() <span class="comment">-- assuming Lion derives from Cat +</span><span class="keyword">if</span> Cat:class_of(pussycat) <span class="keyword">then</span> + <span class="comment">-- it's true +</span><span class="keyword">end</span></pre> + </ul> + +</dd> + <dt> + <a name = "some_class:cast"></a> + <strong>some_class:cast (some_instance)</strong> + </dt> + <dd> + cast an object to another class. + It is not clever (or safe!) so use carefully. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">some_instance</span> + the object to be changed + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "class"></a> + <strong>class (base, c_arg, c)</strong> + </dt> + <dd> + create a new class, derived from a given base class. + Supporting two class creation syntaxes: + either <code>Name = class(base)</code> or <code>class.Name(base)</code>. + The first form returns the class directly and does not set its <code>_name</code>. + The second form creates a variable <code>Name</code> in the current environment set + to the class, and also sets <code>_name</code>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">base</span> + optional base class + </li> + <li><span class="parameter">c_arg</span> + optional parameter to class constructor + </li> + <li><span class="parameter">c</span> + optional table to be used as class + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.compat.html b/Data/Libraries/Penlight/docs/libraries/pl.compat.html new file mode 100644 index 0000000..6680030 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.compat.html @@ -0,0 +1,580 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +<li><a href="#Fields">Fields</a></li> +<li><a href="#Global_exported_functions__for_Lua_5_1___LuaJIT_">Global exported functions (for Lua 5.1 & LuaJIT) </a></li> +<li><a href="#Global_exported_functions__for_Lua___5_4_">Global exported functions (for Lua < 5.4) </a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><strong>pl.compat</strong></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.compat</code></h1> +<p>Lua 5.1/5.2/5.3 compatibility.</p> +<p> Injects <a href="../libraries/pl.compat.html#table.pack">table.pack</a>, <a href="../libraries/pl.compat.html#table.unpack">table.unpack</a>, and <a href="../libraries/pl.compat.html#package.searchpath">package.searchpath</a> in the global + environment, to make sure they are available for Lua 5.1 and LuaJIT.</p> + +<p> All other functions are exported as usual in the returned module table.</p> + +<p> NOTE: everything in this module is also available in <a href="../libraries/pl.utils.html#">pl.utils</a>.</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#execute">execute (cmd)</a></td> + <td class="summary">execute a shell command, in a compatible and platform independent way.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#load">load (ld[, source[, mode[, env]]])</a></td> + <td class="summary">Load Lua code as a text or binary chunk (in a Lua 5.2 compatible way).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#getfenv">getfenv (f)</a></td> + <td class="summary">Get environment of a function (in a Lua 5.1 compatible way).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#setfenv">setfenv (f, env)</a></td> + <td class="summary">Set environment of a function (in a Lua 5.1 compatible way).</td> + </tr> +</table> +<h2><a href="#Fields">Fields</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#lua51">lua51</a></td> + <td class="summary">boolean flag this is Lua 5.1 (or LuaJIT).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#jit">jit</a></td> + <td class="summary">boolean flag this is LuaJIT.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#jit52">jit52</a></td> + <td class="summary">boolean flag this is LuaJIT with 5.2 compatibility compiled in.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#dir_separator">dir_separator</a></td> + <td class="summary">the directory separator character for the current platform.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#is_windows">is_windows</a></td> + <td class="summary">boolean flag this is a Windows platform.</td> + </tr> +</table> +<h2><a href="#Global_exported_functions__for_Lua_5_1___LuaJIT_">Global exported functions (for Lua 5.1 & LuaJIT) </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#table.pack">table.pack (...)</a></td> + <td class="summary">pack an argument list into a table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#table.unpack">table.unpack (t[, i[, t]])</a></td> + <td class="summary">unpack a table and return the elements.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#package.searchpath">package.searchpath (name, path[, sep[, rep]])</a></td> + <td class="summary">return the full path where a file name would be matched.</td> + </tr> +</table> +<h2><a href="#Global_exported_functions__for_Lua___5_4_">Global exported functions (for Lua < 5.4) </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#warn">warn (...)</a></td> + <td class="summary">raise a warning message.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "execute"></a> + <strong>execute (cmd)</strong> + </dt> + <dd> + execute a shell command, in a compatible and platform independent way. + This is a compatibility function that returns the same for Lua 5.1 and + Lua 5.2+.</p> + +<p> NOTE: Windows systems can use signed 32bit integer exitcodes. Posix systems + only use exitcodes 0-255, anything else is undefined.</p> + +<p> NOTE2: In Lua 5.2 and 5.3 a Windows exitcode of -1 would not properly be + returned, this function will return it properly for all versions. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">cmd</span> + a shell command + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + true if successful</li> + <li> + actual return code</li> + </ol> + + + + +</dd> + <dt> + <a name = "load"></a> + <strong>load (ld[, source[, mode[, env]]])</strong> + </dt> + <dd> + Load Lua code as a text or binary chunk (in a Lua 5.2 compatible way). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">ld</span> + code string or loader + </li> + <li><span class="parameter">source</span> + name of chunk for errors + (<em>optional</em>) + </li> + <li><span class="parameter">mode</span> + 'b', 't' or 'bt' + (<em>optional</em>) + </li> + <li><span class="parameter">env</span> + environment to load the chunk in + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "getfenv"></a> + <strong>getfenv (f)</strong> + </dt> + <dd> + Get environment of a function (in a Lua 5.1 compatible way). + Not 100% compatible, so with Lua 5.2 it may return nil for a function with no + global references! + Based on code by <a href="http://lua-users.org/lists/lua-l/2010-06/msg00313.html">Sergey Rozhenko</a> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + a function or a call stack reference + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "setfenv"></a> + <strong>setfenv (f, env)</strong> + </dt> + <dd> + Set environment of a function (in a Lua 5.1 compatible way). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + a function or a call stack reference + </li> + <li><span class="parameter">env</span> + a table that becomes the new environment of <code>f</code> + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Fields"></a>Fields</h2> + + <dl class="function"> + <dt> + <a name = "lua51"></a> + <strong>lua51</strong> + </dt> + <dd> + boolean flag this is Lua 5.1 (or LuaJIT). + + + <ul> + <li><span class="parameter">lua51</span> + + + + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "jit"></a> + <strong>jit</strong> + </dt> + <dd> + boolean flag this is LuaJIT. + + + <ul> + <li><span class="parameter">jit</span> + + + + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "jit52"></a> + <strong>jit52</strong> + </dt> + <dd> + boolean flag this is LuaJIT with 5.2 compatibility compiled in. + + + <ul> + <li><span class="parameter">jit52</span> + + + + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "dir_separator"></a> + <strong>dir_separator</strong> + </dt> + <dd> + the directory separator character for the current platform. + + + <ul> + <li><span class="parameter">dir_separator</span> + + + + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "is_windows"></a> + <strong>is_windows</strong> + </dt> + <dd> + boolean flag this is a Windows platform. + + + <ul> + <li><span class="parameter">is_windows</span> + + + + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Global_exported_functions__for_Lua_5_1___LuaJIT_"></a>Global exported functions (for Lua 5.1 & LuaJIT) </h2> + + <dl class="function"> + <dt> + <a name = "table.pack"></a> + <strong>table.pack (...)</strong> + </dt> + <dd> + pack an argument list into a table. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + any arguments + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a table with field n set to the length + </ol> + + + + +</dd> + <dt> + <a name = "table.unpack"></a> + <strong>table.unpack (t[, i[, t]])</strong> + </dt> + <dd> + unpack a table and return the elements. </p> + +<p> NOTE: this version does NOT honor the n field, and hence it is not nil-safe. + See <a href="../libraries/pl.utils.html#unpack">utils.unpack</a> for a version that is nil-safe. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + index of the last element to unpack, defaults to #t + (<em>optional</em>) + </li> + <li><span class="parameter">i</span> + index from which to start unpacking, defaults to 1 + (<em>optional</em>) + </li> + <li><span class="parameter">t</span> + index of the last element to unpack, defaults to #t + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + multiple return values from the table + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.utils.html#unpack">utils.unpack</a> + </ul> + + +</dd> + <dt> + <a name = "package.searchpath"></a> + <strong>package.searchpath (name, path[, sep[, rep]])</strong> + </dt> + <dd> + return the full path where a file name would be matched. + This function was introduced in Lua 5.2, so this compatibility version + will be injected in Lua 5.1 engines. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + file name, possibly dotted + </li> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a path-template in the same form as package.path or package.cpath + </li> + <li><span class="parameter">sep</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + template separate character to be replaced by path separator. Default: "." + (<em>optional</em>) + </li> + <li><span class="parameter">rep</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the path separator to use, defaults to system separator. Default; "/" on Unixes, "\" on Windows. + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + on success: path of the file</li> + <li> + on failure: nil, error string listing paths tried</li> + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.path.html#package_path">path.package_path</a> + </ul> + + +</dd> +</dl> + <h2 class="section-header "><a name="Global_exported_functions__for_Lua___5_4_"></a>Global exported functions (for Lua < 5.4) </h2> + + <dl class="function"> + <dt> + <a name = "warn"></a> + <strong>warn (...)</strong> + </dt> + <dd> + raise a warning message. + This functions mimics the <a href="../libraries/pl.compat.html#warn">warn</a> function added in Lua 5.4. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + any arguments + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.comprehension.html b/Data/Libraries/Penlight/docs/libraries/pl.comprehension.html new file mode 100644 index 0000000..4c31b64 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.comprehension.html @@ -0,0 +1,165 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><strong>pl.comprehension</strong></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.comprehension</code></h1> +<p>List comprehensions implemented in Lua.</p> +<p> See the <a href="http://lua-users.org/wiki/ListComprehensions">wiki page</a></p> + + +<pre> +<span class="keyword">local</span> C= <span class="global">require</span> <span class="string">'pl.comprehension'</span> . new() + +C (<span class="string">'x for x=1,10'</span>) () +==> {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>,<span class="number">10</span>} +C <span class="string">'x^2 for x=1,4'</span> () +==> {<span class="number">1</span>,<span class="number">4</span>,<span class="number">9</span>,<span class="number">16</span>} +C <span class="string">'{x,x^2} for x=1,4'</span> () +==> {{<span class="number">1</span>,<span class="number">1</span>},{<span class="number">2</span>,<span class="number">4</span>},{<span class="number">3</span>,<span class="number">9</span>},{<span class="number">4</span>,<span class="number">16</span>}} +C <span class="string">'2*x for x'</span> {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>} +==> {<span class="number">2</span>,<span class="number">4</span>,<span class="number">6</span>} +dbl = C <span class="string">'2*x for x'</span> +dbl {<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>} +==> {<span class="number">20</span>,<span class="number">40</span>,<span class="number">60</span>} +C <span class="string">'x for x if x % 2 == 0'</span> {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>} +==> {<span class="number">2</span>,<span class="number">4</span>} +C <span class="string">'{x,y} for x = 1,2 for y = 1,2'</span> () +==> {{<span class="number">1</span>,<span class="number">1</span>},{<span class="number">1</span>,<span class="number">2</span>},{<span class="number">2</span>,<span class="number">1</span>},{<span class="number">2</span>,<span class="number">2</span>}} +C <span class="string">'{x,y} for x for y'</span> ({<span class="number">1</span>,<span class="number">2</span>},{<span class="number">10</span>,<span class="number">20</span>}) +==> {{<span class="number">1</span>,<span class="number">10</span>},{<span class="number">1</span>,<span class="number">20</span>},{<span class="number">2</span>,<span class="number">10</span>},{<span class="number">2</span>,<span class="number">20</span>}} +<span class="global">assert</span>(C <span class="string">'sum(x^2 for x)'</span> {<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>} == <span class="number">2</span>^<span class="number">2</span>+<span class="number">3</span>^<span class="number">2</span>+<span class="number">4</span>^<span class="number">2</span>) +</pre> + +<p> (c) 2008 David Manura. Licensed under the same terms as Lua (MIT license).</p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.luabalanced.html#">pl.luabalanced</a></p> + +<p> See <a href="../manual/07-functional.md.html#List_Comprehensions">the Guide</a></p> + + + +<br/> +<br/> + + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.config.html b/Data/Libraries/Penlight/docs/libraries/pl.config.html new file mode 100644 index 0000000..1ad8833 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.config.html @@ -0,0 +1,259 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><strong>pl.config</strong></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.config</code></h1> +<p>Reads configuration files into a Lua table.</p> +<p> + +<p> Understands INI files, classic Unix config files, and simple + delimited columns of values. See <a href="../manual/06-data.md.html#Reading_Configuration_Files">the Guide</a></p> + + +<pre> +# test.config +# Read timeout <span class="keyword">in</span> seconds +read.timeout=<span class="number">10</span> +# Write timeout <span class="keyword">in</span> seconds +write.timeout=<span class="number">5</span> +#acceptable ports +ports = <span class="number">1002</span>,<span class="number">1003</span>,<span class="number">1004</span> + +<span class="comment">-- readconfig.lua +</span><span class="keyword">local</span> config = <span class="global">require</span> <span class="string">'config'</span> +<span class="keyword">local</span> t = config.read <span class="string">'test.config'</span> +<span class="global">print</span>(pretty.write(t)) + +### output ##### +{ + ports = { + <span class="number">1002</span>, + <span class="number">1003</span>, + <span class="number">1004</span> + }, + write_timeout = <span class="number">5</span>, + read_timeout = <span class="number">10</span> +} +</pre> + +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#lines">lines (file)</a></td> + <td class="summary">like io.lines(), but allows for lines to be continued with ''.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#read">read (file[, cnfg])</a></td> + <td class="summary">read a configuration file into a table</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "lines"></a> + <strong>lines (file)</strong> + </dt> + <dd> + like io.lines(), but allows for lines to be continued with ''. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">file</span> + a file-like object (anything where read() returns the next line) or a filename. + Defaults to stardard input. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + an iterator over the lines, or nil</li> + <li> + error 'not a file-like object' or 'file is nil'</li> + </ol> + + + + +</dd> + <dt> + <a name = "read"></a> + <strong>read (file[, cnfg])</strong> + </dt> + <dd> + read a configuration file into a table + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">file</span> + either a file-like object or a string, which must be a filename + </li> + <li><span class="parameter">cnfg</span> + <span class="types"><span class="type">tab</span></span> + +<p> a configuration table that may contain these fields:</p> + +<ul> + <li><code>smart</code> try to deduce what kind of config file we have (default false)</li> + <li><code>variabilize</code> make names into valid Lua identifiers (default true)</li> + <li><code>convert_numbers</code> try to convert values into numbers (default true)</li> + <li><code>trim_space</code> ensure that there is no starting or trailing whitespace with values (default true)</li> + <li><code>trim_quotes</code> remove quotes from strings (default false)</li> + <li><code>list_delim</code> delimiter to use when separating columns (default ',')</li> + <li><code>keysep</code> separator between key and value pairs (default '=')</li> +</ul> + + + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + a table containing items, or <code>nil</code></li> + <li> + error message (same as <a href="../libraries/pl.config.html#lines">config.lines</a></li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.data.html b/Data/Libraries/Penlight/docs/libraries/pl.data.html new file mode 100644 index 0000000..736e82e --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.data.html @@ -0,0 +1,571 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><strong>pl.data</strong></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.data</code></h1> +<p>Reading and querying simple tabular data.</p> +<p> + + + +<pre> +data.read <span class="string">'test.txt'</span> +==> {{<span class="number">10</span>,<span class="number">20</span>},{<span class="number">2</span>,<span class="number">5</span>},{<span class="number">40</span>,<span class="number">50</span>},fieldnames={<span class="string">'x'</span>,<span class="string">'y'</span>},delim=<span class="string">','</span>} +</pre> + +<p> Provides a way of creating basic SQL-like queries.</p> + + +<pre> +<span class="global">require</span> <span class="string">'pl'</span> +<span class="keyword">local</span> d = data.read(<span class="string">'xyz.txt'</span>) +<span class="keyword">local</span> q = d:<span class="global">select</span>(<span class="string">'x,y,z where x > 3 and z < 2 sort by y'</span>) +<span class="keyword">for</span> x,y,z <span class="keyword">in</span> q <span class="keyword">do</span> + <span class="global">print</span>(x,y,z) +<span class="keyword">end</span> +</pre> + +<p> See <a href="../manual/06-data.md.html#Reading_Columnar_Data">the Guide</a></p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.array2d.html#">pl.array2d</a> (fallback methods)</p> +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#Data.column_by_name">Data.column_by_name (name)</a></td> + <td class="summary">return a particular column as a list of values (method).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Data.select">Data.select (condn)</a></td> + <td class="summary">return a query iterator on this data (method).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Data.select_row">Data.select_row (condn)</a></td> + <td class="summary">return a row iterator on this data (method).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Data.copy_select">Data.copy_select (condn)</a></td> + <td class="summary">return a new data object based on this query (method).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Data.column_names">Data.column_names ()</a></td> + <td class="summary">return the field names of this data object (method).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Data.write_row">Data.write_row (f)</a></td> + <td class="summary">write out a row (method).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Data.write">Data.write (f)</a></td> + <td class="summary">write data out to file (method).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#read">read (file, cnfg)</a></td> + <td class="summary">read a delimited file in a Lua table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#write">write (data, file[, fieldnames[, delim='\t']])</a></td> + <td class="summary">write 2D data to a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#new">new (d[, fieldnames])</a></td> + <td class="summary">create a new dataset from a table of rows.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#query">query (data, condn, context, return_row)</a></td> + <td class="summary">create a query iterator from a select string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#filter">filter (Q, infile, outfile, dont_fail)</a></td> + <td class="summary">Filter input using a query.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "Data.column_by_name"></a> + <strong>Data.column_by_name (name)</strong> + </dt> + <dd> + return a particular column as a list of values (method). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + either name of column, or numerical index. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Data.select"></a> + <strong>Data.select (condn)</strong> + </dt> + <dd> + return a query iterator on this data (method). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">condn</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the query expression + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.data.html#query">data.query</a> + </ul> + + +</dd> + <dt> + <a name = "Data.select_row"></a> + <strong>Data.select_row (condn)</strong> + </dt> + <dd> + return a row iterator on this data (method). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">condn</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the query expression + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Data.copy_select"></a> + <strong>Data.copy_select (condn)</strong> + </dt> + <dd> + return a new data object based on this query (method). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">condn</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the query expression + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Data.column_names"></a> + <strong>Data.column_names ()</strong> + </dt> + <dd> + return the field names of this data object (method). + + + + + + + +</dd> + <dt> + <a name = "Data.write_row"></a> + <strong>Data.write_row (f)</strong> + </dt> + <dd> + write out a row (method). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + file-like object + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Data.write"></a> + <strong>Data.write (f)</strong> + </dt> + <dd> + write data out to file (method). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + file-like object + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "read"></a> + <strong>read (file, cnfg)</strong> + </dt> + <dd> + read a delimited file in a Lua table. + By default, attempts to treat first line as separated list of fieldnames. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">file</span> + a filename or a file-like object + </li> + <li><span class="parameter">cnfg</span> parsing options + <ul> + <li><span class="parameter">delim</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a string pattern to split fields + </li> + <li><span class="parameter">fieldnames</span> + <span class="types"><span class="type">array</span></span> + (i.e. don't read from first line) + </li> + <li><span class="parameter">no_convert</span> + <span class="types"><span class="type">bool</span></span> + (default is to try conversion on first data line) + </li> + <li><span class="parameter">convert</span> + <span class="types"><span class="type">tab</span></span> + table of custom conversion functions with column keys + </li> + <li><span class="parameter">numfields</span> + <span class="types"><span class="type">int</span></span> + indices of columns known to be numbers + </li> + <li><span class="parameter">last_field_collect</span> + <span class="types"><span class="type">bool</span></span> + only split as many fields as fieldnames. + </li> + <li><span class="parameter">thousands_dot</span> + <span class="types"><span class="type">int</span></span> + thousands separator in Excel CSV is '.' + </li> + <li><span class="parameter">csv</span> + <span class="types"><span class="type">bool</span></span> + fields may be double-quoted and contain commas; + Also, empty fields are considered to be equivalent to zero. + </li> + </li></ul> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <a href="../libraries/pl.data.html">data</a> object, or <code>nil</code></li> + <li> + error message. May be a file error, 'not a file-like object' + or a conversion error</li> + </ol> + + + + +</dd> + <dt> + <a name = "write"></a> + <strong>write (data, file[, fieldnames[, delim='\t']])</strong> + </dt> + <dd> + write 2D data to a file. + Does not assume that the data has actually been + generated with <a href="../libraries/pl.data.html#new">new</a> or <a href="../libraries/pl.data.html#read">read</a>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">data</span> + 2D array + </li> + <li><span class="parameter">file</span> + filename or file-like object + </li> + <li><span class="parameter">fieldnames</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">{string}</a></span> + list of fields (optional) + (<em>optional</em>) + </li> + <li><span class="parameter">delim</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + delimiter (default tab) + (<em>default</em> '\t') + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or nil, error + </ol> + + + + +</dd> + <dt> + <a name = "new"></a> + <strong>new (d[, fieldnames])</strong> + </dt> + <dd> + create a new dataset from a table of rows. + Can specify the fieldnames, else the table must have a field called + 'fieldnames', which is either a string of delimiter-separated names, + or a table of names. <br> + If the table does not have a field called 'delim', then an attempt will be + made to guess it from the fieldnames string, defaults otherwise to tab. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">d</span> + the table. + </li> + <li><span class="parameter">fieldnames</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">{string}</a></span> + optional fieldnames + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the table. + </ol> + + + + +</dd> + <dt> + <a name = "query"></a> + <strong>query (data, condn, context, return_row)</strong> + </dt> + <dd> + create a query iterator from a select string. + Select string has this format: <br> + FIELDLIST [ where LUA-CONDN [ sort by FIELD] ]<br> + FIELDLIST is a comma-separated list of valid fields, or '*'. <br> <br> + The condition can also be a table, with fields 'fields' (comma-sep string or + table), 'sort_by' (string) and 'where' (Lua expression string or function) + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">data</span> + table produced by read + </li> + <li><span class="parameter">condn</span> + select string or table + </li> + <li><span class="parameter">context</span> + a list of tables to be searched when resolving functions + </li> + <li><span class="parameter">return_row</span> + if true, wrap the results in a row table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + an iterator over the specified fields, or nil</li> + <li> + an error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "filter"></a> + <strong>filter (Q, infile, outfile, dont_fail)</strong> + </dt> + <dd> + Filter input using a query. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">Q</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a query string + </li> + <li><span class="parameter">infile</span> + filename or file-like object + </li> + <li><span class="parameter">outfile</span> + filename or file-like object + </li> + <li><span class="parameter">dont_fail</span> + <span class="types"><span class="type">bool</span></span> + true if you want to return an error, not just fail + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.dir.html b/Data/Libraries/Penlight/docs/libraries/pl.dir.html new file mode 100644 index 0000000..5dfa0f6 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.dir.html @@ -0,0 +1,615 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><strong>pl.dir</strong></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.dir</code></h1> +<p>Listing files in directories and creating/removing directory paths.</p> +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.path.html#">pl.path</a></p> + +<p> Soft Dependencies: <code>alien</code>, <code>ffi</code> (either are used on Windows for copying/moving files)</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#fnmatch">fnmatch (filename, pattern)</a></td> + <td class="summary">Test whether a file name matches a shell pattern.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#filter">filter (filenames, pattern)</a></td> + <td class="summary">Return a list of all file names within an array which match a pattern.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#getfiles">getfiles (dirname, mask)</a></td> + <td class="summary">return a list of all files in a directory which match a shell pattern.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#getdirectories">getdirectories (dirname)</a></td> + <td class="summary">return a list of all subdirectories of the directory.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#copyfile">copyfile (src, dest, flag)</a></td> + <td class="summary">copy a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#movefile">movefile (src, dest)</a></td> + <td class="summary">move a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#walk">walk (root, bottom_up, follow_links)</a></td> + <td class="summary">return an iterator which walks through a directory tree starting at root.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#rmtree">rmtree (fullpath)</a></td> + <td class="summary">remove a whole directory tree.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#makepath">makepath (p)</a></td> + <td class="summary">create a directory path.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#clonetree">clonetree (path1, path2, file_fun, verbose)</a></td> + <td class="summary">clone a directory tree.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#dirtree">dirtree (d)</a></td> + <td class="summary">return an iterator over all entries in a directory tree</td> + </tr> + <tr> + <td class="name" nowrap><a href="#getallfiles">getallfiles (start_path, shell_pattern)</a></td> + <td class="summary">Recursively returns all the file starting at <em>path</em>.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "fnmatch"></a> + <strong>fnmatch (filename, pattern)</strong> + </dt> + <dd> + Test whether a file name matches a shell pattern. + Both parameters are case-normalized if operating system is + case-insensitive. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">filename</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file name. + </li> + <li><span class="parameter">pattern</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A shell pattern. The only special characters are + <code>'*'</code> and <code>'?'</code>: <code>'*'</code> matches any sequence of characters and + <code>'?'</code> matches any single character. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">bool</span></span> + + + + </ol> + + <h3>Raises:</h3> + dir and mask must be strings + + + +</dd> + <dt> + <a name = "filter"></a> + <strong>filter (filenames, pattern)</strong> + </dt> + <dd> + Return a list of all file names within an array which match a pattern. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">filenames</span> + <span class="types"><span class="type">tab</span></span> + An array containing file names. + </li> + <li><span class="parameter">pattern</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A shell pattern. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="../classes/pl.List.html">List(string)</a></span> + List of matching file names. + </ol> + + <h3>Raises:</h3> + dir and mask must be strings + + + +</dd> + <dt> + <a name = "getfiles"></a> + <strong>getfiles (dirname, mask)</strong> + </dt> + <dd> + return a list of all files in a directory which match a shell pattern. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">dirname</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A directory. If not given, all files in current directory are returned. + </li> + <li><span class="parameter">mask</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A shell pattern. If not given, all files are returned. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">{string}</a></span> + list of files + </ol> + + <h3>Raises:</h3> + dirname and mask must be strings + + + +</dd> + <dt> + <a name = "getdirectories"></a> + <strong>getdirectories (dirname)</strong> + </dt> + <dd> + return a list of all subdirectories of the directory. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">dirname</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A directory + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">{string}</a></span> + a list of directories + </ol> + + <h3>Raises:</h3> + dir must be a a valid directory + + + +</dd> + <dt> + <a name = "copyfile"></a> + <strong>copyfile (src, dest, flag)</strong> + </dt> + <dd> + copy a file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">src</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + source file + </li> + <li><span class="parameter">dest</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + destination file or directory + </li> + <li><span class="parameter">flag</span> + <span class="types"><span class="type">bool</span></span> + true if you want to force the copy (default) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">bool</span></span> + operation succeeded + </ol> + + <h3>Raises:</h3> + src and dest must be strings + + + +</dd> + <dt> + <a name = "movefile"></a> + <strong>movefile (src, dest)</strong> + </dt> + <dd> + move a file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">src</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + source file + </li> + <li><span class="parameter">dest</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + destination file or directory + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">bool</span></span> + operation succeeded + </ol> + + <h3>Raises:</h3> + src and dest must be strings + + + +</dd> + <dt> + <a name = "walk"></a> + <strong>walk (root, bottom_up, follow_links)</strong> + </dt> + <dd> + return an iterator which walks through a directory tree starting at root. + The iterator returns (root,dirs,files) + Note that dirs and files are lists of names (i.e. you must say path.join(root,d) + to get the actual full path) + If bottom_up is false (or not present), then the entries at the current level are returned + before we go deeper. This means that you can modify the returned list of directories before + continuing. + This is a clone of os.walk from the Python libraries. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">root</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A starting directory + </li> + <li><span class="parameter">bottom_up</span> + <span class="types"><span class="type">bool</span></span> + False if we start listing entries immediately. + </li> + <li><span class="parameter">follow_links</span> + <span class="types"><span class="type">bool</span></span> + follow symbolic links + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + an iterator returning root,dirs,files + </ol> + + <h3>Raises:</h3> + root must be a directory + + + +</dd> + <dt> + <a name = "rmtree"></a> + <strong>rmtree (fullpath)</strong> + </dt> + <dd> + remove a whole directory tree. + Symlinks in the tree will be deleted without following them. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fullpath</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A directory path (must be an actual directory, not a symlink) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + true or nil</li> + <li> + error if failed</li> + </ol> + + <h3>Raises:</h3> + fullpath must be a string + + + +</dd> + <dt> + <a name = "makepath"></a> + <strong>makepath (p)</strong> + </dt> + <dd> + create a directory path. + This will create subdirectories as necessary! + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">p</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A directory path + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true on success, nil + errormsg on failure + </ol> + + <h3>Raises:</h3> + failure to create + + + +</dd> + <dt> + <a name = "clonetree"></a> + <strong>clonetree (path1, path2, file_fun, verbose)</strong> + </dt> + <dd> + clone a directory tree. Will always try to create a new directory structure + if necessary. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path1</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the base path of the source tree + </li> + <li><span class="parameter">path2</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the new base path for the destination + </li> + <li><span class="parameter">file_fun</span> + <span class="types"><span class="type">func</span></span> + an optional function to apply on all files + </li> + <li><span class="parameter">verbose</span> + <span class="types"><span class="type">bool</span></span> + an optional boolean to control the verbosity of the output. + It can also be a logging function that behaves like print() + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + true, or nil</li> + <li> + error message, or list of failed directory creations</li> + <li> + list of failed file operations</li> + </ol> + + <h3>Raises:</h3> + path1 and path2 must be strings + + + <h3>Usage:</h3> + <ul> + <pre class="example">clonetree(<span class="string">'.'</span>,<span class="string">'../backup'</span>,copyfile)</pre> + </ul> + +</dd> + <dt> + <a name = "dirtree"></a> + <strong>dirtree (d)</strong> + </dt> + <dd> + return an iterator over all entries in a directory tree + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">d</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a directory + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + an iterator giving pathname and mode (true for dir, false otherwise) + </ol> + + <h3>Raises:</h3> + d must be a non-empty string + + + +</dd> + <dt> + <a name = "getallfiles"></a> + <strong>getallfiles (start_path, shell_pattern)</strong> + </dt> + <dd> + Recursively returns all the file starting at <em>path</em>. It can optionally take a shell pattern and + only returns files that match <em>shell</em>pattern_. If a pattern is given it will do a case insensitive search. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">start_path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A directory. If not given, all files in current directory are returned. + </li> + <li><span class="parameter">shell_pattern</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A shell pattern. If not given, all files are returned. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="../classes/pl.List.html">List(string)</a></span> + containing all the files found recursively starting at <em>path</em> and filtered by <em>shell</em>pattern_. + </ol> + + <h3>Raises:</h3> + start_path must be a directory + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.file.html b/Data/Libraries/Penlight/docs/libraries/pl.file.html new file mode 100644 index 0000000..bd36aca --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.file.html @@ -0,0 +1,301 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><strong>pl.file</strong></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.file</code></h1> +<p>File manipulation functions: reading, writing, moving and copying.</p> +<p> This module wraps a number of functions from other modules into a + file related module for convenience.</p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.dir.html#">pl.dir</a>, <a href="../libraries/pl.path.html#">pl.path</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#read">read ()</a></td> + <td class="summary">return the contents of a file as a string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#write">write ()</a></td> + <td class="summary">write a string to a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#copy">copy ()</a></td> + <td class="summary">copy a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#move">move ()</a></td> + <td class="summary">move a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#access_time">access_time ()</a></td> + <td class="summary">Return the time of last access as the number of seconds since the epoch.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#creation_time">creation_time ()</a></td> + <td class="summary">Return when the file was created.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#modified_time">modified_time ()</a></td> + <td class="summary">Return the time of last modification.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#delete">delete ()</a></td> + <td class="summary">Delete a file.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "read"></a> + <strong>read ()</strong> + </dt> + <dd> + return the contents of a file as a string. + This function is a copy of <a href="../libraries/pl.utils.html#readfile">utils.readfile</a>. + + + + + + + +</dd> + <dt> + <a name = "write"></a> + <strong>write ()</strong> + </dt> + <dd> + write a string to a file. + This function is a copy of <a href="../libraries/pl.utils.html#writefile">utils.writefile</a>. + + + + + + + +</dd> + <dt> + <a name = "copy"></a> + <strong>copy ()</strong> + </dt> + <dd> + copy a file. + This function is a copy of <a href="../libraries/pl.dir.html#copyfile">dir.copyfile</a>. + + + + + + + +</dd> + <dt> + <a name = "move"></a> + <strong>move ()</strong> + </dt> + <dd> + move a file. + This function is a copy of <a href="../libraries/pl.dir.html#movefile">dir.movefile</a>. + + + + + + + +</dd> + <dt> + <a name = "access_time"></a> + <strong>access_time ()</strong> + </dt> + <dd> + Return the time of last access as the number of seconds since the epoch. + This function is a copy of <a href="../libraries/pl.path.html#getatime">path.getatime</a>. + + + + + + + +</dd> + <dt> + <a name = "creation_time"></a> + <strong>creation_time ()</strong> + </dt> + <dd> + Return when the file was created. + This function is a copy of <a href="../libraries/pl.path.html#getctime">path.getctime</a>. + + + + + + + +</dd> + <dt> + <a name = "modified_time"></a> + <strong>modified_time ()</strong> + </dt> + <dd> + Return the time of last modification. + This function is a copy of <a href="../libraries/pl.path.html#getmtime">path.getmtime</a>. + + + + + + + +</dd> + <dt> + <a name = "delete"></a> + <strong>delete ()</strong> + </dt> + <dd> + Delete a file. + This function is a copy of <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.remove">os.remove</a>. + + + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.func.html b/Data/Libraries/Penlight/docs/libraries/pl.func.html new file mode 100644 index 0000000..22e84a1 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.func.html @@ -0,0 +1,460 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><strong>pl.func</strong></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.func</code></h1> +<p>Functional helpers like composition, binding and placeholder expressions.</p> +<p> Placeholder expressions are useful for short anonymous functions, and were + inspired by the Boost Lambda library.</p> + + +<pre> +> utils.import <span class="string">'pl.func'</span> +> ls = List{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>} +> = ls:map(_1+<span class="number">1</span>) +{<span class="number">11</span>,<span class="number">21</span>,<span class="number">31</span>} +</pre> + +<p> They can also be used to <em>bind</em> particular arguments of a function.</p> + + +<pre> +> p = bind(<span class="global">print</span>,<span class="string">'start>'</span>,_0) +> p(<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>) +> start> <span class="number">10</span> <span class="number">20</span> <span class="number">30</span> +</pre> + +<p> See <a href="../manual/07-functional.md.html#Creating_Functions_from_Functions">the Guide</a></p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.tablex.html#">pl.tablex</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#import">import (tname, context)</a></td> + <td class="summary">wrap a table of functions.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#register">register (fun[, name])</a></td> + <td class="summary">register a function for use in placeholder expressions.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#tail">tail (ls)</a></td> + <td class="summary">all elements of a table except the first.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#repr">repr (e, lastpred)</a></td> + <td class="summary">create a string representation of a placeholder expression.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instantiate">instantiate (e)</a></td> + <td class="summary">instantiate a PE into an actual function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#I">I (e)</a></td> + <td class="summary">instantiate a PE unless it has already been done.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#bind1">bind1 (fn, p)</a></td> + <td class="summary">bind the first parameter of the function to a value.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#compose">compose (f, g)</a></td> + <td class="summary">create a function which chains two functions.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#bind">bind (fn, ...)</a></td> + <td class="summary">bind the arguments of a function to given values.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "import"></a> + <strong>import (tname, context)</strong> + </dt> + <dd> + wrap a table of functions. This makes them available for use in + placeholder expressions. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tname</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a table name + </li> + <li><span class="parameter">context</span> + <span class="types"><span class="type">tab</span></span> + context to put results, defaults to environment of caller + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "register"></a> + <strong>register (fun[, name])</strong> + </dt> + <dd> + register a function for use in placeholder expressions. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + an optional name + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a placeholder functiond + </ol> + + + + +</dd> + <dt> + <a name = "tail"></a> + <strong>tail (ls)</strong> + </dt> + <dd> + all elements of a table except the first. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">ls</span> + <span class="types"><span class="type">tab</span></span> + a list-like table. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "repr"></a> + <strong>repr (e, lastpred)</strong> + </dt> + <dd> + create a string representation of a placeholder expression. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">e</span> + a placeholder expression + </li> + <li><span class="parameter">lastpred</span> + not used + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "instantiate"></a> + <strong>instantiate (e)</strong> + </dt> + <dd> + instantiate a PE into an actual function. First we find the largest placeholder used, + e.g. <em>2; from this a list of the formal parameters can be build. Then we collect and replace + any non-PE values from the PE, and build up a constant binding list. + Finally, the expression can be compiled, and e.</em><em>PE</em>function is set. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">e</span> + a placeholder expression + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a function + </ol> + + + + +</dd> + <dt> + <a name = "I"></a> + <strong>I (e)</strong> + </dt> + <dd> + instantiate a PE unless it has already been done. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">e</span> + a placeholder expression + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the function + </ol> + + + + +</dd> + <dt> + <a name = "bind1"></a> + <strong>bind1 (fn, p)</strong> + </dt> + <dd> + bind the first parameter of the function to a value. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fn</span> + <span class="types"><span class="type">func</span></span> + a function of one or more arguments + </li> + <li><span class="parameter">p</span> + a value + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a function of one less argument + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">(bind1(<span class="global">math</span>.max,<span class="number">10</span>))(<span class="number">20</span>) == <span class="global">math</span>.max(<span class="number">10</span>,<span class="number">20</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "compose"></a> + <strong>compose (f, g)</strong> + </dt> + <dd> + create a function which chains two functions. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + <span class="types"><span class="type">func</span></span> + a function of at least one argument + </li> + <li><span class="parameter">g</span> + <span class="types"><span class="type">func</span></span> + a function of at least one argument + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a function + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">printf = compose(<span class="global">io</span>.write,<span class="global">string</span>.format)</pre> + </ul> + +</dd> + <dt> + <a name = "bind"></a> + <strong>bind (fn, ...)</strong> + </dt> + <dd> + bind the arguments of a function to given values. + <code>bind(fn,v,_2)</code> is equivalent to <code>bind1(fn,v)</code>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fn</span> + <span class="types"><span class="type">func</span></span> + a function of at least one argument + </li> + <li><span class="parameter">...</span> + values or placeholder variables + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a function + </ol> + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">(bind(f,_1,a))(b) == f(a,b)</pre></li> + <li><pre class="example">(bind(f,_2,_1))(a,b) == f(b,a)</pre></li> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.html b/Data/Libraries/Penlight/docs/libraries/pl.html new file mode 100644 index 0000000..f3d134f --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.html @@ -0,0 +1,139 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><strong>pl</strong></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl</code></h1> +<p>Entry point for loading all PL libraries only on demand, into the global space.</p> +<p> Requiring 'pl' means that whenever a module is implicitly accesssed + (e.g. <a href="../libraries/pl.utils.html#split">utils.split</a>) + then that module is dynamically loaded. The submodules are all brought into + the global space. +Updated to use <a href="../libraries/pl.import_into.html#">pl.import_into</a></p> + + + +<br/> +<br/> + + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.import_into.html b/Data/Libraries/Penlight/docs/libraries/pl.import_into.html new file mode 100644 index 0000000..5f92459 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.import_into.html @@ -0,0 +1,142 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><strong>pl.import_into</strong></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.import_into</code></h1> +<p>PL loader, for loading all PL libraries, only on demand.</p> +<p> Whenever a module is implicitly accesssed, the table will have the module automatically injected. + (e.g. <code>_ENV.tablex</code>) + then that module is dynamically loaded. The submodules are all brought into + the table that is provided as the argument, or returned in a new table. + If a table is provided, that table's metatable is clobbered, but the values are not. + This module returns a single function, which is passed the environment. + If this is <code>true</code>, then return a 'shadow table' as the module + See <a href="../manual/01-introduction.md.html#To_Inject_or_not_to_Inject_">the Guide</a></p> + + + +<br/> +<br/> + + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.input.html b/Data/Libraries/Penlight/docs/libraries/pl.input.html new file mode 100644 index 0000000..d0086fd --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.input.html @@ -0,0 +1,336 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><strong>pl.input</strong></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.input</code></h1> +<p>Iterators for extracting words or numbers from an input source.</p> +<p> + + + +<pre> +<span class="global">require</span> <span class="string">'pl'</span> +<span class="keyword">local</span> total,n = seq.sum(input.numbers()) +<span class="global">print</span>(<span class="string">'average'</span>,total/n) +</pre> + +<p> <em>source</em> is defined as a string or a file-like object (i.e. has a read() method which returns the next line)</p> + +<p> See <a href="../manual/06-data.md.html#Reading_Unstructured_Text_Data">here</a></p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a></p> +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#alltokens">alltokens (getter, pattern[, fn])</a></td> + <td class="summary">create an iterator over all tokens.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#create_getter">create_getter (f)</a></td> + <td class="summary">create a function which grabs the next value from a source.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#numbers">numbers (f)</a></td> + <td class="summary">generate a sequence of numbers from a source.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#words">words (f)</a></td> + <td class="summary">generate a sequence of words from a source.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#fields">fields (ids, delim, f, opts)</a></td> + <td class="summary">parse an input source into fields.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "alltokens"></a> + <strong>alltokens (getter, pattern[, fn])</strong> + </dt> + <dd> + create an iterator over all tokens. + based on allwords from PiL, 7.1 + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">getter</span> + <span class="types"><span class="type">func</span></span> + any function that returns a line of text + </li> + <li><span class="parameter">pattern</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + + + + </li> + <li><span class="parameter">fn</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + Optionally can pass a function to process each token as it's found. + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + an iterator + </ol> + + + + +</dd> + <dt> + <a name = "create_getter"></a> + <strong>create_getter (f)</strong> + </dt> + <dd> + create a function which grabs the next value from a source. If the source is a string, then the getter + will return the string and thereafter return nil. If not specified then the source is assumed to be stdin. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + a string or a file-like object (i.e. has a read() method which returns the next line) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a getter function + </ol> + + + + +</dd> + <dt> + <a name = "numbers"></a> + <strong>numbers (f)</strong> + </dt> + <dd> + generate a sequence of numbers from a source. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + A source + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + An iterator + </ol> + + + + +</dd> + <dt> + <a name = "words"></a> + <strong>words (f)</strong> + </dt> + <dd> + generate a sequence of words from a source. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + A source + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + An iterator + </ol> + + + + +</dd> + <dt> + <a name = "fields"></a> + <strong>fields (ids, delim, f, opts)</strong> + </dt> + <dd> + parse an input source into fields. + By default, will fail if it cannot convert a field to a number. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">ids</span> + a list of field indices, or a maximum field index + </li> + <li><span class="parameter">delim</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + delimiter to parse fields (default space) + </li> + <li><span class="parameter">f</span> + a source @see create_getter + </li> + <li><span class="parameter">opts</span> + <span class="types"><span class="type">tab</span></span> + option table, <code>{no_fail=true}</code> + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + an iterator with the field values + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">for</span> x,y <span class="keyword">in</span> fields {<span class="number">2</span>,<span class="number">3</span>} <span class="keyword">do</span> <span class="global">print</span>(x,y) <span class="keyword">end</span> <span class="comment">-- 2nd and 3rd fields from stdin</span></pre> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.lapp.html b/Data/Libraries/Penlight/docs/libraries/pl.lapp.html new file mode 100644 index 0000000..24a1079 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.lapp.html @@ -0,0 +1,382 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +<li><a href="#Fields">Fields</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><strong>pl.lapp</strong></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.lapp</code></h1> +<p>Simple command-line parsing using human-readable specification.</p> +<p> Supports GNU-style parameters.</p> + + +<pre> +lapp = <span class="global">require</span> <span class="string">'pl.lapp'</span> +<span class="keyword">local</span> args = lapp <span class="string">[[ +Does some calculations + -o,--offset (default 0.0) Offset to add to scaled number + -s,--scale (number) Scaling factor + <number> (number) Number to be scaled +]]</span> + +<span class="global">print</span>(args.offset + args.scale * args.number) +</pre> + +<p> Lines beginning with <code>'-'</code> are flags; there may be a short and a long name; + lines beginning with <code>'<var>'</code> are arguments. Anything in parens after + the flag/argument is either a default, a type name or a range constraint.</p> + +<p> See <a href="../manual/08-additional.md.html#Command_line_Programs_with_Lapp">the Guide</a></p> + +<p> Dependencies: <a href="../libraries/pl.sip.html#">pl.sip</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#quit">quit (msg, no_usage)</a></td> + <td class="summary">quit this script immediately.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#error">error (msg, no_usage)</a></td> + <td class="summary">print an error to stderr and quit.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#open">open (file[, opt])</a></td> + <td class="summary">open a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#assert">assert (condn, msg)</a></td> + <td class="summary">quit if the condition is false.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#add_type">add_type (name, converter[, constraint])</a></td> + <td class="summary">add a new type to Lapp.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#process_options_string">process_options_string (str, args)</a></td> + <td class="summary">process a Lapp options string.</td> + </tr> +</table> +<h2><a href="#Fields">Fields</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#show_usage_error">show_usage_error</a></td> + <td class="summary">controls whether to dump usage on error.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "quit"></a> + <strong>quit (msg, no_usage)</strong> + </dt> + <dd> + quit this script immediately. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">msg</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + optional message + </li> + <li><span class="parameter">no_usage</span> + <span class="types"><span class="type">bool</span></span> + suppress 'usage' display + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "error"></a> + <strong>error (msg, no_usage)</strong> + </dt> + <dd> + print an error to stderr and quit. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">msg</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a message + </li> + <li><span class="parameter">no_usage</span> + <span class="types"><span class="type">bool</span></span> + suppress 'usage' display + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "open"></a> + <strong>open (file[, opt])</strong> + </dt> + <dd> + open a file. + This will quit on error, and keep a list of file objects for later cleanup. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">file</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + filename + </li> + <li><span class="parameter">opt</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + same as second parameter of <a href="https://www.lua.org/manual/5.1/manual.html#pdf-io.open">io.open</a> + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "assert"></a> + <strong>assert (condn, msg)</strong> + </dt> + <dd> + quit if the condition is false. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">condn</span> + <span class="types"><span class="type">bool</span></span> + a condition + </li> + <li><span class="parameter">msg</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + message text + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "add_type"></a> + <strong>add_type (name, converter[, constraint])</strong> + </dt> + <dd> + add a new type to Lapp. These appear in parens after the value like + a range constraint, e.g. '<ival> (integer) Process PID' + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + name of type + </li> + <li><span class="parameter">converter</span> + either a function to convert values, or a Lua type name. + </li> + <li><span class="parameter">constraint</span> + <span class="types"><span class="type">func</span></span> + optional function to verify values, should use lapp.error + if failed. + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "process_options_string"></a> + <strong>process_options_string (str, args)</strong> + </dt> + <dd> + process a Lapp options string. + Usually called as <code>lapp()</code>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">str</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the options text + </li> + <li><span class="parameter">args</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">{string}</a></span> + a table of arguments (default is <code>_G.arg</code>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a table with parameter-value pairs + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Fields"></a>Fields</h2> + + <dl class="function"> + <dt> + <a name = "show_usage_error"></a> + <strong>show_usage_error</strong> + </dt> + <dd> + controls whether to dump usage on error. + Defaults to true + + + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.lexer.html b/Data/Libraries/Penlight/docs/libraries/pl.lexer.html new file mode 100644 index 0000000..7b3d6fd --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.lexer.html @@ -0,0 +1,524 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><strong>pl.lexer</strong></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.lexer</code></h1> +<p>Lexical scanner for creating a sequence of tokens from text.</p> +<p> <code>lexer.scan(s)</code> returns an iterator over all tokens found in the + string <code>s</code>. This iterator returns two values, a token type string + (such as 'string' for quoted string, 'iden' for identifier) and the value of the + token.</p> + +<p> Versions specialized for Lua and C are available; these also handle block comments + and classify keywords as 'keyword' tokens. For example:</p> + + +<pre> +> s = <span class="string">'for i=1,n do'</span> +> <span class="keyword">for</span> t,v <span class="keyword">in</span> lexer.lua(s) <span class="keyword">do</span> <span class="global">print</span>(t,v) <span class="keyword">end</span> +keyword <span class="keyword">for</span> +iden i += = +number <span class="number">1</span> +, , +iden n +keyword <span class="keyword">do</span> +</pre> + +<p> See the Guide for further <a href="../manual/06-data.md.html#Lexical_Scanning">discussion</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#scan">scan (s, matches[, filter[, options]])</a></td> + <td class="summary">create a plain token iterator from a string or file-like object.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#insert">insert (tok, a1, a2)</a></td> + <td class="summary">insert tokens into a stream.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#getline">getline (tok)</a></td> + <td class="summary">get everything in a stream upto a newline.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#lineno">lineno (tok)</a></td> + <td class="summary">get current line number.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#getrest">getrest (tok)</a></td> + <td class="summary">get the rest of the stream.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#get_keywords">get_keywords ()</a></td> + <td class="summary">get the Lua keywords as a set-like table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#lua">lua (s[, filter[, options]])</a></td> + <td class="summary">create a Lua token iterator from a string or file-like object.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#cpp">cpp (s[, filter[, options]])</a></td> + <td class="summary">create a C/C++ token iterator from a string or file-like object.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#get_separated_list">get_separated_list (tok[, endtoken=')'[, delim=']])</a></td> + <td class="summary">get a list of parameters separated by a delimiter from a stream.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#skipws">skipws (tok)</a></td> + <td class="summary">get the next non-space token from the stream.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#expecting">expecting (tok, expected_type, no_skip_ws)</a></td> + <td class="summary">get the next token, which must be of the expected type.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "scan"></a> + <strong>scan (s, matches[, filter[, options]])</strong> + </dt> + <dd> + create a plain token iterator from a string or file-like object. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">file</span></span> + a string or a file-like object with <code>:read()</code> method returning lines. + </li> + <li><span class="parameter">matches</span> + <span class="types"><span class="type">tab</span></span> + an optional match table - array of token descriptions. + A token is described by a <code>{pattern, action}</code> pair, where <code>pattern</code> should match + token body and <code>action</code> is a function called when a token of described type is found. + </li> + <li><span class="parameter">filter</span> + <span class="types"><span class="type">tab</span></span> + a table of token types to exclude, by default <code>{space=true}</code> + (<em>optional</em>) + </li> + <li><span class="parameter">options</span> + <span class="types"><span class="type">tab</span></span> + a table of options; by default, <code>{number=true,string=true}</code>, + which means convert numbers and strip string quotes. + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "insert"></a> + <strong>insert (tok, a1, a2)</strong> + </dt> + <dd> + insert tokens into a stream. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tok</span> + a token stream + </li> + <li><span class="parameter">a1</span> + a string is the type, a table is a token list and + a function is assumed to be a token-like iterator (returns type & value) + </li> + <li><span class="parameter">a2</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a string is the value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "getline"></a> + <strong>getline (tok)</strong> + </dt> + <dd> + get everything in a stream upto a newline. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tok</span> + a token stream + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a string + </ol> + + + + +</dd> + <dt> + <a name = "lineno"></a> + <strong>lineno (tok)</strong> + </dt> + <dd> + get current line number. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tok</span> + a token stream + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the line number. + if the input source is a file-like object, + also return the column. + </ol> + + + + +</dd> + <dt> + <a name = "getrest"></a> + <strong>getrest (tok)</strong> + </dt> + <dd> + get the rest of the stream. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tok</span> + a token stream + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a string + </ol> + + + + +</dd> + <dt> + <a name = "get_keywords"></a> + <strong>get_keywords ()</strong> + </dt> + <dd> + get the Lua keywords as a set-like table. + So <code>res["and"]</code> etc would be <code>true</code>. + + + + <h3>Returns:</h3> + <ol> + + a table + </ol> + + + + +</dd> + <dt> + <a name = "lua"></a> + <strong>lua (s[, filter[, options]])</strong> + </dt> + <dd> + create a Lua token iterator from a string or file-like object. + Will return the token type and value. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">filter</span> + <span class="types"><span class="type">tab</span></span> + a table of token types to exclude, by default <code>{space=true,comments=true}</code> + (<em>optional</em>) + </li> + <li><span class="parameter">options</span> + <span class="types"><span class="type">tab</span></span> + a table of options; by default, <code>{number=true,string=true}</code>, + which means convert numbers and strip string quotes. + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "cpp"></a> + <strong>cpp (s[, filter[, options]])</strong> + </dt> + <dd> + create a C/C++ token iterator from a string or file-like object. + Will return the token type type and value. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">filter</span> + <span class="types"><span class="type">tab</span></span> + a table of token types to exclude, by default <code>{space=true,comments=true}</code> + (<em>optional</em>) + </li> + <li><span class="parameter">options</span> + <span class="types"><span class="type">tab</span></span> + a table of options; by default, <code>{number=true,string=true}</code>, + which means convert numbers and strip string quotes. + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "get_separated_list"></a> + <strong>get_separated_list (tok[, endtoken=')'[, delim=']])</strong> + </dt> + <dd> + get a list of parameters separated by a delimiter from a stream. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tok</span> + the token stream + </li> + <li><span class="parameter">endtoken</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + end of list. Can be '\n' + (<em>default</em> ')') + </li> + <li><span class="parameter">delim</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + separator + (<em>default</em> ') + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a list of token lists. + </ol> + + + + +</dd> + <dt> + <a name = "skipws"></a> + <strong>skipws (tok)</strong> + </dt> + <dd> + get the next non-space token from the stream. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tok</span> + the token stream. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "expecting"></a> + <strong>expecting (tok, expected_type, no_skip_ws)</strong> + </dt> + <dd> + get the next token, which must be of the expected type. + Throws an error if this type does not match! + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tok</span> + the token stream + </li> + <li><span class="parameter">expected_type</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the token type + </li> + <li><span class="parameter">no_skip_ws</span> + <span class="types"><span class="type">bool</span></span> + whether we should skip whitespace + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.luabalanced.html b/Data/Libraries/Penlight/docs/libraries/pl.luabalanced.html new file mode 100644 index 0000000..48f6c63 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.luabalanced.html @@ -0,0 +1,149 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><strong>pl.luabalanced</strong></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.luabalanced</code></h1> +<p>Extract delimited Lua sequences from strings.</p> +<p> Inspired by Damian Conway's Text::Balanced in Perl. <br/> + <ul> + <li>[1] <a href="http://lua-users.org/wiki/LuaBalanced">Lua Wiki Page</a></li> + <li>[2] http://search.cpan.org/dist/Text-Balanced/lib/Text/Balanced.pm</li> + </ul> <br/> + <pre class=example> + local lb = require "pl.luabalanced" + --Extract Lua expression starting at position 4. + print(lb.match_expression("if x^2 + x > 5 then print(x) end", 4)) + --> x^2 + x > 5 16 + --Extract Lua string starting at (default) position 1. + print(lb.match_string([["test\"123" .. "more"]])) + --> "test\"123" 12 + </pre> + (c) 2008, David Manura, Licensed under the same terms as Lua (MIT license).</p> + + + +<br/> +<br/> + + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.operator.html b/Data/Libraries/Penlight/docs/libraries/pl.operator.html new file mode 100644 index 0000000..fdd4cfb --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.operator.html @@ -0,0 +1,819 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +<li><a href="#Tables">Tables</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><strong>pl.operator</strong></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.operator</code></h1> +<p>Lua operators available as functions.</p> +<p> (similar to the Python module of the same name)</p> + +<p> There is a module field <a href="../libraries/pl.operator.html#optable">optable</a> which maps the operator strings + onto these functions, e.g. <code>operator.optable['()']==operator.call</code></p> + +<p> Operator strings like '>' and '{}' can be passed to most Penlight functions + expecting a function argument.</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#call">call (fn, ...)</a></td> + <td class="summary">apply function to some arguments <strong>()</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#index">index (t, k)</a></td> + <td class="summary">get the indexed value from a table <strong>[]</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#eq">eq (a, b)</a></td> + <td class="summary">returns true if arguments are equal <strong>==</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#neq">neq (a, b)</a></td> + <td class="summary">returns true if arguments are not equal <strong>~=</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#lt">lt (a, b)</a></td> + <td class="summary">returns true if a is less than b <strong><</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#le">le (a, b)</a></td> + <td class="summary">returns true if a is less or equal to b <strong><=</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#gt">gt (a, b)</a></td> + <td class="summary">returns true if a is greater than b <strong>></strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#ge">ge (a, b)</a></td> + <td class="summary">returns true if a is greater or equal to b <strong>>=</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#len">len (a)</a></td> + <td class="summary">returns length of string or table <strong>#</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#add">add (a, b)</a></td> + <td class="summary">add two values <strong>+</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#sub">sub (a, b)</a></td> + <td class="summary">subtract b from a <strong>-</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#mul">mul (a, b)</a></td> + <td class="summary">multiply two values <strong>*</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#div">div (a, b)</a></td> + <td class="summary">divide first value by second <strong>/</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#pow">pow (a, b)</a></td> + <td class="summary">raise first to the power of second <strong>^</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#mod">mod (a, b)</a></td> + <td class="summary">modulo; remainder of a divided by b <strong>%</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#concat">concat (a, b)</a></td> + <td class="summary">concatenate two values (either strings or <code>__concat</code> defined) <strong>..</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#unm">unm (a)</a></td> + <td class="summary">return the negative of a value <strong>-</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#lnot">lnot (a)</a></td> + <td class="summary">false if value evaluates as true <strong>not</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#land">land (a, b)</a></td> + <td class="summary">true if both values evaluate as true <strong>and</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#lor">lor (a, b)</a></td> + <td class="summary">true if either value evaluate as true <strong>or</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#table">table (...)</a></td> + <td class="summary">make a table from the arguments <strong>{}</strong></td> + </tr> + <tr> + <td class="name" nowrap><a href="#match">match (a, b)</a></td> + <td class="summary">match two strings <strong>~</strong>.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#nop">nop (...)</a></td> + <td class="summary">the null operation.</td> + </tr> +</table> +<h2><a href="#Tables">Tables</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#optable">optable</a></td> + <td class="summary">Map from operator symbol to function.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "call"></a> + <strong>call (fn, ...)</strong> + </dt> + <dd> + apply function to some arguments <strong>()</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fn</span> + a function or callable object + </li> + <li><span class="parameter">...</span> + arguments + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "index"></a> + <strong>index (t, k)</strong> + </dt> + <dd> + get the indexed value from a table <strong>[]</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + a table or any indexable object + </li> + <li><span class="parameter">k</span> + the key + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "eq"></a> + <strong>eq (a, b)</strong> + </dt> + <dd> + returns true if arguments are equal <strong>==</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "neq"></a> + <strong>neq (a, b)</strong> + </dt> + <dd> + returns true if arguments are not equal <strong>~=</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "lt"></a> + <strong>lt (a, b)</strong> + </dt> + <dd> + returns true if a is less than b <strong><</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "le"></a> + <strong>le (a, b)</strong> + </dt> + <dd> + returns true if a is less or equal to b <strong><=</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "gt"></a> + <strong>gt (a, b)</strong> + </dt> + <dd> + returns true if a is greater than b <strong>></strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "ge"></a> + <strong>ge (a, b)</strong> + </dt> + <dd> + returns true if a is greater or equal to b <strong>>=</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "len"></a> + <strong>len (a)</strong> + </dt> + <dd> + returns length of string or table <strong>#</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + a string or a table + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "add"></a> + <strong>add (a, b)</strong> + </dt> + <dd> + add two values <strong>+</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "sub"></a> + <strong>sub (a, b)</strong> + </dt> + <dd> + subtract b from a <strong>-</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "mul"></a> + <strong>mul (a, b)</strong> + </dt> + <dd> + multiply two values <strong>*</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "div"></a> + <strong>div (a, b)</strong> + </dt> + <dd> + divide first value by second <strong>/</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "pow"></a> + <strong>pow (a, b)</strong> + </dt> + <dd> + raise first to the power of second <strong>^</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "mod"></a> + <strong>mod (a, b)</strong> + </dt> + <dd> + modulo; remainder of a divided by b <strong>%</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "concat"></a> + <strong>concat (a, b)</strong> + </dt> + <dd> + concatenate two values (either strings or <code>__concat</code> defined) <strong>..</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "unm"></a> + <strong>unm (a)</strong> + </dt> + <dd> + return the negative of a value <strong>-</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "lnot"></a> + <strong>lnot (a)</strong> + </dt> + <dd> + false if value evaluates as true <strong>not</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "land"></a> + <strong>land (a, b)</strong> + </dt> + <dd> + true if both values evaluate as true <strong>and</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "lor"></a> + <strong>lor (a, b)</strong> + </dt> + <dd> + true if either value evaluate as true <strong>or</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + value + </li> + <li><span class="parameter">b</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "table"></a> + <strong>table (...)</strong> + </dt> + <dd> + make a table from the arguments <strong>{}</strong> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + non-nil arguments + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a table + </ol> + + + + +</dd> + <dt> + <a name = "match"></a> + <strong>match (a, b)</strong> + </dt> + <dd> + match two strings <strong>~</strong>. + uses <a href="https://www.lua.org/manual/5.1/manual.html#pdf-string.find">string.find</a> + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + + + + </li> + <li><span class="parameter">b</span> + + + + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "nop"></a> + <strong>nop (...)</strong> + </dt> + <dd> + the null operation. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + arguments + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the arguments + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Tables"></a>Tables</h2> + + <dl class="function"> + <dt> + <a name = "optable"></a> + <strong>optable</strong> + </dt> + <dd> + +<p>Map from operator symbol to function. + Most of these map directly from operators; + But note these extras</p> + +<ul> + <li><strong>'()'</strong> <a href="../libraries/pl.operator.html#call">call</a></li> + <li><strong>'[]'</strong> <a href="../libraries/pl.operator.html#index">index</a></li> + <li><strong>'{}'</strong> <a href="../libraries/pl.operator.html#table">table</a></li> + <li><strong>'~'</strong> <a href="../libraries/pl.operator.html#match">match</a></li> +</ul> + + + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">operator</span> + + + + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.path.html b/Data/Libraries/Penlight/docs/libraries/pl.path.html new file mode 100644 index 0000000..6345f6b --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.path.html @@ -0,0 +1,1070 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +<li><a href="#Fields">Fields</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><strong>pl.path</strong></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.path</code></h1> +<p>Path manipulation and file queries.</p> +<p> This is modelled after Python's os.path library (10.1); see <a href="../manual/04-paths.md.html#">the Guide</a>.</p> + +<p> NOTE: the functions assume the paths being dealt with to originate + from the OS the application is running on. Windows drive letters are not + to be used when running on a Unix system for example. The one exception + is Windows paths to allow both forward and backward slashes (since Lua + also accepts those)</p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="http://stevedonovan.github.io/lua-stdlibs/modules/lfs.html">lfs</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#dir">dir ()</a></td> + <td class="summary">Lua iterator over the entries of a given directory.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#mkdir">mkdir ()</a></td> + <td class="summary">Creates a directory.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#rmdir">rmdir ()</a></td> + <td class="summary">Removes a directory.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#attrib">attrib ()</a></td> + <td class="summary">Gets attributes.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#currentdir">currentdir ()</a></td> + <td class="summary">Get the working directory.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#link_attrib">link_attrib ()</a></td> + <td class="summary">Gets symlink attributes.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#chdir">chdir ()</a></td> + <td class="summary">Changes the working directory.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#isdir">isdir (P)</a></td> + <td class="summary">is this a directory?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#isfile">isfile (P)</a></td> + <td class="summary">is this a file?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#getsize">getsize (P)</a></td> + <td class="summary">return size of a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#exists">exists (P)</a></td> + <td class="summary">does a path exist?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#getatime">getatime (P)</a></td> + <td class="summary">Return the time of last access as the number of seconds since the epoch.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#getmtime">getmtime (P)</a></td> + <td class="summary">Return the time of last modification as the number of seconds since the epoch.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#getctime">getctime (P)</a></td> + <td class="summary">Return the system's ctime as the number of seconds since the epoch.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#splitpath">splitpath (P)</a></td> + <td class="summary">given a path, return the directory part and a file part.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#abspath">abspath (P[, pwd])</a></td> + <td class="summary">return an absolute path.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#splitext">splitext (P)</a></td> + <td class="summary">given a path, return the root part and the extension part.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#dirname">dirname (P)</a></td> + <td class="summary">return the directory part of a path</td> + </tr> + <tr> + <td class="name" nowrap><a href="#basename">basename (P)</a></td> + <td class="summary">return the file part of a path</td> + </tr> + <tr> + <td class="name" nowrap><a href="#extension">extension (P)</a></td> + <td class="summary">get the extension part of a path.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#isabs">isabs (P)</a></td> + <td class="summary">is this an absolute path?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#join">join (p1, p2, ...)</a></td> + <td class="summary">return the path resulting from combining the individual paths.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#normcase">normcase (P)</a></td> + <td class="summary">normalize the case of a pathname.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#normpath">normpath (P)</a></td> + <td class="summary">normalize a path name.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#relpath">relpath (P[, start])</a></td> + <td class="summary">relative path from current directory or optional start point</td> + </tr> + <tr> + <td class="name" nowrap><a href="#expanduser">expanduser (P)</a></td> + <td class="summary">Replace a starting '~' with the user's home directory.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#tmpname">tmpname ()</a></td> + <td class="summary">Return a suitable full path to a new temporary file name.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#common_prefix">common_prefix (path1, path2)</a></td> + <td class="summary">return the largest common prefix path of two paths.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#package_path">package_path (mod)</a></td> + <td class="summary">return the full path where a particular Lua module would be found.</td> + </tr> +</table> +<h2><a href="#Fields">Fields</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#is_windows">is_windows</a></td> + <td class="summary">are we running Windows?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sep">sep</a></td> + <td class="summary">path separator for this platform.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#dirsep">dirsep</a></td> + <td class="summary">separator for PATH for this platform</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "dir"></a> + <strong>dir ()</strong> + </dt> + <dd> + Lua iterator over the entries of a given directory. + Implicit link to <a href="https://keplerproject.github.io/luafilesystem/manual.html#reference"><code>luafilesystem.dir</code></a> + + + + + + + +</dd> + <dt> + <a name = "mkdir"></a> + <strong>mkdir ()</strong> + </dt> + <dd> + Creates a directory. + Implicit link to <a href="https://keplerproject.github.io/luafilesystem/manual.html#reference"><code>luafilesystem.mkdir</code></a> + + + + + + + +</dd> + <dt> + <a name = "rmdir"></a> + <strong>rmdir ()</strong> + </dt> + <dd> + Removes a directory. + Implicit link to <a href="https://keplerproject.github.io/luafilesystem/manual.html#reference"><code>luafilesystem.rmdir</code></a> + + + + + + + +</dd> + <dt> + <a name = "attrib"></a> + <strong>attrib ()</strong> + </dt> + <dd> + Gets attributes. + Implicit link to <a href="https://keplerproject.github.io/luafilesystem/manual.html#reference"><code>luafilesystem.attributes</code></a> + + + + + + + +</dd> + <dt> + <a name = "currentdir"></a> + <strong>currentdir ()</strong> + </dt> + <dd> + Get the working directory. + Implicit link to <a href="https://keplerproject.github.io/luafilesystem/manual.html#reference"><code>luafilesystem.currentdir</code></a> + + + + + + + +</dd> + <dt> + <a name = "link_attrib"></a> + <strong>link_attrib ()</strong> + </dt> + <dd> + Gets symlink attributes. + Implicit link to <a href="https://keplerproject.github.io/luafilesystem/manual.html#reference"><code>luafilesystem.symlinkattributes</code></a> + + + + + + + +</dd> + <dt> + <a name = "chdir"></a> + <strong>chdir ()</strong> + </dt> + <dd> + Changes the working directory. + On Windows, if a drive is specified, it also changes the current drive. If + only specifying the drive, it will only switch drive, but not modify the path. + Implicit link to <a href="https://keplerproject.github.io/luafilesystem/manual.html#reference"><code>luafilesystem.chdir</code></a> + + + + + + + +</dd> + <dt> + <a name = "isdir"></a> + <strong>isdir (P)</strong> + </dt> + <dd> + is this a directory? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "isfile"></a> + <strong>isfile (P)</strong> + </dt> + <dd> + is this a file? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "getsize"></a> + <strong>getsize (P)</strong> + </dt> + <dd> + return size of a file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "exists"></a> + <strong>exists (P)</strong> + </dt> + <dd> + does a path exist? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the file path if it exists (either as file, directory, socket, etc), nil otherwise + </ol> + + + + +</dd> + <dt> + <a name = "getatime"></a> + <strong>getatime (P)</strong> + </dt> + <dd> + Return the time of last access as the number of seconds since the epoch. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "getmtime"></a> + <strong>getmtime (P)</strong> + </dt> + <dd> + Return the time of last modification as the number of seconds since the epoch. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "getctime"></a> + <strong>getctime (P)</strong> + </dt> + <dd> + Return the system's ctime as the number of seconds since the epoch. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "splitpath"></a> + <strong>splitpath (P)</strong> + </dt> + <dd> + given a path, return the directory part and a file part. + if there's no directory part, the first value will be empty + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + directory part</li> + <li> + file part</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> dir, file = path.splitpath(<span class="string">"some/dir/myfile.txt"</span>) +<span class="global">assert</span>(dir == <span class="string">"some/dir"</span>) +<span class="global">assert</span>(file == <span class="string">"myfile.txt"</span>) + +<span class="keyword">local</span> dir, file = path.splitpath(<span class="string">"some/dir/"</span>) +<span class="global">assert</span>(dir == <span class="string">"some/dir"</span>) +<span class="global">assert</span>(file == <span class="string">""</span>) + +<span class="keyword">local</span> dir, file = path.splitpath(<span class="string">"some_dir"</span>) +<span class="global">assert</span>(dir == <span class="string">""</span>) +<span class="global">assert</span>(file == <span class="string">"some_dir"</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "abspath"></a> + <strong>abspath (P[, pwd])</strong> + </dt> + <dd> + return an absolute path. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + <li><span class="parameter">pwd</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + optional start path to use (default is current dir) + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "splitext"></a> + <strong>splitext (P)</strong> + </dt> + <dd> + given a path, return the root part and the extension part. + if there's no extension part, the second value will be empty + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + root part (everything upto the "."", maybe empty)</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + extension part (including the ".", maybe empty)</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> file_path, ext = path.splitext(<span class="string">"/bonzo/dog_stuff/cat.txt"</span>) +<span class="global">assert</span>(file_path == <span class="string">"/bonzo/dog_stuff/cat"</span>) +<span class="global">assert</span>(ext == <span class="string">".txt"</span>) + +<span class="keyword">local</span> file_path, ext = path.splitext(<span class="string">""</span>) +<span class="global">assert</span>(file_path == <span class="string">""</span>) +<span class="global">assert</span>(ext == <span class="string">""</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "dirname"></a> + <strong>dirname (P)</strong> + </dt> + <dd> + return the directory part of a path + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + everything before the last dir-separator + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.path.html#splitpath">splitpath</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example">path.dirname(<span class="string">"/some/path/file.txt"</span>) <span class="comment">-- "/some/path" +</span>path.dirname(<span class="string">"file.txt"</span>) <span class="comment">-- "" (empty string)</span></pre> + </ul> + +</dd> + <dt> + <a name = "basename"></a> + <strong>basename (P)</strong> + </dt> + <dd> + return the file part of a path + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + + + + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.path.html#splitpath">splitpath</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example">path.basename(<span class="string">"/some/path/file.txt"</span>) <span class="comment">-- "file.txt" +</span>path.basename(<span class="string">"/some/path/file/"</span>) <span class="comment">-- "" (empty string)</span></pre> + </ul> + +</dd> + <dt> + <a name = "extension"></a> + <strong>extension (P)</strong> + </dt> + <dd> + get the extension part of a path. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + + + + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.path.html#splitext">splitext</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example">path.extension(<span class="string">"/some/path/file.txt"</span>) <span class="comment">-- ".txt" +</span>path.extension(<span class="string">"/some/path/file_txt"</span>) <span class="comment">-- "" (empty string)</span></pre> + </ul> + +</dd> + <dt> + <a name = "isabs"></a> + <strong>isabs (P)</strong> + </dt> + <dd> + is this an absolute path? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">path.isabs(<span class="string">"hello/path"</span>) <span class="comment">-- false +</span>path.isabs(<span class="string">"/hello/path"</span>) <span class="comment">-- true +</span><span class="comment">-- Windows; +</span>path.isabs(<span class="string">"hello\path"</span>) <span class="comment">-- false +</span>path.isabs(<span class="string">"\hello\path"</span>) <span class="comment">-- true +</span>path.isabs(<span class="string">"C:\hello\path"</span>) <span class="comment">-- true +</span>path.isabs(<span class="string">"C:hello\path"</span>) <span class="comment">-- false</span></pre> + </ul> + +</dd> + <dt> + <a name = "join"></a> + <strong>join (p1, p2, ...)</strong> + </dt> + <dd> + return the path resulting from combining the individual paths. + if the second (or later) path is absolute, we return the last absolute path (joined with any non-absolute paths following). + empty elements (except the last) will be ignored. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">p1</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + <li><span class="parameter">p2</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + <li><span class="parameter">...</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + more file paths + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the combined path + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">path.join(<span class="string">"/first"</span>,<span class="string">"second"</span>,<span class="string">"third"</span>) <span class="comment">-- "/first/second/third" +</span>path.join(<span class="string">"first"</span>,<span class="string">"second/third"</span>) <span class="comment">-- "first/second/third" +</span>path.join(<span class="string">"/first"</span>,<span class="string">"/second"</span>,<span class="string">"third"</span>) <span class="comment">-- "/second/third"</span></pre> + </ul> + +</dd> + <dt> + <a name = "normcase"></a> + <strong>normcase (P)</strong> + </dt> + <dd> + +<p>normalize the case of a pathname. On Unix, this returns the path unchanged, + for Windows it converts;</p> + +<ul> + <li>the path to lowercase</li> + <li>forward slashes to backward slashes</li> +</ul> + + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">path.normcase(<span class="string">"/Some/Path/File.txt"</span>) +<span class="comment">-- Windows: "\some\path\file.txt" +</span><span class="comment">-- Others : "/Some/Path/File.txt"</span></pre> + </ul> + +</dd> + <dt> + <a name = "normpath"></a> + <strong>normpath (P)</strong> + </dt> + <dd> + normalize a path name. + <code>A//B</code>, <code>A/./B</code>, and <code>A/foo/../B</code> all become <code>A/B</code>.</p> + +<p> An empty path results in '.'. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a file path + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "relpath"></a> + <strong>relpath (P[, start])</strong> + </dt> + <dd> + relative path from current directory or optional start point + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a path + </li> + <li><span class="parameter">start</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + optional start point (default current directory) + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "expanduser"></a> + <strong>expanduser (P)</strong> + </dt> + <dd> + Replace a starting '~' with the user's home directory. + In windows, if HOME isn't set, then USERPROFILE is used in preference to + HOMEDRIVE HOMEPATH. This is guaranteed to be writeable on all versions of Windows. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">P</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + A file path + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "tmpname"></a> + <strong>tmpname ()</strong> + </dt> + <dd> + Return a suitable full path to a new temporary file name. + unlike os.tmpname(), it always gives you a writeable path (uses TEMP environment variable on Windows) + + + + + + + +</dd> + <dt> + <a name = "common_prefix"></a> + <strong>common_prefix (path1, path2)</strong> + </dt> + <dd> + return the largest common prefix path of two paths. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path1</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a file path + </li> + <li><span class="parameter">path2</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a file path + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the common prefix (Windows: separators will be normalized, casing will be original) + </ol> + + + + +</dd> + <dt> + <a name = "package_path"></a> + <strong>package_path (mod)</strong> + </dt> + <dd> + return the full path where a particular Lua module would be found. + Both package.path and package.cpath is searched, so the result may + either be a Lua file or a shared library. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">mod</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + name of the module + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + on success: path of module, lua or binary</li> + <li> + on error: nil, error string listing paths tried</li> + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Fields"></a>Fields</h2> + + <dl class="function"> + <dt> + <a name = "is_windows"></a> + <strong>is_windows</strong> + </dt> + <dd> + are we running Windows? + + + + + + + +</dd> + <dt> + <a name = "sep"></a> + <strong>sep</strong> + </dt> + <dd> + path separator for this platform. + + + + + + + +</dd> + <dt> + <a name = "dirsep"></a> + <strong>dirsep</strong> + </dt> + <dd> + separator for PATH for this platform + + + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.permute.html b/Data/Libraries/Penlight/docs/libraries/pl.permute.html new file mode 100644 index 0000000..afb887e --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.permute.html @@ -0,0 +1,354 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><strong>pl.permute</strong></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.permute</code></h1> +<p>Permutation operations.</p> +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.tablex.html#">pl.tablex</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#order_iter">order_iter (a)</a></td> + <td class="summary">an iterator over all order-permutations of the elements of a list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#order_table">order_table (a)</a></td> + <td class="summary">construct a table containing all the order-permutations of a list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#list_iter">list_iter (...)</a></td> + <td class="summary">an iterator over all permutations of the elements of the given lists.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#list_table">list_table (...)</a></td> + <td class="summary">construct a table containing all the permutations of a set of lists.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#iter">iter (...)</a></td> + <td class="summary">deprecated.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#table">table (...)</a></td> + <td class="summary">deprecated.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "order_iter"></a> + <strong>order_iter (a)</strong> + </dt> + <dd> + an iterator over all order-permutations of the elements of a list. + Please note that the same list is returned each time, so do not keep references! + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + list-like table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + an iterator which provides the next permutation as a list + </ol> + + + + +</dd> + <dt> + <a name = "order_table"></a> + <strong>order_table (a)</strong> + </dt> + <dd> + construct a table containing all the order-permutations of a list. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + list-like table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a table of tables + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">permute.order_table {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>} <span class="comment">--> {{2,3,1},{3,2,1},{3,1,2},{1,3,2},{2,1,3},{1,2,3}}</span></pre> + </ul> + +</dd> + <dt> + <a name = "list_iter"></a> + <strong>list_iter (...)</strong> + </dt> + <dd> + an iterator over all permutations of the elements of the given lists. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + list-like tables, they are nil-safe if a length-field <code>n</code> is provided (see <a href="../libraries/pl.utils.html#pack">utils.pack</a>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + an iterator which provides the next permutation as return values in the same order as the provided lists, preceeded by an index + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> strs = utils.pack(<span class="string">"one"</span>, <span class="keyword">nil</span>, <span class="string">"three"</span>) <span class="comment">-- adds an 'n' field for nil-safety +</span><span class="keyword">local</span> bools = utils.pack(<span class="keyword">true</span>, <span class="keyword">false</span>) +<span class="keyword">local</span> iter = permute.list_iter(strs, bools) + +<span class="global">print</span>(iter()) <span class="comment">--> 1, one, true +</span><span class="global">print</span>(iter()) <span class="comment">--> 2, nil, true +</span><span class="global">print</span>(iter()) <span class="comment">--> 3, three, true +</span><span class="global">print</span>(iter()) <span class="comment">--> 4, one, false +</span><span class="global">print</span>(iter()) <span class="comment">--> 5, nil, false +</span><span class="global">print</span>(iter()) <span class="comment">--> 6, three, false</span></pre> + </ul> + +</dd> + <dt> + <a name = "list_table"></a> + <strong>list_table (...)</strong> + </dt> + <dd> + construct a table containing all the permutations of a set of lists. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + list-like tables, they are nil-safe if a length-field <code>n</code> is provided + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a list of lists, the sub-lists have an 'n' field for nil-safety + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> strs = utils.pack(<span class="string">"one"</span>, <span class="keyword">nil</span>, <span class="string">"three"</span>) <span class="comment">-- adds an 'n' field for nil-safety +</span><span class="keyword">local</span> bools = utils.pack(<span class="keyword">true</span>, <span class="keyword">false</span>) +<span class="keyword">local</span> results = permute.list_table(strs, bools) +<span class="comment">-- results = { +</span><span class="comment">-- { "one, true, n = 2 } +</span><span class="comment">-- { nil, true, n = 2 }, +</span><span class="comment">-- { "three, true, n = 2 }, +</span><span class="comment">-- { "one, false, n = 2 }, +</span><span class="comment">-- { nil, false, n = 2 }, +</span><span class="comment">-- { "three", false, n = 2 }, +</span><span class="comment">-- }</span></pre> + </ul> + +</dd> + <dt> + <a name = "iter"></a> + <strong>iter (...)</strong> + </dt> + <dd> + deprecated. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + + + + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.permute.html#order_iter">permute.order_iter</a> + </ul> + + +</dd> + <dt> + <a name = "table"></a> + <strong>table (...)</strong> + </dt> + <dd> + deprecated. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + + + + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.permute.html#order_iter">permute.order_iter</a> + </ul> + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.pretty.html b/Data/Libraries/Penlight/docs/libraries/pl.pretty.html new file mode 100644 index 0000000..4561699 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.pretty.html @@ -0,0 +1,402 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><strong>pl.pretty</strong></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.pretty</code></h1> +<p>Pretty-printing Lua tables.</p> +<p> Also provides a sandboxed Lua table reader and + a function to present large numbers in human-friendly format.</p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.lexer.html#">pl.lexer</a>, <a href="../libraries/pl.stringx.html#">pl.stringx</a>, <a href="../libraries/pl.pretty.html#debug">debug</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#read">read (s)</a></td> + <td class="summary">Read a string representation of a Lua table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#load">load (s[, env[, paranoid]])</a></td> + <td class="summary">Read a Lua chunk.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#write">write (tbl[, space[, not_clever]])</a></td> + <td class="summary">Create a string representation of a Lua table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#dump">dump (t[, filename])</a></td> + <td class="summary">Dump a Lua table out to a file or stdout.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#debug">debug (...)</a></td> + <td class="summary">Dump a series of arguments to stdout for debug purposes.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#number">number (num[, kind[, prec]])</a></td> + <td class="summary">Format large numbers nicely for human consumption.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "read"></a> + <strong>read (s)</strong> + </dt> + <dd> + Read a string representation of a Lua table. + This function loads and runs the string as Lua code, but bails out + if it contains a function definition. + Loaded string is executed in an empty environment. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + string to read in <code>{...}</code> format, possibly with some whitespace + before or after the curly braces. A single line comment may be present + at the beginning. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a table in case of success. + If loading the string failed, return <code>nil</code> and error message. + If executing loaded string failed, return <code>nil</code> and the error it raised. + </ol> + + + + +</dd> + <dt> + <a name = "load"></a> + <strong>load (s[, env[, paranoid]])</strong> + </dt> + <dd> + Read a Lua chunk. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + Lua code. + </li> + <li><span class="parameter">env</span> + <span class="types"><span class="type">tab</span></span> + environment used to run the code, empty by default. + (<em>optional</em>) + </li> + <li><span class="parameter">paranoid</span> + <span class="types"><span class="type">bool</span></span> + abort loading if any looping constructs a found in the code + and disable string methods. + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the environment in case of success or <code>nil</code> and syntax or runtime error + if something went wrong. + </ol> + + + + +</dd> + <dt> + <a name = "write"></a> + <strong>write (tbl[, space[, not_clever]])</strong> + </dt> + <dd> + Create a string representation of a Lua table. + This function never fails, but may complain by returning an + extra value. Normally puts out one item per line, using + the provided indent; set the second parameter to an empty string + if you want output on one line.</p> + +<p> <em>NOTE:</em> this is NOT a serialization function, not a full blown + debug function. Checkout out respectively the + <a href="https://github.com/pkulchenko/serpent">serpent</a> + or <a href="https://github.com/kikito/inspect.lua">inspect</a> + Lua modules for that if you need them. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tbl</span> + <span class="types"><span class="type">tab</span></span> + Table to serialize to a string. + </li> + <li><span class="parameter">space</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + The indent to use. + Defaults to two spaces; pass an empty string for no indentation. + (<em>optional</em>) + </li> + <li><span class="parameter">not_clever</span> + <span class="types"><span class="type">bool</span></span> + Pass <code>true</code> for plain output, e.g <code>{['key']=1}</code>. + Defaults to <code>false</code>. + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + a string</li> + <li> + an optional error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "dump"></a> + <strong>dump (t[, filename])</strong> + </dt> + <dd> + Dump a Lua table out to a file or stdout. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + The table to write to a file or stdout. + </li> + <li><span class="parameter">filename</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + File name to write too. Defaults to writing + to stdout. + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "debug"></a> + <strong>debug (...)</strong> + </dt> + <dd> + Dump a series of arguments to stdout for debug purposes. + This function is attached to the module table <code>__call</code> method, to make it + extra easy to access. So the full:</p> + +<pre><code> print(require("pl.pretty").write({...})) +</code></pre> + +<p> Can be shortened to:</p> + +<pre><code> require"pl.pretty" (...) +</code></pre> + +<p> Any <code>nil</code> entries will be printed as <code>"<nil>"</code> to make them explicit. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + the parameters to dump to stdout. + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="comment">-- example debug output +</span><span class="global">require</span><span class="string">"pl.pretty"</span> (<span class="string">"hello"</span>, <span class="keyword">nil</span>, <span class="string">"world"</span>, { bye = <span class="string">"world"</span>, <span class="keyword">true</span>} ) + +<span class="comment">-- output: +</span>{ + [<span class="string">"arg 1"</span>] = <span class="string">"hello"</span>, + [<span class="string">"arg 2"</span>] = <span class="string">"<nil>"</span>, + [<span class="string">"arg 3"</span>] = <span class="string">"world"</span>, + [<span class="string">"arg 4"</span>] = { + <span class="keyword">true</span>, + bye = <span class="string">"world"</span> + } +}</pre> + </ul> + +</dd> + <dt> + <a name = "number"></a> + <strong>number (num[, kind[, prec]])</strong> + </dt> + <dd> + Format large numbers nicely for human consumption. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">num</span> + <span class="types"><a class="type" href="../libraries/pl.pretty.html#number">number</a></span> + a number. + </li> + <li><span class="parameter">kind</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + one of <code>'M'</code> (memory in <code>KiB</code>, <code>MiB</code>, etc.), + <code>'N'</code> (postfixes are <code>'K'</code>, <code>'M'</code> and <code>'B'</code>), + or <code>'T'</code> (use commas as thousands separator), <code>'N'</code> by default. + (<em>optional</em>) + </li> + <li><span class="parameter">prec</span> + <span class="types"><span class="type">int</span></span> + number of digits to use for <code>'M'</code> and <code>'N'</code>, <code>1</code> by default. + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.seq.html b/Data/Libraries/Penlight/docs/libraries/pl.seq.html new file mode 100644 index 0000000..e694581 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.seq.html @@ -0,0 +1,888 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><strong>pl.seq</strong></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.seq</code></h1> +<p>Manipulating iterators as sequences.</p> +<p> See <a href="../manual/07-functional.md.html#Sequences">The Guide</a></p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.types.html#">pl.types</a>, <a href="https://www.lua.org/manual/5.1/manual.html#5.9">debug</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#matching">matching (s)</a></td> + <td class="summary">given a string, return a function(y) which matches y against the string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#list">list (t)</a></td> + <td class="summary">sequence adaptor for a table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#keys">keys (t)</a></td> + <td class="summary">return the keys of the table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#range">range (start, finish)</a></td> + <td class="summary">create an iterator over a numerical range.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#minmax">minmax (iter)</a></td> + <td class="summary">return the minimum and the maximum value of the sequence.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sum">sum (iter, fn)</a></td> + <td class="summary">return the sum and element count of the sequence.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#copy">copy (iter)</a></td> + <td class="summary">create a table from the sequence.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#copy2">copy2 (iter, i1, i2)</a></td> + <td class="summary">create a table of pairs from the double-valued sequence.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#copy_tuples">copy_tuples (iter)</a></td> + <td class="summary">create a table of 'tuples' from a multi-valued sequence.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#random">random (n, l, u)</a></td> + <td class="summary">return an iterator of random numbers.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sort">sort (iter, comp)</a></td> + <td class="summary">return an iterator to the sorted elements of a sequence.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#zip">zip (iter1, iter2)</a></td> + <td class="summary">return an iterator which returns elements of two sequences.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#count_map">count_map (iter)</a></td> + <td class="summary">Makes a table where the key/values are the values and value counts of the sequence.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#printall">printall (iter, sep, nfields, fmt)</a></td> + <td class="summary">print out a sequence iter with a separator.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#map">map (fn, iter, arg)</a></td> + <td class="summary">return a sequence where every element of a sequence has been transformed + by a function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#filter">filter (iter, pred, arg)</a></td> + <td class="summary">filter a sequence using a predicate function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#reduce">reduce (fn, iter, initval)</a></td> + <td class="summary">'reduce' a sequence using a binary function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#take">take (iter, n)</a></td> + <td class="summary">take the first n values from the sequence.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#skip">skip (iter, n)</a></td> + <td class="summary">skip the first n values of a sequence</td> + </tr> + <tr> + <td class="name" nowrap><a href="#enum">enum (iter)</a></td> + <td class="summary">a sequence with a sequence count and the original value.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#mapmethod">mapmethod (iter, name, arg1, arg2)</a></td> + <td class="summary">map using a named method over a sequence.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#last">last (iter)</a></td> + <td class="summary">a sequence of (last,current) values from another sequence.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#foreach">foreach (iter, fn)</a></td> + <td class="summary">call the function on each element of the sequence.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#lines">lines (f, ...)</a></td> + <td class="summary">create a wrapped iterator over all lines in the file.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "matching"></a> + <strong>matching (s)</strong> + </dt> + <dd> + given a string, return a function(y) which matches y against the string. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + a string + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "list"></a> + <strong>list (t)</strong> + </dt> + <dd> + sequence adaptor for a table. Note that if any generic function is + passed a table, it will automatically use seq.list() + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + a list-like table + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">sum(list(t)) is the sum of all elements of t</pre></li> + <li><pre class="example"><span class="keyword">for</span> x <span class="keyword">in</span> list(t) <span class="keyword">do</span>...<span class="keyword">end</span></pre></li> + </ul> + +</dd> + <dt> + <a name = "keys"></a> + <strong>keys (t)</strong> + </dt> + <dd> + return the keys of the table. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + an arbitrary table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + iterator over keys + </ol> + + + + +</dd> + <dt> + <a name = "range"></a> + <strong>range (start, finish)</strong> + </dt> + <dd> + create an iterator over a numerical range. Like the standard Python function xrange. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">start</span> + a number + </li> + <li><span class="parameter">finish</span> + a number greater than start + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "minmax"></a> + <strong>minmax (iter)</strong> + </dt> + <dd> + return the minimum and the maximum value of the sequence. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + minimum value</li> + <li> + maximum value</li> + </ol> + + + + +</dd> + <dt> + <a name = "sum"></a> + <strong>sum (iter, fn)</strong> + </dt> + <dd> + return the sum and element count of the sequence. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence + </li> + <li><span class="parameter">fn</span> + an optional function to apply to the values + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "copy"></a> + <strong>copy (iter)</strong> + </dt> + <dd> + create a table from the sequence. (This will make the result a List.) + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a List + </ol> + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">copy(list(ls)) is equal to ls</pre></li> + <li><pre class="example">copy(list {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}) == List{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}</pre></li> + </ul> + +</dd> + <dt> + <a name = "copy2"></a> + <strong>copy2 (iter, i1, i2)</strong> + </dt> + <dd> + create a table of pairs from the double-valued sequence. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a double-valued sequence + </li> + <li><span class="parameter">i1</span> + used to capture extra iterator values + </li> + <li><span class="parameter">i2</span> + as with pairs & ipairs + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a list-like table + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">copy2(<span class="global">ipairs</span>{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>}) == {{<span class="number">1</span>,<span class="number">10</span>},{<span class="number">2</span>,<span class="number">20</span>},{<span class="number">3</span>,<span class="number">30</span>}}</pre> + </ul> + +</dd> + <dt> + <a name = "copy_tuples"></a> + <strong>copy_tuples (iter)</strong> + </dt> + <dd> + create a table of 'tuples' from a multi-valued sequence. + A generalization of copy2 above + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a multiple-valued sequence + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a list-like table + </ol> + + + + +</dd> + <dt> + <a name = "random"></a> + <strong>random (n, l, u)</strong> + </dt> + <dd> + return an iterator of random numbers. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">n</span> + the length of the sequence + </li> + <li><span class="parameter">l</span> + same as the first optional argument to math.random + </li> + <li><span class="parameter">u</span> + same as the second optional argument to math.random + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a sequence + </ol> + + + + +</dd> + <dt> + <a name = "sort"></a> + <strong>sort (iter, comp)</strong> + </dt> + <dd> + return an iterator to the sorted elements of a sequence. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence + </li> + <li><span class="parameter">comp</span> + an optional comparison function (comp(x,y) is true if x < y) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "zip"></a> + <strong>zip (iter1, iter2)</strong> + </dt> + <dd> + return an iterator which returns elements of two sequences. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter1</span> + a sequence + </li> + <li><span class="parameter">iter2</span> + a sequence + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">for</span> x,y <span class="keyword">in</span> seq.zip(ls1,ls2) <span class="keyword">do</span>....<span class="keyword">end</span></pre> + </ul> + +</dd> + <dt> + <a name = "count_map"></a> + <strong>count_map (iter)</strong> + </dt> + <dd> + Makes a table where the key/values are the values and value counts of the sequence. + This version works with 'hashable' values like strings and numbers. + <a href="../libraries/pl.tablex.html#count_map">pl.tablex.count_map</a> is more general. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + a map-like table</li> + <li> + a table</li> + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#count_map">pl.tablex.count_map</a> + </ul> + + +</dd> + <dt> + <a name = "printall"></a> + <strong>printall (iter, sep, nfields, fmt)</strong> + </dt> + <dd> + print out a sequence iter with a separator. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence + </li> + <li><span class="parameter">sep</span> + the separator (default space) + </li> + <li><span class="parameter">nfields</span> + maximum number of values per line (default 7) + </li> + <li><span class="parameter">fmt</span> + optional format function for each value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "map"></a> + <strong>map (fn, iter, arg)</strong> + </dt> + <dd> + return a sequence where every element of a sequence has been transformed + by a function. If you don't supply an argument, then the function will + receive both values of a double-valued sequence, otherwise behaves rather like + tablex.map. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fn</span> + a function to apply to elements; may take two arguments + </li> + <li><span class="parameter">iter</span> + a sequence of one or two values + </li> + <li><span class="parameter">arg</span> + optional argument to pass to function. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "filter"></a> + <strong>filter (iter, pred, arg)</strong> + </dt> + <dd> + filter a sequence using a predicate function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence of one or two values + </li> + <li><span class="parameter">pred</span> + a boolean function; may take two arguments + </li> + <li><span class="parameter">arg</span> + optional argument to pass to function. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "reduce"></a> + <strong>reduce (fn, iter, initval)</strong> + </dt> + <dd> + 'reduce' a sequence using a binary function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fn</span> + <span class="types"><span class="type">func</span></span> + a function of two arguments + </li> + <li><span class="parameter">iter</span> + a sequence + </li> + <li><span class="parameter">initval</span> + optional initial value + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">seq.reduce(operator.add,seq.list{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) == <span class="number">10</span></pre></li> + <li><pre class="example">seq.reduce(<span class="string">'-'</span>,{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>}) == -<span class="number">13</span></pre></li> + </ul> + +</dd> + <dt> + <a name = "take"></a> + <strong>take (iter, n)</strong> + </dt> + <dd> + take the first n values from the sequence. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence of one or two values + </li> + <li><span class="parameter">n</span> + number of items to take + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a sequence of at most n items + </ol> + + + + +</dd> + <dt> + <a name = "skip"></a> + <strong>skip (iter, n)</strong> + </dt> + <dd> + skip the first n values of a sequence + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence of one or more values + </li> + <li><span class="parameter">n</span> + number of items to skip + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "enum"></a> + <strong>enum (iter)</strong> + </dt> + <dd> + a sequence with a sequence count and the original value. + enum(copy(ls)) is a roundabout way of saying ipairs(ls). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a single or double valued sequence + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + sequence of (i,v), i = 1..n and v is from iter. + </ol> + + + + +</dd> + <dt> + <a name = "mapmethod"></a> + <strong>mapmethod (iter, name, arg1, arg2)</strong> + </dt> + <dd> + map using a named method over a sequence. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence + </li> + <li><span class="parameter">name</span> + the method name + </li> + <li><span class="parameter">arg1</span> + optional first extra argument + </li> + <li><span class="parameter">arg2</span> + optional second extra argument + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "last"></a> + <strong>last (iter)</strong> + </dt> + <dd> + a sequence of (last,current) values from another sequence. + This will return S(i-1),S(i) if given S(i) + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "foreach"></a> + <strong>foreach (iter, fn)</strong> + </dt> + <dd> + call the function on each element of the sequence. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">iter</span> + a sequence with up to 3 values + </li> + <li><span class="parameter">fn</span> + a function + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "lines"></a> + <strong>lines (f, ...)</strong> + </dt> + <dd> + create a wrapped iterator over all lines in the file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + either a filename, file-like object, or 'STDIN' (for standard input) + </li> + <li><span class="parameter">...</span> + for Lua 5.2 only, optional format specifiers, as in <a href="https://www.lua.org/manual/5.1/manual.html#pdf-io.read">io.read</a>. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a sequence wrapper + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.sip.html b/Data/Libraries/Penlight/docs/libraries/pl.sip.html new file mode 100644 index 0000000..d488b09 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.sip.html @@ -0,0 +1,399 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><strong>pl.sip</strong></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.sip</code></h1> +<p>Simple Input Patterns (SIP).</p> +<p> SIP patterns start with '$', then a + one-letter type, and then an optional variable in curly braces.</p> + + +<pre> +sip.match(<span class="string">'$v=$q'</span>,<span class="string">'name="dolly"'</span>,res) +==> res=={<span class="string">'name'</span>,<span class="string">'dolly'</span>} +sip.match(<span class="string">'($q{first},$q{second})'</span>,<span class="string">'("john","smith")'</span>,res) +==> res=={second=<span class="string">'smith'</span>,first=<span class="string">'john'</span>} +</pre> + +<p> Type names:</p> + + +<pre> +v identifier +i integer +f floating-point +q quoted <span class="global">string</span> +([{< match up to closing bracket +</pre> + +<p> See <a href="../manual/08-additional.md.html#Simple_Input_Patterns">the Guide</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#create_pattern">create_pattern (spec, options)</a></td> + <td class="summary">convert a SIP pattern into the equivalent Lua string pattern.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#compile">compile (spec, options)</a></td> + <td class="summary">convert a SIP pattern into a matching function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#match">match (spec, line, res, options)</a></td> + <td class="summary">match a SIP pattern against a string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#match_at_start">match_at_start (spec, line, res)</a></td> + <td class="summary">match a SIP pattern against the start of a string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#fields">fields (spec, f)</a></td> + <td class="summary">given a pattern and a file object, return an iterator over the results</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pattern">pattern (spec, fun)</a></td> + <td class="summary">register a match which will be used in the read function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#read">read (f, matches)</a></td> + <td class="summary">enter a loop which applies all registered matches to the input file.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "create_pattern"></a> + <strong>create_pattern (spec, options)</strong> + </dt> + <dd> + convert a SIP pattern into the equivalent Lua string pattern. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">spec</span> + a SIP pattern + </li> + <li><span class="parameter">options</span> + a table; only the <code>at_start</code> field is + currently meaningful and ensures that the pattern is anchored + at the start of the string. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a Lua string pattern. + </ol> + + + + +</dd> + <dt> + <a name = "compile"></a> + <strong>compile (spec, options)</strong> + </dt> + <dd> + convert a SIP pattern into a matching function. + The returned function takes two arguments, the line and an empty table. + If the line matched the pattern, then this function returns true + and the table is filled with field-value pairs. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">spec</span> + a SIP pattern + </li> + <li><span class="parameter">options</span> + optional table; {at_start=true} ensures that the pattern + is anchored at the start of the string. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a function if successful, or nil,error + </ol> + + + + +</dd> + <dt> + <a name = "match"></a> + <strong>match (spec, line, res, options)</strong> + </dt> + <dd> + match a SIP pattern against a string. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">spec</span> + a SIP pattern + </li> + <li><span class="parameter">line</span> + a string + </li> + <li><span class="parameter">res</span> + a table to receive values + </li> + <li><span class="parameter">options</span> + (optional) option table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + +</dd> + <dt> + <a name = "match_at_start"></a> + <strong>match_at_start (spec, line, res)</strong> + </dt> + <dd> + match a SIP pattern against the start of a string. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">spec</span> + a SIP pattern + </li> + <li><span class="parameter">line</span> + a string + </li> + <li><span class="parameter">res</span> + a table to receive values + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + +</dd> + <dt> + <a name = "fields"></a> + <strong>fields (spec, f)</strong> + </dt> + <dd> + given a pattern and a file object, return an iterator over the results + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">spec</span> + a SIP pattern + </li> + <li><span class="parameter">f</span> + a file-like object. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "pattern"></a> + <strong>pattern (spec, fun)</strong> + </dt> + <dd> + register a match which will be used in the read function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">spec</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a SIP pattern + </li> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function to be called with the results of the match + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.sip.html#read">read</a> + </ul> + + +</dd> + <dt> + <a name = "read"></a> + <strong>read (f, matches)</strong> + </dt> + <dd> + enter a loop which applies all registered matches to the input file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + a file-like object + </li> + <li><span class="parameter">matches</span> + <span class="types"><span class="type">array</span></span> + optional list of <code>{spec,fun}</code> pairs, as for <a href="../libraries/pl.sip.html#pattern">pattern</a> above. + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.strict.html b/Data/Libraries/Penlight/docs/libraries/pl.strict.html new file mode 100644 index 0000000..3c9c0bd --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.strict.html @@ -0,0 +1,270 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><strong>pl.strict</strong></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.strict</code></h1> +<p>Checks uses of undeclared global variables.</p> +<p> All global variables must be 'declared' through a regular assignment + (even assigning <code>nil</code> will do) in a main chunk before being used + anywhere or assigned to inside a function. Existing metatables <code>__newindex</code> and <code>__index</code> + metamethods are respected.</p> + +<p> You can set any table to have strict behaviour using <a href="../libraries/pl.strict.html#module">strict.module</a>. Creating a new + module with <a href="../libraries/pl.strict.html#closed_module">strict.closed_module</a> makes the module immune to monkey-patching, if + you don't wish to encourage monkey business.</p> + +<p> If the global <code>PENLIGHT_NO_GLOBAL_STRICT</code> is defined, then this module won't make the + global environment strict - if you just want to explicitly set table strictness.</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module">module ([name[, mod[, predeclared]]])</a></td> + <td class="summary">make an existing table strict.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#make_all_strict">make_all_strict (T)</a></td> + <td class="summary">make all tables in a table strict.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#closed_module">closed_module (mod, name)</a></td> + <td class="summary">make a new module table which is closed to further changes.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "module"></a> + <strong>module ([name[, mod[, predeclared]]])</strong> + </dt> + <dd> + make an existing table strict. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + name of table + (<em>optional</em>) + </li> + <li><span class="parameter">mod</span> + <span class="types"><span class="type">tab</span></span> + the table to protect - if <code>nil</code> then we'll return a new table + (<em>optional</em>) + </li> + <li><span class="parameter">predeclared</span> + <span class="types"><span class="type">tab</span></span> + +<ul> + <li>table of variables that are to be considered predeclared.</li> +</ul> + + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the given table, or a new table + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> M = { hello = <span class="string">"world"</span> } +strict.<span class="global">module</span> (<span class="string">"Awesome_Module"</span>, M, { + Lua = <span class="keyword">true</span>, <span class="comment">-- defines allowed keys +</span>}) + +<span class="global">assert</span>(M.hello == <span class="string">"world"</span>) +<span class="global">assert</span>(M.Lua == <span class="keyword">nil</span>) <span class="comment">-- access allowed, but has no value yet +</span>M.Lua = <span class="string">"Rocks"</span> +<span class="global">assert</span>(M.Lua == <span class="string">"Rocks"</span>) +M.not_allowed = <span class="string">"bad boy"</span> <span class="comment">-- throws an error</span></pre> + </ul> + +</dd> + <dt> + <a name = "make_all_strict"></a> + <strong>make_all_strict (T)</strong> + </dt> + <dd> + make all tables in a table strict. + So <code>strict.make_all_strict(_G)</code> prevents monkey-patching + of any global table + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">T</span> + <span class="types"><span class="type">tab</span></span> + the table containing the tables to protect. Table <code>T</code> itself will NOT be protected. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "closed_module"></a> + <strong>closed_module (mod, name)</strong> + </dt> + <dd> + make a new module table which is closed to further changes. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">mod</span> + <span class="types"><span class="type">tab</span></span> + module table + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + module name + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.stringio.html b/Data/Libraries/Penlight/docs/libraries/pl.stringio.html new file mode 100644 index 0000000..7505d34 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.stringio.html @@ -0,0 +1,215 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><strong>pl.stringio</strong></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.stringio</code></h1> +<p>Reading and writing strings using file-like objects.</p> +<p> <br></p> + + +<pre> +f = stringio.open(text) +l1 = f:read() <span class="comment">-- read first line +</span>n,m = f:read (<span class="string">'*n'</span>,<span class="string">'*n'</span>) <span class="comment">-- read two numbers +</span><span class="keyword">for</span> line <span class="keyword">in</span> f:lines() <span class="keyword">do</span> <span class="global">print</span>(line) <span class="keyword">end</span> <span class="comment">-- iterate over all lines +</span>f = stringio.create() +f:write(<span class="string">'hello'</span>) +f:write(<span class="string">'dolly'</span>) +<span class="global">assert</span>(f:value(),<span class="string">'hellodolly'</span>) +</pre> + +<p> See <a href="../manual/03-strings.md.html#File_style_I_O_on_Strings">the Guide</a>.</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#create">create ()</a></td> + <td class="summary">create a file-like object which can be used to construct a string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#open">open (s)</a></td> + <td class="summary">create a file-like object for reading from a given string.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "create"></a> + <strong>create ()</strong> + </dt> + <dd> + create a file-like object which can be used to construct a string. + The resulting object has an extra <code>value()</code> method for + retrieving the string value. Implements <a href="../libraries/pl.file.html#write">file:write</a>, <code>file:seek</code>, <code>file:lines</code>, + plus an extra <code>writef</code> method which works like <a href="../libraries/pl.utils.html#printf">utils.printf</a>. + + + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">f = create(); f:write(<span class="string">'hello, dolly\n'</span>); <span class="global">print</span>(f:value())</pre> + </ul> + +</dd> + <dt> + <a name = "open"></a> + <strong>open (s)</strong> + </dt> + <dd> + create a file-like object for reading from a given string. + Implements <a href="../libraries/pl.file.html#read">file:read</a>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + The input string. + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">fs = open <span class="string">'20 10'</span>; x,y = f:read (<span class="string">'*n'</span>,<span class="string">'*n'</span>); <span class="global">assert</span>(x == <span class="number">20</span> <span class="keyword">and</span> y == <span class="number">10</span>)</pre> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.stringx.html b/Data/Libraries/Penlight/docs/libraries/pl.stringx.html new file mode 100644 index 0000000..4fd08cd --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.stringx.html @@ -0,0 +1,1239 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#String_Predicates">String Predicates </a></li> +<li><a href="#Strings_and_Lists">Strings and Lists </a></li> +<li><a href="#Finding_and_Replacing">Finding and Replacing </a></li> +<li><a href="#Stripping_and_Justifying">Stripping and Justifying </a></li> +<li><a href="#Partioning_Strings">Partioning Strings </a></li> +<li><a href="#Miscelaneous">Miscelaneous </a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><strong>pl.stringx</strong></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.stringx</code></h1> +<p>Python-style extended string library.</p> +<p> see 3.6.1 of the Python reference. + If you want to make these available as string methods, then say + <code>stringx.import()</code> to bring them into the standard <a href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> table.</p> + +<p> See <a href="../manual/03-strings.md.html#">the Guide</a></p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a></p> + + +<h2><a href="#String_Predicates">String Predicates </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#isalpha">isalpha (s)</a></td> + <td class="summary">does s only contain alphabetic characters?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#isdigit">isdigit (s)</a></td> + <td class="summary">does s only contain digits?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#isalnum">isalnum (s)</a></td> + <td class="summary">does s only contain alphanumeric characters?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#isspace">isspace (s)</a></td> + <td class="summary">does s only contain spaces?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#islower">islower (s)</a></td> + <td class="summary">does s only contain lower case characters?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#isupper">isupper (s)</a></td> + <td class="summary">does s only contain upper case characters?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#startswith">startswith (s, prefix)</a></td> + <td class="summary">does s start with prefix or one of prefixes?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#endswith">endswith (s, suffix)</a></td> + <td class="summary">does s end with suffix or one of suffixes?</td> + </tr> +</table> +<h2><a href="#Strings_and_Lists">Strings and Lists </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#join">join (s, seq)</a></td> + <td class="summary">concatenate the strings using this string as a delimiter.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#splitlines">splitlines (s[, keep_ends])</a></td> + <td class="summary">Split a string into a list of lines.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#split">split (s[, re[, n]])</a></td> + <td class="summary">split a string into a list of strings using a delimiter.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#expandtabs">expandtabs (s, tabsize)</a></td> + <td class="summary">replace all tabs in s with tabsize spaces.</td> + </tr> +</table> +<h2><a href="#Finding_and_Replacing">Finding and Replacing </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#lfind">lfind (s, sub[, first[, last]])</a></td> + <td class="summary">find index of first instance of sub in s from the left.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#rfind">rfind (s, sub[, first[, last]])</a></td> + <td class="summary">find index of first instance of sub in s from the right.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#replace">replace (s, old, new[, n])</a></td> + <td class="summary">replace up to n instances of old by new in the string s.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#count">count (s, sub[, allow_overlap])</a></td> + <td class="summary">count all instances of substring in string.</td> + </tr> +</table> +<h2><a href="#Stripping_and_Justifying">Stripping and Justifying </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#ljust">ljust (s, w[, ch=' '])</a></td> + <td class="summary">left-justify s with width w.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#rjust">rjust (s, w[, ch=' '])</a></td> + <td class="summary">right-justify s with width w.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#center">center (s, w[, ch=' '])</a></td> + <td class="summary">center-justify s with width w.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#lstrip">lstrip (s[, chrs='%s'])</a></td> + <td class="summary">trim any whitespace on the left of s.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#rstrip">rstrip (s[, chrs='%s'])</a></td> + <td class="summary">trim any whitespace on the right of s.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#strip">strip (s[, chrs='%s'])</a></td> + <td class="summary">trim any whitespace on both left and right of s.</td> + </tr> +</table> +<h2><a href="#Partioning_Strings">Partioning Strings </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#splitv">splitv (s[, re='%s'])</a></td> + <td class="summary">split a string using a pattern.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#partition">partition (s, ch)</a></td> + <td class="summary">partition the string using first occurance of a delimiter</td> + </tr> + <tr> + <td class="name" nowrap><a href="#rpartition">rpartition (s, ch)</a></td> + <td class="summary">partition the string p using last occurance of a delimiter</td> + </tr> + <tr> + <td class="name" nowrap><a href="#at">at (s, idx)</a></td> + <td class="summary">return the 'character' at the index.</td> + </tr> +</table> +<h2><a href="#Miscelaneous">Miscelaneous </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#lines">lines (s)</a></td> + <td class="summary">return an iterator over all lines in a string</td> + </tr> + <tr> + <td class="name" nowrap><a href="#title">title (s)</a></td> + <td class="summary">inital word letters uppercase ('title case').</td> + </tr> + <tr> + <td class="name" nowrap><a href="#shorten">shorten (s, w, tail)</a></td> + <td class="summary">Return a shortened version of a string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#quote_string">quote_string (s)</a></td> + <td class="summary">Quote the given string and preserve any control or escape characters, such that reloading the string in Lua returns the same result.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="String_Predicates"></a>String Predicates </h2> + + <dl class="function"> + <dt> + <a name = "isalpha"></a> + <strong>isalpha (s)</strong> + </dt> + <dd> + does s only contain alphabetic characters? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a string + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "isdigit"></a> + <strong>isdigit (s)</strong> + </dt> + <dd> + does s only contain digits? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a string + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "isalnum"></a> + <strong>isalnum (s)</strong> + </dt> + <dd> + does s only contain alphanumeric characters? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a string + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "isspace"></a> + <strong>isspace (s)</strong> + </dt> + <dd> + does s only contain spaces? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a string + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "islower"></a> + <strong>islower (s)</strong> + </dt> + <dd> + does s only contain lower case characters? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a string + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "isupper"></a> + <strong>isupper (s)</strong> + </dt> + <dd> + does s only contain upper case characters? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a string + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "startswith"></a> + <strong>startswith (s, prefix)</strong> + </dt> + <dd> + does s start with prefix or one of prefixes? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a string + </li> + <li><span class="parameter">prefix</span> + a string or an array of strings + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "endswith"></a> + <strong>endswith (s, suffix)</strong> + </dt> + <dd> + does s end with suffix or one of suffixes? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a string + </li> + <li><span class="parameter">suffix</span> + a string or an array of strings + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Strings_and_Lists"></a>Strings and Lists </h2> + + <dl class="function"> + <dt> + <a name = "join"></a> + <strong>join (s, seq)</strong> + </dt> + <dd> + concatenate the strings using this string as a delimiter. + Note that the arguments are reversed from <code>string.concat</code>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">seq</span> + a table of strings or numbers + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">stringx.join(<span class="string">' '</span>, {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}) == <span class="string">'1 2 3'</span></pre> + </ul> + +</dd> + <dt> + <a name = "splitlines"></a> + <strong>splitlines (s[, keep_ends])</strong> + </dt> + <dd> + Split a string into a list of lines. + <code>"\r"</code>, <code>"\n"</code>, and <code>"\r\n"</code> are considered line ends. + They are not included in the lines unless <code>keepends</code> is passed. + Terminal line end does not produce an extra line. + Splitting an empty string results in an empty list. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string. + </li> + <li><span class="parameter">keep_ends</span> + <span class="types"><span class="type">bool</span></span> + include line ends. + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + List of lines + </ol> + + + + +</dd> + <dt> + <a name = "split"></a> + <strong>split (s[, re[, n]])</strong> + </dt> + <dd> + split a string into a list of strings using a delimiter. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">re</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a delimiter (defaults to whitespace) + (<em>optional</em>) + </li> + <li><span class="parameter">n</span> + <span class="types"><span class="type">int</span></span> + maximum number of results + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + List + </ol> + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">#(stringx.split(<span class="string">'one two'</span>)) == <span class="number">2</span></pre></li> + <li><pre class="example">stringx.split(<span class="string">'one,two,three'</span>, <span class="string">','</span>) == List{<span class="string">'one'</span>,<span class="string">'two'</span>,<span class="string">'three'</span>}</pre></li> + <li><pre class="example">stringx.split(<span class="string">'one,two,three'</span>, <span class="string">','</span>, <span class="number">2</span>) == List{<span class="string">'one'</span>,<span class="string">'two,three'</span>}</pre></li> + </ul> + +</dd> + <dt> + <a name = "expandtabs"></a> + <strong>expandtabs (s, tabsize)</strong> + </dt> + <dd> + replace all tabs in s with tabsize spaces. If not specified, tabsize defaults to 8. + Tab stops will be honored. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">tabsize</span> + <span class="types"><span class="type">int</span></span> + [opt=8] number of spaces to expand each tab + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + expanded string + </ol> + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">stringx.expandtabs(<span class="string">'\tone,two,three'</span>, <span class="number">4</span>) == <span class="string">' one,two,three'</span></pre></li> + <li><pre class="example">stringx.expandtabs(<span class="string">' \tone,two,three'</span>, <span class="number">4</span>) == <span class="string">' one,two,three'</span></pre></li> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="Finding_and_Replacing"></a>Finding and Replacing </h2> + + <dl class="function"> + <dt> + <a name = "lfind"></a> + <strong>lfind (s, sub[, first[, last]])</strong> + </dt> + <dd> + find index of first instance of sub in s from the left. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">sub</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + substring + </li> + <li><span class="parameter">first</span> + <span class="types"><span class="type">int</span></span> + first index + (<em>optional</em>) + </li> + <li><span class="parameter">last</span> + <span class="types"><span class="type">int</span></span> + last index + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + start index, or nil if not found + </ol> + + + + +</dd> + <dt> + <a name = "rfind"></a> + <strong>rfind (s, sub[, first[, last]])</strong> + </dt> + <dd> + find index of first instance of sub in s from the right. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">sub</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + substring + </li> + <li><span class="parameter">first</span> + <span class="types"><span class="type">int</span></span> + first index + (<em>optional</em>) + </li> + <li><span class="parameter">last</span> + <span class="types"><span class="type">int</span></span> + last index + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + start index, or nil if not found + </ol> + + + + +</dd> + <dt> + <a name = "replace"></a> + <strong>replace (s, old, new[, n])</strong> + </dt> + <dd> + replace up to n instances of old by new in the string s. + If n is not present, replace all instances. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">old</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the target substring + </li> + <li><span class="parameter">new</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the substitution + </li> + <li><span class="parameter">n</span> + <span class="types"><span class="type">int</span></span> + optional maximum number of substitutions + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + result string + </ol> + + + + +</dd> + <dt> + <a name = "count"></a> + <strong>count (s, sub[, allow_overlap])</strong> + </dt> + <dd> + count all instances of substring in string. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">sub</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + substring + </li> + <li><span class="parameter">allow_overlap</span> + <span class="types"><span class="type">bool</span></span> + allow matches to overlap + (<em>optional</em>) + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="global">assert</span>(stringx.count(<span class="string">'banana'</span>, <span class="string">'ana'</span>) == <span class="number">1</span>) +<span class="global">assert</span>(stringx.count(<span class="string">'banana'</span>, <span class="string">'ana'</span>, <span class="keyword">true</span>) == <span class="number">2</span>)</pre> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="Stripping_and_Justifying"></a>Stripping and Justifying </h2> + + <dl class="function"> + <dt> + <a name = "ljust"></a> + <strong>ljust (s, w[, ch=' '])</strong> + </dt> + <dd> + left-justify s with width w. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">w</span> + <span class="types"><span class="type">int</span></span> + width of justification + </li> + <li><span class="parameter">ch</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + padding character + (<em>default</em> ' ') + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">stringx.ljust(<span class="string">'hello'</span>, <span class="number">10</span>, <span class="string">'*'</span>) == <span class="string">'*****hello'</span></pre> + </ul> + +</dd> + <dt> + <a name = "rjust"></a> + <strong>rjust (s, w[, ch=' '])</strong> + </dt> + <dd> + right-justify s with width w. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">w</span> + <span class="types"><span class="type">int</span></span> + width of justification + </li> + <li><span class="parameter">ch</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + padding character + (<em>default</em> ' ') + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">stringx.rjust(<span class="string">'hello'</span>, <span class="number">10</span>, <span class="string">'*'</span>) == <span class="string">'hello*****'</span></pre> + </ul> + +</dd> + <dt> + <a name = "center"></a> + <strong>center (s, w[, ch=' '])</strong> + </dt> + <dd> + center-justify s with width w. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">w</span> + <span class="types"><span class="type">int</span></span> + width of justification + </li> + <li><span class="parameter">ch</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + padding character + (<em>default</em> ' ') + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">stringx.center(<span class="string">'hello'</span>, <span class="number">10</span>, <span class="string">'*'</span>) == <span class="string">'**hello***'</span></pre> + </ul> + +</dd> + <dt> + <a name = "lstrip"></a> + <strong>lstrip (s[, chrs='%s'])</strong> + </dt> + <dd> + trim any whitespace on the left of s. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">chrs</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + default any whitespace character, + but can be a string of characters to be trimmed + (<em>default</em> '%s') + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "rstrip"></a> + <strong>rstrip (s[, chrs='%s'])</strong> + </dt> + <dd> + trim any whitespace on the right of s. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">chrs</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + default any whitespace character, + but can be a string of characters to be trimmed + (<em>default</em> '%s') + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "strip"></a> + <strong>strip (s[, chrs='%s'])</strong> + </dt> + <dd> + trim any whitespace on both left and right of s. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">chrs</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + default any whitespace character, + but can be a string of characters to be trimmed + (<em>default</em> '%s') + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Partioning_Strings"></a>Partioning Strings </h2> + + <dl class="function"> + <dt> + <a name = "splitv"></a> + <strong>splitv (s[, re='%s'])</strong> + </dt> + <dd> + split a string using a pattern. Note that at least one value will be returned! + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">re</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a Lua string pattern (defaults to whitespace) + (<em>default</em> '%s') + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the parts of the string + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.utils.html#splitv">utils.splitv</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example">a,b = line:splitv(<span class="string">'='</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "partition"></a> + <strong>partition (s, ch)</strong> + </dt> + <dd> + partition the string using first occurance of a delimiter + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">ch</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + delimiter + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + part before ch</li> + <li> + ch</li> + <li> + part after ch</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">{stringx.partition(<span class="string">'a,b,c'</span>, <span class="string">','</span>))} == {<span class="string">'a'</span>, <span class="string">','</span>, <span class="string">'b,c'</span>}</pre></li> + <li><pre class="example">{stringx.partition(<span class="string">'abc'</span>, <span class="string">'x'</span>))} == {<span class="string">'abc'</span>, <span class="string">''</span>, <span class="string">''</span>}</pre></li> + </ul> + +</dd> + <dt> + <a name = "rpartition"></a> + <strong>rpartition (s, ch)</strong> + </dt> + <dd> + partition the string p using last occurance of a delimiter + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">ch</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + delimiter + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + part before ch</li> + <li> + ch</li> + <li> + part after ch</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">{stringx.rpartition(<span class="string">'a,b,c'</span>, <span class="string">','</span>))} == {<span class="string">'a,b'</span>, <span class="string">','</span>, <span class="string">'c'</span>}</pre></li> + <li><pre class="example">{stringx.rpartition(<span class="string">'abc'</span>, <span class="string">'x'</span>))} == {<span class="string">''</span>, <span class="string">''</span>, <span class="string">'abc'</span>}</pre></li> + </ul> + +</dd> + <dt> + <a name = "at"></a> + <strong>at (s, idx)</strong> + </dt> + <dd> + return the 'character' at the index. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">idx</span> + <span class="types"><span class="type">int</span></span> + an index (can be negative) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a substring of length 1 if successful, empty string otherwise. + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Miscelaneous"></a>Miscelaneous </h2> + + <dl class="function"> + <dt> + <a name = "lines"></a> + <strong>lines (s)</strong> + </dt> + <dd> + return an iterator over all lines in a string + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + an iterator + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> line_no = <span class="number">1</span> +<span class="keyword">for</span> line <span class="keyword">in</span> stringx.lines(some_text) <span class="keyword">do</span> + <span class="global">print</span>(line_no, line) + line_no = line_no + <span class="number">1</span> +<span class="keyword">end</span></pre> + </ul> + +</dd> + <dt> + <a name = "title"></a> + <strong>title (s)</strong> + </dt> + <dd> + inital word letters uppercase ('title case'). + Here 'words' mean chunks of non-space characters. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a string with each word's first letter uppercase + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">stringx.title(<span class="string">"hello world"</span>) == <span class="string">"Hello World"</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "shorten"></a> + <strong>shorten (s, w, tail)</strong> + </dt> + <dd> + Return a shortened version of a string. + Fits string within w characters. Removed characters are marked with ellipsis. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">w</span> + <span class="types"><span class="type">int</span></span> + the maxinum size allowed + </li> + <li><span class="parameter">tail</span> + <span class="types"><span class="type">bool</span></span> + true if we want to show the end of the string (head otherwise) + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">(<span class="string">'1234567890'</span>):shorten(<span class="number">8</span>) == <span class="string">'12345...'</span></pre></li> + <li><pre class="example">(<span class="string">'1234567890'</span>):shorten(<span class="number">8</span>, <span class="keyword">true</span>) == <span class="string">'...67890'</span></pre></li> + <li><pre class="example">(<span class="string">'1234567890'</span>):shorten(<span class="number">20</span>) == <span class="string">'1234567890'</span></pre></li> + </ul> + +</dd> + <dt> + <a name = "quote_string"></a> + <strong>quote_string (s)</strong> + </dt> + <dd> + Quote the given string and preserve any control or escape characters, such that reloading the string in Lua returns the same result. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + The string to be quoted. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + The quoted string. + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.tablex.html b/Data/Libraries/Penlight/docs/libraries/pl.tablex.html new file mode 100644 index 0000000..2e07080 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.tablex.html @@ -0,0 +1,1980 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +<li><a href="#Copying">Copying</a></li> +<li><a href="#Comparing">Comparing</a></li> +<li><a href="#Finding">Finding</a></li> +<li><a href="#MappingAndFiltering">MappingAndFiltering</a></li> +<li><a href="#Iterating">Iterating</a></li> +<li><a href="#Extraction">Extraction</a></li> +<li><a href="#Merging">Merging</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><strong>pl.tablex</strong></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.tablex</code></h1> +<p>Extended operations on Lua tables.</p> +<p> See <a href="../manual/02-arrays.md.html#Useful_Operations_on_Tables">the Guide</a></p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.types.html#">pl.types</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#size">size (t)</a></td> + <td class="summary">total number of elements in this table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#index_by">index_by (tbl, idx)</a></td> + <td class="summary">return a list of all values in a table indexed by another list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#transform">transform (fun, t, ...)</a></td> + <td class="summary">apply a function to all values of a table, in-place.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#range">range (start, finish[, step=1])</a></td> + <td class="summary">generate a table of all numbers in a range.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#reduce">reduce (fun, t, memo)</a></td> + <td class="summary">'reduce' a list using a binary function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#index_map">index_map (t)</a></td> + <td class="summary">create an index map from a list-like table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#makeset">makeset (t)</a></td> + <td class="summary">create a set from a list-like table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#union">union (t1, t2)</a></td> + <td class="summary">the union of two map-like tables.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#intersection">intersection (t1, t2)</a></td> + <td class="summary">the intersection of two map-like tables.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#count_map">count_map (t, cmp)</a></td> + <td class="summary">A table where the key/values are the values and value counts of the table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#set">set (t, val[, i1=1[, i2=#t]])</a></td> + <td class="summary">set an array range to a value.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#new">new (n, val)</a></td> + <td class="summary">create a new array of specified size with initial value.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#clear">clear (t, istart)</a></td> + <td class="summary">clear out the contents of a table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#removevalues">removevalues (t, i1, i2)</a></td> + <td class="summary">remove a range of values from a table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#readonly">readonly (t)</a></td> + <td class="summary">modifies a table to be read only.</td> + </tr> +</table> +<h2><a href="#Copying">Copying</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#update">update (t1, t2)</a></td> + <td class="summary">copy a table into another, in-place.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#copy">copy (t)</a></td> + <td class="summary">make a shallow copy of a table</td> + </tr> + <tr> + <td class="name" nowrap><a href="#deepcopy">deepcopy (t)</a></td> + <td class="summary">make a deep copy of a table, recursively copying all the keys and fields.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#icopy">icopy (dest, src[, idest=1[, isrc=1[, nsrc=#src]]])</a></td> + <td class="summary">copy an array into another one, clearing <code>dest</code> after <code>idest+nsrc</code>, if necessary.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#move">move (dest, src[, idest=1[, isrc=1[, nsrc=#src]]])</a></td> + <td class="summary">copy an array into another one.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#insertvalues">insertvalues (t[, position], values)</a></td> + <td class="summary">insert values into a table.</td> + </tr> +</table> +<h2><a href="#Comparing">Comparing</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#deepcompare">deepcompare (t1, t2[, ignore_mt[, eps]])</a></td> + <td class="summary">compare two values.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#compare">compare (t1, t2, cmp)</a></td> + <td class="summary">compare two arrays using a predicate.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#compare_no_order">compare_no_order (t1, t2, cmp)</a></td> + <td class="summary">compare two list-like tables using an optional predicate, without regard for element order.</td> + </tr> +</table> +<h2><a href="#Finding">Finding</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#find">find (t, val, idx)</a></td> + <td class="summary">return the index of a value in a list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#rfind">rfind (t, val, idx)</a></td> + <td class="summary">return the index of a value in a list, searching from the end.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#find_if">find_if (t, cmp, arg)</a></td> + <td class="summary">return the index (or key) of a value in a table using a comparison function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#search">search (t, value[, exclude])</a></td> + <td class="summary">find a value in a table by recursive search.</td> + </tr> +</table> +<h2><a href="#MappingAndFiltering">MappingAndFiltering</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#map">map (fun, t, ...)</a></td> + <td class="summary">apply a function to all values of a table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#imap">imap (fun, t, ...)</a></td> + <td class="summary">apply a function to all values of a list.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#map_named_method">map_named_method (name, t, ...)</a></td> + <td class="summary">apply a named method to values from a table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#map2">map2 (fun, t1, t2, ...)</a></td> + <td class="summary">apply a function to values from two tables.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#imap2">imap2 (fun, t1, t2, ...)</a></td> + <td class="summary">apply a function to values from two arrays.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#mapn">mapn (fun, ..., fun)</a></td> + <td class="summary">Apply a function to a number of tables.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#pairmap">pairmap (fun, t, ...)</a></td> + <td class="summary">call the function with the key and value pairs from a table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#filter">filter (t, pred, arg)</a></td> + <td class="summary">filter an array's values using a predicate function</td> + </tr> +</table> +<h2><a href="#Iterating">Iterating</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#foreach">foreach (t, fun, ...)</a></td> + <td class="summary">apply a function to all elements of a table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#foreachi">foreachi (t, fun, ...)</a></td> + <td class="summary">apply a function to all elements of a list-like table in order.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sort">sort (t, f)</a></td> + <td class="summary">return an iterator to a table sorted by its keys</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sortv">sortv (t, f)</a></td> + <td class="summary">return an iterator to a table sorted by its values</td> + </tr> +</table> +<h2><a href="#Extraction">Extraction</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#keys">keys (t)</a></td> + <td class="summary">return all the keys of a table in arbitrary order.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#values">values (t)</a></td> + <td class="summary">return all the values of the table in arbitrary order</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sub">sub (t, first, last)</a></td> + <td class="summary">Extract a range from a table, like 'string.sub'.</td> + </tr> +</table> +<h2><a href="#Merging">Merging</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#merge">merge (t1, t2, dup)</a></td> + <td class="summary">combine two tables, either as union or intersection.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#difference">difference (s1, s2, symm)</a></td> + <td class="summary">a new table which is the difference of two tables.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#zip">zip (...)</a></td> + <td class="summary">return a table where each element is a table of the ith values of an arbitrary + number of tables.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "size"></a> + <strong>size (t)</strong> + </dt> + <dd> + total number of elements in this table. + Note that this is distinct from <code>#t</code>, which is the number + of values in the array part; this value will always + be greater or equal. The difference gives the size of + the hash part, for practical purposes. Works for any + object with a __pairs metamethod. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the size + </ol> + + + + +</dd> + <dt> + <a name = "index_by"></a> + <strong>index_by (tbl, idx)</strong> + </dt> + <dd> + return a list of all values in a table indexed by another list. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tbl</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + <li><span class="parameter">idx</span> + <span class="types"><span class="type">array</span></span> + an index table (a list of keys) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a list-like table + </ol> + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">index_by({<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>},{<span class="number">2</span>,<span class="number">4</span>}) == {<span class="number">20</span>,<span class="number">40</span>}</pre></li> + <li><pre class="example">index_by({one=<span class="number">1</span>,two=<span class="number">2</span>,three=<span class="number">3</span>},{<span class="string">'one'</span>,<span class="string">'three'</span>}) == {<span class="number">1</span>,<span class="number">3</span>}</pre></li> + </ul> + +</dd> + <dt> + <a name = "transform"></a> + <strong>transform (fun, t, ...)</strong> + </dt> + <dd> + apply a function to all values of a table, in-place. + Any extra arguments are passed to the function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + A function that takes at least one argument + </li> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + <li><span class="parameter">...</span> + extra arguments passed to <code>fun</code> + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#foreach">tablex.foreach</a> + </ul> + + +</dd> + <dt> + <a name = "range"></a> + <strong>range (start, finish[, step=1])</strong> + </dt> + <dd> + generate a table of all numbers in a range. + This is consistent with a numerical for loop. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">start</span> + <span class="types"><span class="type">int</span></span> + number + </li> + <li><span class="parameter">finish</span> + <span class="types"><span class="type">int</span></span> + number + </li> + <li><span class="parameter">step</span> + <span class="types"><span class="type">int</span></span> + make this negative for start < finish + (<em>default</em> 1) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "reduce"></a> + <strong>reduce (fun, t, memo)</strong> + </dt> + <dd> + 'reduce' a list using a binary function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function of two arguments + </li> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">memo</span> + <span class="types"><span class="type">array</span></span> + optional initial memo value. Defaults to first value in table. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the result of the function + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">reduce(<span class="string">'+'</span>,{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) == <span class="number">10</span></pre> + </ul> + +</dd> + <dt> + <a name = "index_map"></a> + <strong>index_map (t)</strong> + </dt> + <dd> + create an index map from a list-like table. The original values become keys, + and the associated values are the indices into the original list. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a map-like table + </ol> + + + + +</dd> + <dt> + <a name = "makeset"></a> + <strong>makeset (t)</strong> + </dt> + <dd> + create a set from a list-like table. A set is a table where the original values + become keys, and the associated values are all true. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a set (a map-like table) + </ol> + + + + +</dd> + <dt> + <a name = "union"></a> + <strong>union (t1, t2)</strong> + </dt> + <dd> + the union of two map-like tables. + If there are duplicate keys, the second table wins. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t1</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + <li><span class="parameter">t2</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">tab</span></span> + + + + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#merge">tablex.merge</a> + </ul> + + +</dd> + <dt> + <a name = "intersection"></a> + <strong>intersection (t1, t2)</strong> + </dt> + <dd> + the intersection of two map-like tables. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t1</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + <li><span class="parameter">t2</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">tab</span></span> + + + + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#merge">tablex.merge</a> + </ul> + + +</dd> + <dt> + <a name = "count_map"></a> + <strong>count_map (t, cmp)</strong> + </dt> + <dd> + A table where the key/values are the values and value counts of the table. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">cmp</span> + <span class="types"><span class="type">func</span></span> + a function that defines equality (otherwise uses ==) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a map-like table + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.seq.html#count_map">seq.count_map</a> + </ul> + + +</dd> + <dt> + <a name = "set"></a> + <strong>set (t, val[, i1=1[, i2=#t]])</strong> + </dt> + <dd> + set an array range to a value. If it's a function we use the result + of applying it to the indices. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">val</span> + a value + </li> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + start range + (<em>default</em> 1) + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + end range + (<em>default</em> #t) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "new"></a> + <strong>new (n, val)</strong> + </dt> + <dd> + create a new array of specified size with initial value. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">n</span> + <span class="types"><span class="type">int</span></span> + size + </li> + <li><span class="parameter">val</span> + initial value (can be <code>nil</code>, but don't expect <code>#</code> to work!) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the table + </ol> + + + + +</dd> + <dt> + <a name = "clear"></a> + <strong>clear (t, istart)</strong> + </dt> + <dd> + clear out the contents of a table. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a list + </li> + <li><span class="parameter">istart</span> + optional start position + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "removevalues"></a> + <strong>removevalues (t, i1, i2)</strong> + </dt> + <dd> + remove a range of values from a table. + End of range may be negative. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">i1</span> + <span class="types"><span class="type">int</span></span> + start index + </li> + <li><span class="parameter">i2</span> + <span class="types"><span class="type">int</span></span> + end index + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the table + </ol> + + + + +</dd> + <dt> + <a name = "readonly"></a> + <strong>readonly (t)</strong> + </dt> + <dd> + modifies a table to be read only. + This only offers weak protection. Tables can still be modified with + <a href="https://www.lua.org/manual/5.1/manual.html#pdf-table.insert">table.insert</a> and <a href="https://www.lua.org/manual/5.1/manual.html#pdf-rawset">rawset</a>.</p> + +<p> <em>NOTE</em>: for Lua 5.1 length, pairs and ipairs will not work, since the + equivalent metamethods are only available in Lua 5.2 and newer. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + the table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the table read only (a proxy). + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Copying"></a>Copying</h2> + + <dl class="function"> + <dt> + <a name = "update"></a> + <strong>update (t1, t2)</strong> + </dt> + <dd> + copy a table into another, in-place. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t1</span> + <span class="types"><span class="type">tab</span></span> + destination table + </li> + <li><span class="parameter">t2</span> + <span class="types"><span class="type">tab</span></span> + source (actually any iterable object) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + first table + </ol> + + + + +</dd> + <dt> + <a name = "copy"></a> + <strong>copy (t)</strong> + </dt> + <dd> + make a shallow copy of a table + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + an iterable source + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + new table + </ol> + + + + +</dd> + <dt> + <a name = "deepcopy"></a> + <strong>deepcopy (t)</strong> + </dt> + <dd> + make a deep copy of a table, recursively copying all the keys and fields. + This supports cycles in tables; cycles will be reproduced in the copy. + This will also set the copied table's metatable to that of the original. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + A table + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + new table + </ol> + + + + +</dd> + <dt> + <a name = "icopy"></a> + <strong>icopy (dest, src[, idest=1[, isrc=1[, nsrc=#src]]])</strong> + </dt> + <dd> + copy an array into another one, clearing <code>dest</code> after <code>idest+nsrc</code>, if necessary. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">dest</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">src</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">idest</span> + <span class="types"><span class="type">int</span></span> + where to start copying values into destination + (<em>default</em> 1) + </li> + <li><span class="parameter">isrc</span> + <span class="types"><span class="type">int</span></span> + where to start copying values from source + (<em>default</em> 1) + </li> + <li><span class="parameter">nsrc</span> + <span class="types"><span class="type">int</span></span> + number of elements to copy from source + (<em>default</em> #src) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "move"></a> + <strong>move (dest, src[, idest=1[, isrc=1[, nsrc=#src]]])</strong> + </dt> + <dd> + copy an array into another one. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">dest</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">src</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">idest</span> + <span class="types"><span class="type">int</span></span> + where to start copying values into destination + (<em>default</em> 1) + </li> + <li><span class="parameter">isrc</span> + <span class="types"><span class="type">int</span></span> + where to start copying values from source + (<em>default</em> 1) + </li> + <li><span class="parameter">nsrc</span> + <span class="types"><span class="type">int</span></span> + number of elements to copy from source + (<em>default</em> #src) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "insertvalues"></a> + <strong>insertvalues (t[, position], values)</strong> + </dt> + <dd> + insert values into a table. + similar to <a href="https://www.lua.org/manual/5.1/manual.html#pdf-table.insert">table.insert</a> but inserts values from given table <a href="../libraries/pl.tablex.html#values">values</a>, + not the object itself, into table <code>t</code> at position <code>pos</code>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + the list + </li> + <li><span class="parameter">position</span> + <span class="types"><span class="type">int</span></span> + (default is at end) + (<em>optional</em>) + </li> + <li><span class="parameter">values</span> + <span class="types"><span class="type">array</span></span> + + + + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Comparing"></a>Comparing</h2> + + <dl class="function"> + <dt> + <a name = "deepcompare"></a> + <strong>deepcompare (t1, t2[, ignore_mt[, eps]])</strong> + </dt> + <dd> + compare two values. + if they are tables, then compare their keys and fields recursively. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t1</span> + A value + </li> + <li><span class="parameter">t2</span> + A value + </li> + <li><span class="parameter">ignore_mt</span> + <span class="types"><span class="type">bool</span></span> + if true, ignore __eq metamethod (default false) + (<em>optional</em>) + </li> + <li><span class="parameter">eps</span> + <span class="types"><span class="type">number</span></span> + if defined, then used for any number comparisons + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + +</dd> + <dt> + <a name = "compare"></a> + <strong>compare (t1, t2, cmp)</strong> + </dt> + <dd> + compare two arrays using a predicate. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t1</span> + <span class="types"><span class="type">array</span></span> + an array + </li> + <li><span class="parameter">t2</span> + <span class="types"><span class="type">array</span></span> + an array + </li> + <li><span class="parameter">cmp</span> + <span class="types"><span class="type">func</span></span> + A comparison function; <code>bool = cmp(t1_value, t2_value)</code> + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true or false + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="global">assert</span>(tablex.compare({ <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span> }, { <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span> }, <span class="string">"=="</span>)) + +<span class="global">assert</span>(tablex.compare( + {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>, hello = <span class="string">"world"</span>}, <span class="comment">-- fields are not compared! +</span> {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}, <span class="keyword">function</span>(v1, v2) <span class="keyword">return</span> v1 == v2 <span class="keyword">end</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "compare_no_order"></a> + <strong>compare_no_order (t1, t2, cmp)</strong> + </dt> + <dd> + compare two list-like tables using an optional predicate, without regard for element order. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t1</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">t2</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">cmp</span> + A comparison function (may be nil) + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Finding"></a>Finding</h2> + + <dl class="function"> + <dt> + <a name = "find"></a> + <strong>find (t, val, idx)</strong> + </dt> + <dd> + return the index of a value in a list. + Like string.find, there is an optional index to start searching, + which can be negative. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + A list-like table + </li> + <li><span class="parameter">val</span> + A value + </li> + <li><span class="parameter">idx</span> + <span class="types"><span class="type">int</span></span> + index to start; -1 means last element,etc (default 1) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + index of value or nil if not found + </ol> + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">find({<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>},<span class="number">20</span>) == <span class="number">2</span></pre></li> + <li><pre class="example">find({<span class="string">'a'</span>,<span class="string">'b'</span>,<span class="string">'a'</span>,<span class="string">'c'</span>},<span class="string">'a'</span>,<span class="number">2</span>) == <span class="number">3</span></pre></li> + </ul> + +</dd> + <dt> + <a name = "rfind"></a> + <strong>rfind (t, val, idx)</strong> + </dt> + <dd> + return the index of a value in a list, searching from the end. + Like string.find, there is an optional index to start searching, + which can be negative. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + A list-like table + </li> + <li><span class="parameter">val</span> + A value + </li> + <li><span class="parameter">idx</span> + index to start; -1 means last element,etc (default <code>#t</code>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + index of value or nil if not found + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">rfind({<span class="number">10</span>,<span class="number">10</span>,<span class="number">10</span>},<span class="number">10</span>) == <span class="number">3</span></pre> + </ul> + +</dd> + <dt> + <a name = "find_if"></a> + <strong>find_if (t, cmp, arg)</strong> + </dt> + <dd> + return the index (or key) of a value in a table using a comparison function. </p> + +<p> <em>NOTE</em>: the 2nd return value of this function, the value returned + by the comparison function, has a limitation that it cannot be <code>false</code>. + Because if it is, then it indicates the comparison failed, and the + function will continue the search. See examples. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + A table + </li> + <li><span class="parameter">cmp</span> + <span class="types"><span class="type">func</span></span> + A comparison function + </li> + <li><span class="parameter">arg</span> + an optional second argument to the function + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + index of value, or nil if not found</li> + <li> + value returned by comparison function (cannot be <code>false</code>!)</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="comment">-- using an operator +</span><span class="keyword">local</span> lst = { <span class="string">"Rudolph"</span>, <span class="keyword">true</span>, <span class="keyword">false</span>, <span class="number">15</span> } +<span class="keyword">local</span> idx, cmp_result = tablex.rfind(lst, <span class="string">"=="</span>, <span class="string">"Rudolph"</span>) +<span class="global">assert</span>(idx == <span class="number">1</span>) +<span class="global">assert</span>(cmp_result == <span class="keyword">true</span>) + +<span class="keyword">local</span> idx, cmp_result = tablex.rfind(lst, <span class="string">"=="</span>, <span class="keyword">false</span>) +<span class="global">assert</span>(idx == <span class="number">3</span>) +<span class="global">assert</span>(cmp_result == <span class="keyword">true</span>) <span class="comment">-- looking up 'false' works! +</span> +<span class="comment">-- using a function returning the value looked up +</span><span class="keyword">local</span> cmp = <span class="keyword">function</span>(v1, v2) <span class="keyword">return</span> v1 == v2 <span class="keyword">and</span> v2 <span class="keyword">end</span> +<span class="keyword">local</span> idx, cmp_result = tablex.rfind(lst, cmp, <span class="string">"Rudolph"</span>) +<span class="global">assert</span>(idx == <span class="number">1</span>) +<span class="global">assert</span>(cmp_result == <span class="string">"Rudolph"</span>) <span class="comment">-- the value is returned +</span> +<span class="comment">-- NOTE: this fails, since 'false' cannot be returned! +</span><span class="keyword">local</span> idx, cmp_result = tablex.rfind(lst, cmp, <span class="keyword">false</span>) +<span class="global">assert</span>(idx == <span class="keyword">nil</span>) <span class="comment">-- looking up 'false' failed! +</span><span class="global">assert</span>(cmp_result == <span class="keyword">nil</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "search"></a> + <strong>search (t, value[, exclude])</strong> + </dt> + <dd> + find a value in a table by recursive search. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + the table + </li> + <li><span class="parameter">value</span> + the value + </li> + <li><span class="parameter">exclude</span> + <span class="types"><span class="type">array</span></span> + any tables to avoid searching + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a fieldspec, e.g. 'a.b' or 'math.sin' + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">search(_G,<span class="global">math</span>.sin,{<span class="global">package</span>.path}) == <span class="string">'math.sin'</span></pre> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="MappingAndFiltering"></a>MappingAndFiltering</h2> + + <dl class="function"> + <dt> + <a name = "map"></a> + <strong>map (fun, t, ...)</strong> + </dt> + <dd> + apply a function to all values of a table. + This returns a table of the results. + Any extra arguments are passed to the function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + A function that takes at least one argument + </li> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + A table + </li> + <li><span class="parameter">...</span> + optional arguments + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">map(<span class="keyword">function</span>(v) <span class="keyword">return</span> v*v <span class="keyword">end</span>, {<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,fred=<span class="number">2</span>}) is {<span class="number">100</span>,<span class="number">400</span>,<span class="number">900</span>,fred=<span class="number">4</span>}</pre> + </ul> + +</dd> + <dt> + <a name = "imap"></a> + <strong>imap (fun, t, ...)</strong> + </dt> + <dd> + apply a function to all values of a list. + This returns a table of the results. + Any extra arguments are passed to the function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + A function that takes at least one argument + </li> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a table (applies to array part) + </li> + <li><span class="parameter">...</span> + optional arguments + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a list-like table + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">imap(<span class="keyword">function</span>(v) <span class="keyword">return</span> v*v <span class="keyword">end</span>, {<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,fred=<span class="number">2</span>}) is {<span class="number">100</span>,<span class="number">400</span>,<span class="number">900</span>}</pre> + </ul> + +</dd> + <dt> + <a name = "map_named_method"></a> + <strong>map_named_method (name, t, ...)</strong> + </dt> + <dd> + apply a named method to values from a table. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the method name + </li> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">...</span> + any extra arguments to the method + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a <a href="../classes/pl.List.html">List</a> with the results of the method (1st result only) + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> Car = {} +Car.__index = Car +<span class="keyword">function</span> Car.new(car) + <span class="keyword">return</span> <span class="global">setmetatable</span>(car <span class="keyword">or</span> {}, Car) +<span class="keyword">end</span> +Car.speed = <span class="number">0</span> +<span class="keyword">function</span> Car:faster(increase) + self.speed = self.speed + increase + <span class="keyword">return</span> self.speed +<span class="keyword">end</span> + +<span class="keyword">local</span> ferrari = Car.new{ name = <span class="string">"Ferrari"</span> } +<span class="keyword">local</span> lamborghini = Car.new{ name = <span class="string">"Lamborghini"</span>, speed = <span class="number">50</span> } +<span class="keyword">local</span> cars = { ferrari, lamborghini } + +<span class="global">assert</span>(ferrari.speed == <span class="number">0</span>) +<span class="global">assert</span>(lamborghini.speed == <span class="number">50</span>) +tablex.map_named_method(<span class="string">"faster"</span>, cars, <span class="number">10</span>) +<span class="global">assert</span>(ferrari.speed == <span class="number">10</span>) +<span class="global">assert</span>(lamborghini.speed == <span class="number">60</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "map2"></a> + <strong>map2 (fun, t1, t2, ...)</strong> + </dt> + <dd> + apply a function to values from two tables. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function of at least two arguments + </li> + <li><span class="parameter">t1</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + <li><span class="parameter">t2</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + <li><span class="parameter">...</span> + extra arguments + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a table + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">map2(<span class="string">'+'</span>,{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,m=<span class="number">4</span>},{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,m=<span class="number">40</span>}) is {<span class="number">11</span>,<span class="number">22</span>,<span class="number">23</span>,m=<span class="number">44</span>}</pre> + </ul> + +</dd> + <dt> + <a name = "imap2"></a> + <strong>imap2 (fun, t1, t2, ...)</strong> + </dt> + <dd> + apply a function to values from two arrays. + The result will be the length of the shortest array. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function of at least two arguments + </li> + <li><span class="parameter">t1</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">t2</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">...</span> + extra arguments + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">imap2(<span class="string">'+'</span>,{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,m=<span class="number">4</span>},{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,m=<span class="number">40</span>}) is {<span class="number">11</span>,<span class="number">22</span>,<span class="number">23</span>}</pre> + </ul> + +</dd> + <dt> + <a name = "mapn"></a> + <strong>mapn (fun, ..., fun)</strong> + </dt> + <dd> + Apply a function to a number of tables. + A more general version of map + The result is a table containing the result of applying that function to the + ith value of each table. Length of output list is the minimum length of all the lists + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + A function that takes as many arguments as there are tables + </li> + <li><span class="parameter">...</span> + <span class="types"><span class="type">tab</span></span> + n tables + </li> + <li><span class="parameter">fun</span> + A function that takes as many arguments as there are tables + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">mapn(<span class="keyword">function</span>(x,y,z) <span class="keyword">return</span> x+y+z <span class="keyword">end</span>, {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>},{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>},{<span class="number">100</span>,<span class="number">200</span>,<span class="number">300</span>}) is {<span class="number">111</span>,<span class="number">222</span>,<span class="number">333</span>}</pre></li> + <li><pre class="example">mapn(<span class="global">math</span>.max, {<span class="number">1</span>,<span class="number">20</span>,<span class="number">300</span>},{<span class="number">10</span>,<span class="number">2</span>,<span class="number">3</span>},{<span class="number">100</span>,<span class="number">200</span>,<span class="number">100</span>}) is {<span class="number">100</span>,<span class="number">200</span>,<span class="number">300</span>}</pre></li> + </ul> + +</dd> + <dt> + <a name = "pairmap"></a> + <strong>pairmap (fun, t, ...)</strong> + </dt> + <dd> + call the function with the key and value pairs from a table. + The function can return a value and a key (note the order!). If both + are not nil, then this pair is inserted into the result: if the key already exists, we convert the value for that + key into a table and append into it. If only value is not nil, then it is appended to the result. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + A function which will be passed each key and value as arguments, plus any extra arguments to pairmap. + </li> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + A table + </li> + <li><span class="parameter">...</span> + optional arguments + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <li><pre class="example">pairmap(<span class="keyword">function</span>(k,v) <span class="keyword">return</span> v <span class="keyword">end</span>,{fred=<span class="number">10</span>,bonzo=<span class="number">20</span>}) is {<span class="number">10</span>,<span class="number">20</span>} _or_ {<span class="number">20</span>,<span class="number">10</span>}</pre></li> + <li><pre class="example">pairmap(<span class="keyword">function</span>(k,v) <span class="keyword">return</span> {k,v},k <span class="keyword">end</span>,{one=<span class="number">1</span>,two=<span class="number">2</span>}) is {one={<span class="string">'one'</span>,<span class="number">1</span>},two={<span class="string">'two'</span>,<span class="number">2</span>}}</pre></li> + </ul> + +</dd> + <dt> + <a name = "filter"></a> + <strong>filter (t, pred, arg)</strong> + </dt> + <dd> + filter an array's values using a predicate function + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">pred</span> + <span class="types"><span class="type">func</span></span> + a boolean function + </li> + <li><span class="parameter">arg</span> + optional argument to be passed as second argument of the predicate + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Iterating"></a>Iterating</h2> + + <dl class="function"> + <dt> + <a name = "foreach"></a> + <strong>foreach (t, fun, ...)</strong> + </dt> + <dd> + apply a function to all elements of a table. + The arguments to the function will be the value, + the key and <em>finally</em> any extra arguments passed to this function. + Note that the Lua 5.0 function table.foreach passed the <em>key</em> first. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function on the elements; <code>function(value, key, ...)</code> + </li> + <li><span class="parameter">...</span> + extra arguments passed to <code>fun</code> + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#transform">tablex.transform</a> + </ul> + + +</dd> + <dt> + <a name = "foreachi"></a> + <strong>foreachi (t, fun, ...)</strong> + </dt> + <dd> + apply a function to all elements of a list-like table in order. + The arguments to the function will be the value, + the index and <em>finally</em> any extra arguments passed to this function + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a table + </li> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a function with at least one argument + </li> + <li><span class="parameter">...</span> + optional arguments + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "sort"></a> + <strong>sort (t, f)</strong> + </dt> + <dd> + return an iterator to a table sorted by its keys + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + the table + </li> + <li><span class="parameter">f</span> + <span class="types"><span class="type">func</span></span> + an optional comparison function (f(x,y) is true if x < y) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + an iterator to traverse elements sorted by the keys + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">for</span> k,v <span class="keyword">in</span> tablex.sort(t) <span class="keyword">do</span> <span class="global">print</span>(k,v) <span class="keyword">end</span></pre> + </ul> + +</dd> + <dt> + <a name = "sortv"></a> + <strong>sortv (t, f)</strong> + </dt> + <dd> + return an iterator to a table sorted by its values + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + the table + </li> + <li><span class="parameter">f</span> + <span class="types"><span class="type">func</span></span> + an optional comparison function (f(x,y) is true if x < y) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + an iterator to traverse elements sorted by the values + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">for</span> k,v <span class="keyword">in</span> tablex.sortv(t) <span class="keyword">do</span> <span class="global">print</span>(k,v) <span class="keyword">end</span></pre> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="Extraction"></a>Extraction</h2> + + <dl class="function"> + <dt> + <a name = "keys"></a> + <strong>keys (t)</strong> + </dt> + <dd> + return all the keys of a table in arbitrary order. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + A table + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "values"></a> + <strong>values (t)</strong> + </dt> + <dd> + return all the values of the table in arbitrary order + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">tab</span></span> + A table + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "sub"></a> + <strong>sub (t, first, last)</strong> + </dt> + <dd> + Extract a range from a table, like 'string.sub'. + If first or last are negative then they are relative to the end of the list + eg. sub(t,-2) gives last 2 entries in a list, and + sub(t,-4,-2) gives from -4th to -2nd + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + <span class="types"><span class="type">array</span></span> + a list-like table + </li> + <li><span class="parameter">first</span> + <span class="types"><span class="type">int</span></span> + An index + </li> + <li><span class="parameter">last</span> + <span class="types"><span class="type">int</span></span> + An index + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a new List + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Merging"></a>Merging</h2> + + <dl class="function"> + <dt> + <a name = "merge"></a> + <strong>merge (t1, t2, dup)</strong> + </dt> + <dd> + combine two tables, either as union or intersection. Corresponds to + set operations for sets () but more general. Not particularly + useful for list-like tables. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t1</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + <li><span class="parameter">t2</span> + <span class="types"><span class="type">tab</span></span> + a table + </li> + <li><span class="parameter">dup</span> + <span class="types"><span class="type">bool</span></span> + true for a union, false for an intersection. + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.tablex.html#index_map">tablex.index_map</a> + </ul> + + <h3>Usage:</h3> + <ul> + <li><pre class="example">merge({alice=<span class="number">23</span>,fred=<span class="number">34</span>},{bob=<span class="number">25</span>,fred=<span class="number">34</span>}) is {fred=<span class="number">34</span>}</pre></li> + <li><pre class="example">merge({alice=<span class="number">23</span>,fred=<span class="number">34</span>},{bob=<span class="number">25</span>,fred=<span class="number">34</span>},<span class="keyword">true</span>) is {bob=<span class="number">25</span>,fred=<span class="number">34</span>,alice=<span class="number">23</span>}</pre></li> + </ul> + +</dd> + <dt> + <a name = "difference"></a> + <strong>difference (s1, s2, symm)</strong> + </dt> + <dd> + a new table which is the difference of two tables. + With sets (where the values are all true) this is set difference and + symmetric difference depending on the third parameter. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s1</span> + <span class="types"><span class="type">tab</span></span> + a map-like table or set + </li> + <li><span class="parameter">s2</span> + <span class="types"><span class="type">tab</span></span> + a map-like table or set + </li> + <li><span class="parameter">symm</span> + <span class="types"><span class="type">bool</span></span> + symmetric difference (default false) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a map-like table or set + </ol> + + + + +</dd> + <dt> + <a name = "zip"></a> + <strong>zip (...)</strong> + </dt> + <dd> + return a table where each element is a table of the ith values of an arbitrary + number of tables. It is equivalent to a matrix transpose. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + <span class="types"><span class="type">array</span></span> + arrays to be zipped + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">zip({<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>},{<span class="number">100</span>,<span class="number">200</span>,<span class="number">300</span>}) is {{<span class="number">10</span>,<span class="number">100</span>},{<span class="number">20</span>,<span class="number">200</span>},{<span class="number">30</span>,<span class="number">300</span>}}</pre> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.template.html b/Data/Libraries/Penlight/docs/libraries/pl.template.html new file mode 100644 index 0000000..c2ac2c0 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.template.html @@ -0,0 +1,336 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><strong>pl.template</strong></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.template</code></h1> +<p>A template preprocessor.</p> +<p> Originally by <a href="http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor">Ricki Lake</a></p> + +<p> There are two rules:</p> + +<ul> + <li>lines starting with # are Lua</li> + <li>otherwise, <code>$(expr)</code> is the result of evaluating <code>expr</code></li> +</ul> + +<p> Example:</p> + + +<pre> +# <span class="keyword">for</span> i = <span class="number">1</span>,<span class="number">3</span> <span class="keyword">do</span> + $(i) Hello, Word! +# <span class="keyword">end</span> +===> +<span class="number">1</span> Hello, Word! +<span class="number">2</span> Hello, Word! +<span class="number">3</span> Hello, Word! +</pre> + +<p> Other escape characters can be used, when the defaults conflict + with the output language.</p> + + +<pre> +> <span class="keyword">for</span> _,n <span class="keyword">in</span> <span class="global">pairs</span>{<span class="string">'one'</span>,<span class="string">'two'</span>,<span class="string">'three'</span>} <span class="keyword">do</span> +static int l_${n} (luaState *state); +> <span class="keyword">end</span> +</pre> + +<p> See <a href="../manual/03-strings.md.html#Another_Style_of_Template">the Guide</a>.</p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#substitute">substitute (str[, env])</a></td> + <td class="summary">expand the template using the specified environment.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#ct:render">ct:render ([env[, parent[, db]]])</a></td> + <td class="summary">executes the previously compiled template and renders it.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#compile">compile (str[, opts])</a></td> + <td class="summary">compiles the template.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "substitute"></a> + <strong>substitute (str[, env])</strong> + </dt> + <dd> + +<p>expand the template using the specified environment. + This function will compile and render the template. For more performant + recurring usage use the two step approach by using <a href="../libraries/pl.template.html#compile">compile</a> and <a href="../libraries/pl.template.html#ct:render">ct:render</a>. + There are six special fields in the environment table <code>env</code></p> + +<ul> + <li><code>_parent</code>: continue looking up in this table (e.g. <code>_parent=_G</code>).</li> + <li><code>_brackets</code>: bracket pair that wraps inline Lua expressions, default is '()'.</li> + <li><code>_escape</code>: character marking Lua lines, default is '#'</li> + <li><code>_inline_escape</code>: character marking inline Lua expression, default is '$'.</li> + <li><code>_chunk_name</code>: chunk name for loaded templates, used if there + is an error in Lua code. Default is 'TMP'.</li> + <li><code>_debug</code>: if truthy, the generated code will be printed upon a render error</li> +</ul> + + + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">str</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the template string + </li> + <li><span class="parameter">env</span> + <span class="types"><span class="type">tab</span></span> + the environment + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <code>rendered template + nil + source_code</code>, or <code>nil + error + source_code</code>. The last + return value (<code>source_code</code>) is only returned if the debug option is used. + </ol> + + + + +</dd> + <dt> + <a name = "ct:render"></a> + <strong>ct:render ([env[, parent[, db]]])</strong> + </dt> + <dd> + executes the previously compiled template and renders it. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">env</span> + <span class="types"><span class="type">tab</span></span> + the environment. + (<em>optional</em>) + </li> + <li><span class="parameter">parent</span> + <span class="types"><span class="type">tab</span></span> + continue looking up in this table (e.g. <code>parent=_G</code>). + (<em>optional</em>) + </li> + <li><span class="parameter">db</span> + <span class="types"><span class="type">bool</span></span> + if thruthy, it will print the code upon a render error + (provided the template was compiled with the debug option). + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <code>rendered template + nil + source_code</code>, or <code>nil + error + source_code</code>. The last return value + (<code>source_code</code>) is only returned if the template was compiled with the debug option. + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> ct, err = template.compile(my_template) +<span class="keyword">local</span> rendered , err = ct:render(my_env, parent)</pre> + </ul> + +</dd> + <dt> + <a name = "compile"></a> + <strong>compile (str[, opts])</strong> + </dt> + <dd> + +<p>compiles the template. + Returns an object that can repeatedly be rendered without parsing/compiling + the template again. + The options passed in the <code>opts</code> table support the following options:</p> + +<ul> + <li><code>chunk_name</code>: chunk name for loaded templates, used if there + is an error in Lua code. Default is 'TMP'.</li> + <li><code>escape</code>: character marking Lua lines, default is '#'</li> + <li><code>inline_escape</code>: character marking inline Lua expression, default is '$'.</li> + <li><code>inline_brackets</code>: bracket pair that wraps inline Lua expressions, default is '()'.</li> + <li><code>newline</code>: string to replace newline characters, default is <code>nil</code> (not replacing newlines).</li> + <li><a href="https://www.lua.org/manual/5.1/manual.html#5.9">debug</a>: if truthy, the generated source code will be retained within the compiled template object, default is <code>nil</code>.</li> +</ul> + + + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">str</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the template string + </li> + <li><span class="parameter">opts</span> + <span class="types"><span class="type">tab</span></span> + the compilation options to use + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + template object, or <code>nil + error + source_code</code> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> ct, err = template.compile(my_template) +<span class="keyword">local</span> rendered , err = ct:render(my_env, parent)</pre> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.test.html b/Data/Libraries/Penlight/docs/libraries/pl.test.html new file mode 100644 index 0000000..72aff1d --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.test.html @@ -0,0 +1,445 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><strong>pl.test</strong></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.test</code></h1> +<p>Useful test utilities.</p> +<p> + + + +<pre> +test.asserteq({<span class="number">1</span>,<span class="number">2</span>},{<span class="number">1</span>,<span class="number">2</span>}) <span class="comment">-- can compare tables +</span>test.asserteq(<span class="number">1.2</span>,<span class="number">1.19</span>,<span class="number">0.02</span>) <span class="comment">-- compare FP numbers within precision +</span>T = test.tuple <span class="comment">-- used for comparing multiple results +</span>test.asserteq(T(<span class="global">string</span>.find(<span class="string">" me"</span>,<span class="string">"me"</span>)),T(<span class="number">2</span>,<span class="number">3</span>)) +</pre> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.tablex.html#">pl.tablex</a>, <a href="../libraries/pl.pretty.html#">pl.pretty</a>, <a href="../libraries/pl.path.html#">pl.path</a>, <a href="https://www.lua.org/manual/5.1/manual.html#5.9">debug</a></p> +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#error_handler">error_handler (file, line, got_text, needed_text, msg)</a></td> + <td class="summary">error handling for test results.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#complain">complain (x, y, msg, where)</a></td> + <td class="summary">general test complain message.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#asserteq">asserteq (x, y, eps, where)</a></td> + <td class="summary">like assert, except takes two arguments that must be equal and can be tables.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#assertmatch">assertmatch (s1, s2, where)</a></td> + <td class="summary">assert that the first string matches the second.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#assertraise">assertraise (fn, e, where)</a></td> + <td class="summary">assert that the function raises a particular error.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#asserteq2">asserteq2 (x1, x2, y1, y2, where)</a></td> + <td class="summary">a version of asserteq that takes two pairs of values.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#tuple">tuple (...)</a></td> + <td class="summary">encode an arbitrary argument list as a tuple.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#timer">timer (msg, n, fun, ...)</a></td> + <td class="summary">Time a function.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "error_handler"></a> + <strong>error_handler (file, line, got_text, needed_text, msg)</strong> + </dt> + <dd> + error handling for test results. + By default, this writes to stderr and exits the program. + Re-define this function to raise an error and/or redirect output + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">file</span> + + + + </li> + <li><span class="parameter">line</span> + + + + </li> + <li><span class="parameter">got_text</span> + + + + </li> + <li><span class="parameter">needed_text</span> + + + + </li> + <li><span class="parameter">msg</span> + + + + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "complain"></a> + <strong>complain (x, y, msg, where)</strong> + </dt> + <dd> + general test complain message. + Useful for composing new test functions (see tests/tablex.lua for an example) + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">x</span> + a value + </li> + <li><span class="parameter">y</span> + value to compare first value against + </li> + <li><span class="parameter">msg</span> + message + </li> + <li><span class="parameter">where</span> + extra level offset for errors + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "asserteq"></a> + <strong>asserteq (x, y, eps, where)</strong> + </dt> + <dd> + like assert, except takes two arguments that must be equal and can be tables. + If they are plain tables, it will use tablex.deepcompare. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">x</span> + any value + </li> + <li><span class="parameter">y</span> + a value equal to x + </li> + <li><span class="parameter">eps</span> + an optional tolerance for numerical comparisons + </li> + <li><span class="parameter">where</span> + extra level offset + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "assertmatch"></a> + <strong>assertmatch (s1, s2, where)</strong> + </dt> + <dd> + assert that the first string matches the second. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s1</span> + a string + </li> + <li><span class="parameter">s2</span> + a string + </li> + <li><span class="parameter">where</span> + extra level offset + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "assertraise"></a> + <strong>assertraise (fn, e, where)</strong> + </dt> + <dd> + assert that the function raises a particular error. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fn</span> + a function or a table of the form {function,arg1,...} + </li> + <li><span class="parameter">e</span> + a string to match the error against + </li> + <li><span class="parameter">where</span> + extra level offset + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "asserteq2"></a> + <strong>asserteq2 (x1, x2, y1, y2, where)</strong> + </dt> + <dd> + a version of asserteq that takes two pairs of values. + <code>x1==y1 and x2==y2</code> must be true. Useful for functions that naturally + return two values. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">x1</span> + any value + </li> + <li><span class="parameter">x2</span> + any value + </li> + <li><span class="parameter">y1</span> + any value + </li> + <li><span class="parameter">y2</span> + any value + </li> + <li><span class="parameter">where</span> + extra level offset + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "tuple"></a> + <strong>tuple (...)</strong> + </dt> + <dd> + encode an arbitrary argument list as a tuple. + This can be used to compare to other argument lists, which is + very useful for testing functions which return a number of values. + Unlike regular array-like tables ('sequences') they may contain nils. + Tuples understand equality and know how to print themselves out. + The # operator is defined to be the size, irrespecive of any nils, + and there is an <a href="https://www.lua.org/manual/5.1/manual.html#pdf-unpack">unpack</a> method. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + + + + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">asserteq(tuple( (<span class="string">'ab'</span>):find <span class="string">'a'</span>), tuple(<span class="number">1</span>,<span class="number">1</span>))</pre> + </ul> + +</dd> + <dt> + <a name = "timer"></a> + <strong>timer (msg, n, fun, ...)</strong> + </dt> + <dd> + Time a function. Call the function a given number of times, and report the number of seconds taken, + together with a message. Any extra arguments will be passed to the function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">msg</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a descriptive message + </li> + <li><span class="parameter">n</span> + <span class="types"><span class="type">int</span></span> + number of times to call the function + </li> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + the function + </li> + <li><span class="parameter">...</span> + optional arguments to fun + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.text.html b/Data/Libraries/Penlight/docs/libraries/pl.text.html new file mode 100644 index 0000000..77b7db1 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.text.html @@ -0,0 +1,381 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><strong>pl.text</strong></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.text</code></h1> +<p>Text processing utilities.</p> +<p> This provides a Template class (modeled after the same from the Python + libraries, see string.Template). It also provides similar functions to those + found in the textwrap module.</p> + +<p> See <a href="../manual/03-strings.md.html#String_Templates">the Guide</a>.</p> + +<p> Calling <code>text.format_operator()</code> overloads the % operator for strings to give Python/Ruby style formated output. + This is extended to also do template-like substitution for map-like data.</p> + + +<pre> +> <span class="global">require</span> <span class="string">'pl.text'</span>.format_operator() +> = <span class="string">'%s = %5.3f'</span> % {<span class="string">'PI'</span>,<span class="global">math</span>.pi} +PI = <span class="number">3.142</span> +> = <span class="string">'$name = $value'</span> % {name=<span class="string">'dog'</span>,value=<span class="string">'Pluto'</span>} +dog = Pluto +</pre> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a>, <a href="../libraries/pl.types.html#">pl.types</a></p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#indent">indent (s, n, ch)</a></td> + <td class="summary">indent a multiline string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#dedent">dedent (s)</a></td> + <td class="summary">dedent a multiline string by removing any initial indent.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#wrap">wrap (s, width)</a></td> + <td class="summary">format a paragraph into lines so that they fit into a line width.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#fill">fill (s, width)</a></td> + <td class="summary">format a paragraph so that it fits into a line width.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Template:substitute">Template:substitute (tbl)</a></td> + <td class="summary">substitute values into a template, throwing an error.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Template:safe_substitute">Template:safe_substitute (tbl)</a></td> + <td class="summary">substitute values into a template.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Template:indent_substitute">Template:indent_substitute (tbl)</a></td> + <td class="summary">substitute values into a template, preserving indentation.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "indent"></a> + <strong>indent (s, n, ch)</strong> + </dt> + <dd> + indent a multiline string. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + the string + </li> + <li><span class="parameter">n</span> + the size of the indent + </li> + <li><span class="parameter">ch</span> + the character to use when indenting (default ' ') + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + indented string + </ol> + + + + +</dd> + <dt> + <a name = "dedent"></a> + <strong>dedent (s)</strong> + </dt> + <dd> + dedent a multiline string by removing any initial indent. + useful when working with [[..]] strings. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + the string + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a string with initial indent zero. + </ol> + + + + +</dd> + <dt> + <a name = "wrap"></a> + <strong>wrap (s, width)</strong> + </dt> + <dd> + format a paragraph into lines so that they fit into a line width. + It will not break long words, so lines can be over the length + to that extent. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + the string + </li> + <li><span class="parameter">width</span> + the margin width, default 70 + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a list of lines (List object) + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../classes/pl.List.html#">pl.List</a> + </ul> + + +</dd> + <dt> + <a name = "fill"></a> + <strong>fill (s, width)</strong> + </dt> + <dd> + format a paragraph so that it fits into a line width. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + the string + </li> + <li><span class="parameter">width</span> + the margin width, default 70 + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a string + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.text.html#wrap">wrap</a> + </ul> + + +</dd> + <dt> + <a name = "Template:substitute"></a> + <strong>Template:substitute (tbl)</strong> + </dt> + <dd> + substitute values into a template, throwing an error. + This will throw an error if no name is found. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tbl</span> + a table of name-value pairs. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Template:safe_substitute"></a> + <strong>Template:safe_substitute (tbl)</strong> + </dt> + <dd> + substitute values into a template. + This version just passes unknown names through. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tbl</span> + a table of name-value pairs. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Template:indent_substitute"></a> + <strong>Template:indent_substitute (tbl)</strong> + </dt> + <dd> + substitute values into a template, preserving indentation. <br> + If the value is a multiline string <em>or</em> a template, it will insert + the lines at the correct indentation. <br> + Furthermore, if a template, then that template will be subsituted + using the same table. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tbl</span> + a table of name-value pairs. + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.types.html b/Data/Libraries/Penlight/docs/libraries/pl.types.html new file mode 100644 index 0000000..252005e --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.types.html @@ -0,0 +1,475 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><strong>pl.types</strong></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.types</code></h1> +<p>Dealing with Detailed Type Information</p> +<p> + +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#is_callable">is_callable (obj)</a></td> + <td class="summary">is the object either a function or a callable object?.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#is_type">is_type (obj, tp)</a></td> + <td class="summary">is the object of the specified type?.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#type">type (obj)</a></td> + <td class="summary">a string representation of a type.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#is_integer">is_integer (x)</a></td> + <td class="summary">is this number an integer?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#is_empty">is_empty (o, ignore_spaces)</a></td> + <td class="summary">Check if the object is "empty".</td> + </tr> + <tr> + <td class="name" nowrap><a href="#is_indexable">is_indexable (val)</a></td> + <td class="summary">is an object 'array-like'?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#is_iterable">is_iterable (val)</a></td> + <td class="summary">can an object be iterated over with <a href="https://www.lua.org/manual/5.1/manual.html#pdf-pairs">pairs</a>?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#is_writeable">is_writeable (val)</a></td> + <td class="summary">can an object accept new key/pair values?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#to_bool">to_bool (o[, true_strs[, check_objs]])</a></td> + <td class="summary">Convert to a boolean value.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "is_callable"></a> + <strong>is_callable (obj)</strong> + </dt> + <dd> + is the object either a function or a callable object?. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">obj</span> + Object to check. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "is_type"></a> + <strong>is_type (obj, tp)</strong> + </dt> + <dd> + is the object of the specified type?. + If the type is a string, then use type, otherwise compare with metatable.</p> + +<p> NOTE: this function is imported from <a href="../libraries/pl.utils.html#is_type">utils.is_type</a>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">obj</span> + An object to check + </li> + <li><span class="parameter">tp</span> + The expected type + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.utils.html#is_type">utils.is_type</a> + </ul> + + +</dd> + <dt> + <a name = "type"></a> + <strong>type (obj)</strong> + </dt> + <dd> + a string representation of a type. + For tables and userdata with metatables, we assume that the metatable has a <code>_name</code> + field. If the field is not present it will return 'unknown table' or + 'unknown userdata'. + Lua file objects return the type 'file'. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">obj</span> + an object + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a string like 'number', 'table', 'file' or 'List' + </ol> + + + + +</dd> + <dt> + <a name = "is_integer"></a> + <strong>is_integer (x)</strong> + </dt> + <dd> + is this number an integer? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">x</span> + a number + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + boolean + </ol> + + <h3>Raises:</h3> + error if x is not a number + + + +</dd> + <dt> + <a name = "is_empty"></a> + <strong>is_empty (o, ignore_spaces)</strong> + </dt> + <dd> + +<p>Check if the object is "empty". + An object is considered empty if it is:</p> + +<ul> + <li><code>nil</code></li> + <li>a table without any items (key-value pairs or indexes)</li> + <li>a string with no content ("")</li> + <li>not a nil/table/string</li> +</ul> + + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">o</span> + The object to check if it is empty. + </li> + <li><span class="parameter">ignore_spaces</span> + If the object is a string and this is true the string is + considered empty if it only contains spaces. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <code>true</code> if the object is empty, otherwise a falsy value. + </ol> + + + + +</dd> + <dt> + <a name = "is_indexable"></a> + <strong>is_indexable (val)</strong> + </dt> + <dd> + is an object 'array-like'? + An object is array like if:</p> + +<ul> + <li>it is a table, or</li> + <li>it has a metatable with <code>__len</code> and <code>__index</code> methods</li> +</ul> + +<p> NOTE: since <code>__len</code> is 5.2+, on 5.1 is usually returns <code>false</code> for userdata + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">val</span> + any value. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <code>true</code> if the object is array-like, otherwise a falsy value. + </ol> + + + + +</dd> + <dt> + <a name = "is_iterable"></a> + <strong>is_iterable (val)</strong> + </dt> + <dd> + can an object be iterated over with <a href="https://www.lua.org/manual/5.1/manual.html#pdf-pairs">pairs</a>? + An object is iterable if:</p> + +<ul> + <li>it is a table, or</li> + <li>it has a metatable with a <code>__pairs</code> meta method</li> +</ul> + +<p> NOTE: since <code>__pairs</code> is 5.2+, on 5.1 is usually returns <code>false</code> for userdata + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">val</span> + any value. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <code>true</code> if the object is iterable, otherwise a falsy value. + </ol> + + + + +</dd> + <dt> + <a name = "is_writeable"></a> + <strong>is_writeable (val)</strong> + </dt> + <dd> + +<p>can an object accept new key/pair values? + An object is iterable if:</p> + +<ul> + <li>it is a table, or</li> + <li>it has a metatable with a <code>__newindex</code> meta method</li> +</ul> + + + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">val</span> + any value. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <code>true</code> if the object is writeable, otherwise a falsy value. + </ol> + + + + +</dd> + <dt> + <a name = "to_bool"></a> + <strong>to_bool (o[, true_strs[, check_objs]])</strong> + </dt> + <dd> + +<p>Convert to a boolean value. + True values are:</p> + +<ul> + <li>boolean: true.</li> + <li>string: 'yes', 'y', 'true', 't', '1' or additional strings specified by <code>true_strs</code>.</li> + <li>number: Any non-zero value.</li> + <li>table: Is not empty and <code>check_objs</code> is true.</li> + <li>everything else: Is not <code>nil</code> and <code>check_objs</code> is true.</li> +</ul> + + + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">o</span> + The object to evaluate. + </li> + <li><span class="parameter">true_strs</span> + optional Additional strings that when matched should evaluate to true. Comparison is case insensitive. + This should be a List of strings. E.g. "ja" to support German. + (<em>optional</em>) + </li> + <li><span class="parameter">check_objs</span> + True if objects should be evaluated. + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + true if the input evaluates to true, otherwise false. + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.url.html b/Data/Libraries/Penlight/docs/libraries/pl.url.html new file mode 100644 index 0000000..7c97371 --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.url.html @@ -0,0 +1,212 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><strong>pl.url</strong></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.url</code></h1> +<p>Python-style URL quoting library.</p> +<p> + +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#quote">quote (s, quote_plus)</a></td> + <td class="summary">Quote the url, replacing special characters using the '%xx' escape.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#unquote">unquote (s)</a></td> + <td class="summary">Unquote the url, replacing '%xx' escapes and plus signs.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "quote"></a> + <strong>quote (s, quote_plus)</strong> + </dt> + <dd> + Quote the url, replacing special characters using the '%xx' escape. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + <li><span class="parameter">quote_plus</span> + <span class="types"><span class="type">bool</span></span> + Also escape slashes and replace spaces by plus signs. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + The quoted string, or if <code>s</code> wasn't a string, just plain unaltered <code>s</code>. + </ol> + + + + +</dd> + <dt> + <a name = "unquote"></a> + <strong>unquote (s)</strong> + </dt> + <dd> + Unquote the url, replacing '%xx' escapes and plus signs. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the string + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + The unquoted string, or if <code>s</code> wasn't a string, just plain unaltered <code>s</code>. + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.utils.html b/Data/Libraries/Penlight/docs/libraries/pl.utils.html new file mode 100644 index 0000000..ee1773a --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.utils.html @@ -0,0 +1,1384 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +<li><a href="#Tables">Tables</a></li> +<li><a href="#Error_handling">Error handling </a></li> +<li><a href="#File_handling">File handling </a></li> +<li><a href="#OS_functions">OS functions </a></li> +<li><a href="#String_functions">String functions </a></li> +<li><a href="#Functional">Functional </a></li> +<li><a href="#Deprecation">Deprecation </a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><strong>pl.utils</strong></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.utils</code></h1> +<p>Generally useful routines.</p> +<p> See <a href="../manual/01-introduction.md.html#Generally_useful_functions_">the Guide</a>.</p> + +<p> Dependencies: <a href="../libraries/pl.compat.html#">pl.compat</a>, all exported fields and functions from + <a href="../libraries/pl.compat.html#">pl.compat</a> are also available in this module.</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#pack">pack (...)</a></td> + <td class="summary">pack an argument list into a table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#unpack">unpack (t[, i[, t]])</a></td> + <td class="summary">unpack a table and return its contents.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#printf">printf (fmt, ...)</a></td> + <td class="summary">print an arbitrary number of arguments using a format.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#fprintf">fprintf (f, fmt, ...)</a></td> + <td class="summary">write an arbitrary number of arguments to a file using a format.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#import">import (t, T)</a></td> + <td class="summary">take a table and 'inject' it into the local namespace.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#choose">choose (cond, value1, value2)</a></td> + <td class="summary">return either of two values, depending on a condition.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#array_tostring">array_tostring (t[, temp[, tostr]])</a></td> + <td class="summary">convert an array of values to strings.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#is_type">is_type (obj, tp)</a></td> + <td class="summary">is the object of the specified type?</td> + </tr> +</table> +<h2><a href="#Tables">Tables</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#patterns">patterns</a></td> + <td class="summary">Some standard patterns</td> + </tr> + <tr> + <td class="name" nowrap><a href="#stdmt">stdmt</a></td> + <td class="summary">Standard meta-tables as used by other Penlight modules</td> + </tr> +</table> +<h2><a href="#Error_handling">Error handling </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#assert_arg">assert_arg (n, val, tp, verify, msg, lev)</a></td> + <td class="summary">assert that the given argument is in fact of the correct type.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#function_arg">function_arg (idx, f, msg)</a></td> + <td class="summary">process a function argument.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#assert_string">assert_string (n, val)</a></td> + <td class="summary">assert the common case that the argument is a string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#on_error">on_error (mode)</a></td> + <td class="summary">control the error strategy used by Penlight.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#raise">raise (err)</a></td> + <td class="summary">used by Penlight functions to return errors.</td> + </tr> +</table> +<h2><a href="#File_handling">File handling </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#readfile">readfile (filename, is_bin)</a></td> + <td class="summary">return the contents of a file as a string</td> + </tr> + <tr> + <td class="name" nowrap><a href="#writefile">writefile (filename, str, is_bin)</a></td> + <td class="summary">write a string to a file</td> + </tr> + <tr> + <td class="name" nowrap><a href="#readlines">readlines (filename)</a></td> + <td class="summary">return the contents of a file as a list of lines</td> + </tr> +</table> +<h2><a href="#OS_functions">OS functions </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#executeex">executeex (cmd, bin)</a></td> + <td class="summary">execute a shell command and return the output.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#quote_arg">quote_arg (argument)</a></td> + <td class="summary">Quote and escape an argument of a command.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#quit">quit ([code], msg, ...)</a></td> + <td class="summary">error out of this program gracefully.</td> + </tr> +</table> +<h2><a href="#String_functions">String functions </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#escape">escape (s)</a></td> + <td class="summary">escape any Lua 'magic' characters in a string</td> + </tr> + <tr> + <td class="name" nowrap><a href="#split">split (s, re, plain, n)</a></td> + <td class="summary">split a string into a list of strings separated by a delimiter.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#splitv">splitv (s, re, plain, n)</a></td> + <td class="summary">split a string into a number of return values.</td> + </tr> +</table> +<h2><a href="#Functional">Functional </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#memoize">memoize (func)</a></td> + <td class="summary">'memoize' a function (cache returned value for next call).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#add_function_factory">add_function_factory (mt, fun)</a></td> + <td class="summary">associate a function factory with a type.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#string_lambda">string_lambda (lf)</a></td> + <td class="summary">an anonymous function as a string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#bind1">bind1 (fn, p)</a></td> + <td class="summary">bind the first argument of the function to a value.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#bind2">bind2 (fn, p)</a></td> + <td class="summary">bind the second argument of the function to a value.</td> + </tr> +</table> +<h2><a href="#Deprecation">Deprecation </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#set_deprecation_func">set_deprecation_func (func)</a></td> + <td class="summary">Sets a deprecation warning function.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#raise_deprecation">raise_deprecation (opts)</a></td> + <td class="summary">raises a deprecation warning.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "pack"></a> + <strong>pack (...)</strong> + </dt> + <dd> + pack an argument list into a table. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + any arguments + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a table with field <code>n</code> set to the length + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.compat.html#table.pack">compat.pack</a> + </ul> + + +</dd> + <dt> + <a name = "unpack"></a> + <strong>unpack (t[, i[, t]])</strong> + </dt> + <dd> + unpack a table and return its contents. </p> + +<p> NOTE: this implementation differs from the Lua implementation in the way + that this one DOES honor the <code>n</code> field in the table <code>t</code>, such that it is 'nil-safe'. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + index of the last element to unpack, defaults to <code>t.n</code> or <code>#t</code> + (<em>optional</em>) + </li> + <li><span class="parameter">i</span> + index from which to start unpacking, defaults to 1 + (<em>optional</em>) + </li> + <li><span class="parameter">t</span> + index of the last element to unpack, defaults to <code>t.n</code> or <code>#t</code> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + multiple return values from the table + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.compat.html#table.unpack">compat.unpack</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> t = <span class="global">table</span>.pack(<span class="keyword">nil</span>, <span class="keyword">nil</span>, <span class="keyword">nil</span>, <span class="number">4</span>) +<span class="keyword">local</span> a, b, c, d = <span class="global">table</span>.<span class="global">unpack</span>(t) <span class="comment">-- this <a href="../libraries/pl.utils.html#unpack">unpack</a> is NOT nil-safe, so d == nil +</span> +<span class="keyword">local</span> a, b, c, d = utils.<span class="global">unpack</span>(t) <span class="comment">-- this is nil-safe, so d == 4</span></pre> + </ul> + +</dd> + <dt> + <a name = "printf"></a> + <strong>printf (fmt, ...)</strong> + </dt> + <dd> + print an arbitrary number of arguments using a format. + Output will be sent to <code>stdout</code>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fmt</span> + The format (see <a href="https://www.lua.org/manual/5.1/manual.html#pdf-string.format">string.format</a>) + </li> + <li><span class="parameter">...</span> + Extra arguments for format + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "fprintf"></a> + <strong>fprintf (f, fmt, ...)</strong> + </dt> + <dd> + write an arbitrary number of arguments to a file using a format. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">f</span> + File handle to write to. + </li> + <li><span class="parameter">fmt</span> + The format (see <a href="https://www.lua.org/manual/5.1/manual.html#pdf-string.format">string.format</a>). + </li> + <li><span class="parameter">...</span> + Extra arguments for format + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "import"></a> + <strong>import (t, T)</strong> + </dt> + <dd> + take a table and 'inject' it into the local namespace. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + The table (table), or module name (string), defaults to this <a href="../libraries/pl.utils.html">utils</a> module table + </li> + <li><span class="parameter">T</span> + An optional destination table (defaults to callers environment) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "choose"></a> + <strong>choose (cond, value1, value2)</strong> + </dt> + <dd> + return either of two values, depending on a condition. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">cond</span> + A condition + </li> + <li><span class="parameter">value1</span> + Value returned if cond is truthy + </li> + <li><span class="parameter">value2</span> + Value returned if cond is falsy + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "array_tostring"></a> + <strong>array_tostring (t[, temp[, tostr]])</strong> + </dt> + <dd> + convert an array of values to strings. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + a list-like table + </li> + <li><span class="parameter">temp</span> + (table) buffer to use, otherwise allocate + (<em>optional</em>) + </li> + <li><span class="parameter">tostr</span> + custom tostring function, called with (value,index). Defaults to <a href="https://www.lua.org/manual/5.1/manual.html#pdf-tostring">tostring</a>. + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the converted buffer + </ol> + + + + +</dd> + <dt> + <a name = "is_type"></a> + <strong>is_type (obj, tp)</strong> + </dt> + <dd> + is the object of the specified type? + If the type is a string, then use type, otherwise compare with metatable + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">obj</span> + An object to check + </li> + <li><span class="parameter">tp</span> + String of what type it should be + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + boolean + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">utils.is_type(<span class="string">"hello world"</span>, <span class="string">"string"</span>) <span class="comment">--> true +</span><span class="comment">-- or check metatable +</span><span class="keyword">local</span> my_mt = {} +<span class="keyword">local</span> my_obj = <span class="global">setmetatable</span>(my_obj, my_mt) +utils.is_type(my_obj, my_mt) <span class="comment">--> true</span></pre> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="Tables"></a>Tables</h2> + + <dl class="function"> + <dt> + <a name = "patterns"></a> + <strong>patterns</strong> + </dt> + <dd> + Some standard patterns + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">FLOAT</span> + floating point number + </li> + <li><span class="parameter">INTEGER</span> + integer number + </li> + <li><span class="parameter">IDEN</span> + identifier + </li> + <li><span class="parameter">FILE</span> + file + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "stdmt"></a> + <strong>stdmt</strong> + </dt> + <dd> + Standard meta-tables as used by other Penlight modules + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">List</span> + the List metatable + </li> + <li><span class="parameter">Map</span> + the Map metatable + </li> + <li><span class="parameter">Set</span> + the Set metatable + </li> + <li><span class="parameter">MultiMap</span> + the MultiMap metatable + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Error_handling"></a>Error handling </h2> + + <dl class="function"> + <dt> + <a name = "assert_arg"></a> + <strong>assert_arg (n, val, tp, verify, msg, lev)</strong> + </dt> + <dd> + assert that the given argument is in fact of the correct type. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">n</span> + argument index + </li> + <li><span class="parameter">val</span> + the value + </li> + <li><span class="parameter">tp</span> + the type + </li> + <li><span class="parameter">verify</span> + an optional verification function + </li> + <li><span class="parameter">msg</span> + an optional custom message + </li> + <li><span class="parameter">lev</span> + optional stack position for trace, default 2 + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the validated value + </ol> + + <h3>Raises:</h3> + if <code>val</code> is not the correct type + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> param1 = assert_arg(<span class="number">1</span>,<span class="string">"hello"</span>,<span class="string">'table'</span>) <span class="comment">--> error: argument 1 expected a 'table', got a 'string' +</span><span class="keyword">local</span> param4 = assert_arg(<span class="number">4</span>,<span class="string">'!@#$%^&*'</span>,<span class="string">'string'</span>,path.isdir,<span class="string">'not a directory'</span>) + <span class="comment">--> error: argument 4: '!@#$%^&*' not a directory</span></pre> + </ul> + +</dd> + <dt> + <a name = "function_arg"></a> + <strong>function_arg (idx, f, msg)</strong> + </dt> + <dd> + process a function argument. + This is used throughout Penlight and defines what is meant by a function: + Something that is callable, or an operator string as defined by <code>pl.operator</code>, + such as '>' or '#'. If a function factory has been registered for the type, it will + be called to get the function. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">idx</span> + argument index + </li> + <li><span class="parameter">f</span> + a function, operator string, or callable object + </li> + <li><span class="parameter">msg</span> + optional error message + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a callable + </ol> + + <h3>Raises:</h3> + if idx is not a number or if f is not callable + + + +</dd> + <dt> + <a name = "assert_string"></a> + <strong>assert_string (n, val)</strong> + </dt> + <dd> + assert the common case that the argument is a string. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">n</span> + argument index + </li> + <li><span class="parameter">val</span> + a value that must be a string + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + the validated value + </ol> + + <h3>Raises:</h3> + val must be a string + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> val = <span class="number">42</span> +<span class="keyword">local</span> param2 = utils.assert_string(<span class="number">2</span>, val) <span class="comment">--> error: argument 2 expected a 'string', got a 'number'</span></pre> + </ul> + +</dd> + <dt> + <a name = "on_error"></a> + <strong>on_error (mode)</strong> + </dt> + <dd> + +<p>control the error strategy used by Penlight. + This is a global setting that controls how <a href="../libraries/pl.utils.html#raise">utils.raise</a> behaves:</p> + +<ul> + <li>'default': return <code>nil + error</code> (this is the default)</li> + <li>'error': throw a Lua error</li> + <li>'quit': exit the program</li> +</ul> + + + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">mode</span> + either 'default', 'quit' or 'error' + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.utils.html#raise">utils.raise</a> + </ul> + + +</dd> + <dt> + <a name = "raise"></a> + <strong>raise (err)</strong> + </dt> + <dd> + used by Penlight functions to return errors. Its global behaviour is controlled + by <a href="../libraries/pl.utils.html#on_error">utils.on_error</a>. + To use this function you MUST use it in conjunction with <code>return</code>, since it might + return <code>nil + error</code>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">err</span> + the error string. + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.utils.html#on_error">utils.on_error</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">if</span> some_condition <span class="keyword">then</span> + <span class="keyword">return</span> utils.raise(<span class="string">"some condition was not met"</span>) <span class="comment">-- MUST use 'return'! +</span><span class="keyword">end</span></pre> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="File_handling"></a>File handling </h2> + + <dl class="function"> + <dt> + <a name = "readfile"></a> + <strong>readfile (filename, is_bin)</strong> + </dt> + <dd> + return the contents of a file as a string + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">filename</span> + The file path + </li> + <li><span class="parameter">is_bin</span> + open in binary mode + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + file contents + </ol> + + + + +</dd> + <dt> + <a name = "writefile"></a> + <strong>writefile (filename, str, is_bin)</strong> + </dt> + <dd> + write a string to a file + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">filename</span> + The file path + </li> + <li><span class="parameter">str</span> + The string + </li> + <li><span class="parameter">is_bin</span> + open in binary mode + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + true or nil</li> + <li> + error message</li> + </ol> + + <h3>Raises:</h3> + error if filename or str aren't strings + + + +</dd> + <dt> + <a name = "readlines"></a> + <strong>readlines (filename)</strong> + </dt> + <dd> + return the contents of a file as a list of lines + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">filename</span> + The file path + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + file contents as a table + </ol> + + <h3>Raises:</h3> + error if filename is not a string + + + +</dd> +</dl> + <h2 class="section-header "><a name="OS_functions"></a>OS functions </h2> + + <dl class="function"> + <dt> + <a name = "executeex"></a> + <strong>executeex (cmd, bin)</strong> + </dt> + <dd> + execute a shell command and return the output. + This function redirects the output to tempfiles and returns the content of those files. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">cmd</span> + a shell command + </li> + <li><span class="parameter">bin</span> + boolean, if true, read output as binary file + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + true if successful</li> + <li> + actual return code</li> + <li> + stdout output (string)</li> + <li> + errout output (string)</li> + </ol> + + + + +</dd> + <dt> + <a name = "quote_arg"></a> + <strong>quote_arg (argument)</strong> + </dt> + <dd> + Quote and escape an argument of a command. + Quotes a single (or list of) argument(s) of a command to be passed + to <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.execute">os.execute</a>, <code>pl.utils.execute</code> or <a href="../libraries/pl.utils.html#executeex">pl.utils.executeex</a>. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">argument</span> + (string or table/list) the argument to quote. If a list then + all arguments in the list will be returned as a single string quoted. + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + quoted and escaped argument. + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> options = utils.quote_arg { + <span class="string">"-lluacov"</span>, + <span class="string">"-e"</span>, + <span class="string">"utils = print(require('pl.utils')._VERSION"</span>, +} +<span class="comment">-- returns: -lluacov -e 'utils = print(require('\''pl.utils'\'')._VERSION'</span></pre> + </ul> + +</dd> + <dt> + <a name = "quit"></a> + <strong>quit ([code], msg, ...)</strong> + </dt> + <dd> + error out of this program gracefully. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">code</span> + The exit code, defaults to -<code>1</code> if omitted + (<em>optional</em>) + </li> + <li><span class="parameter">msg</span> + The exit message will be sent to <code>stderr</code> (will be formatted with the extra parameters) + </li> + <li><span class="parameter">...</span> + extra arguments for message's format' + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.utils.html#fprintf">utils.fprintf</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example">utils.quit(-<span class="number">1</span>, <span class="string">"Error '%s' happened"</span>, <span class="string">"42"</span>) +<span class="comment">-- is equivalent to +</span>utils.quit(<span class="string">"Error '%s' happened"</span>, <span class="string">"42"</span>) <span class="comment">--> Error '42' happened</span></pre> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="String_functions"></a>String functions </h2> + + <dl class="function"> + <dt> + <a name = "escape"></a> + <strong>escape (s)</strong> + </dt> + <dd> + escape any Lua 'magic' characters in a string + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + The input string + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "split"></a> + <strong>split (s, re, plain, n)</strong> + </dt> + <dd> + split a string into a list of strings separated by a delimiter. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + The input string + </li> + <li><span class="parameter">re</span> + optional A Lua string pattern; defaults to '%s+' + </li> + <li><span class="parameter">plain</span> + optional If truthy don't use Lua patterns + </li> + <li><span class="parameter">n</span> + optional maximum number of elements (if there are more, the last will remian un-split) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a list-like table + </ol> + + <h3>Raises:</h3> + error if s is not a string + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.utils.html#splitv">splitv</a> + </ul> + + +</dd> + <dt> + <a name = "splitv"></a> + <strong>splitv (s, re, plain, n)</strong> + </dt> + <dd> + split a string into a number of return values. + Identical to <a href="../libraries/pl.utils.html#split">split</a> but returns multiple sub-strings instead of + a single list of sub-strings. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + the string + </li> + <li><span class="parameter">re</span> + A Lua string pattern; defaults to '%s+' + </li> + <li><span class="parameter">plain</span> + don't use Lua patterns + </li> + <li><span class="parameter">n</span> + optional maximum number of splits + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + n values + </ol> + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.utils.html#split">split</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example">first,<span class="global">next</span> = splitv(<span class="string">'user=jane=doe'</span>,<span class="string">'='</span>, <span class="keyword">false</span>, <span class="number">2</span>) +<span class="global">assert</span>(first == <span class="string">"user"</span>) +<span class="global">assert</span>(<span class="global">next</span> == <span class="string">"jane=doe"</span>)</pre> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="Functional"></a>Functional </h2> + + <dl class="function"> + <dt> + <a name = "memoize"></a> + <strong>memoize (func)</strong> + </dt> + <dd> + 'memoize' a function (cache returned value for next call). + This is useful if you have a function which is relatively expensive, + but you don't know in advance what values will be required, so + building a table upfront is wasteful/impossible. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">func</span> + a function of at least one argument + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a function with at least one argument, which is used as the key. + </ol> + + + + +</dd> + <dt> + <a name = "add_function_factory"></a> + <strong>add_function_factory (mt, fun)</strong> + </dt> + <dd> + associate a function factory with a type. + A function factory takes an object of the given type and + returns a function for evaluating it + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">mt</span> + <span class="types"><span class="type">tab</span></span> + metatable + </li> + <li><span class="parameter">fun</span> + <span class="types"><span class="type">func</span></span> + a callable that returns a function + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "string_lambda"></a> + <strong>string_lambda (lf)</strong> + </dt> + <dd> + an anonymous function as a string. This string is either of the form + '|args| expression' or is a function of one argument, '_' + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">lf</span> + function as a string + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a function + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example">string_lambda <span class="string">'|x|x+1'</span> (<span class="number">2</span>) == <span class="number">3</span> +string_lambda <span class="string">'_+1'</span> (<span class="number">2</span>) == <span class="number">3</span></pre> + </ul> + +</dd> + <dt> + <a name = "bind1"></a> + <strong>bind1 (fn, p)</strong> + </dt> + <dd> + bind the first argument of the function to a value. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fn</span> + a function of at least two values (may be an operator string) + </li> + <li><span class="parameter">p</span> + a value + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a function such that f(x) is fn(p,x) + </ol> + + <h3>Raises:</h3> + same as <a href="../libraries/pl.utils.html#function_arg">function_arg</a> + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.func.html#bind1">func.bind1</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> <span class="keyword">function</span> f(msg, name) + <span class="global">print</span>(msg .. <span class="string">" "</span> .. name) +<span class="keyword">end</span> + +<span class="keyword">local</span> hello = utils.bind1(f, <span class="string">"Hello"</span>) + +<span class="global">print</span>(hello(<span class="string">"world"</span>)) <span class="comment">--> "Hello world" +</span><span class="global">print</span>(hello(<span class="string">"sunshine"</span>)) <span class="comment">--> "Hello sunshine"</span></pre> + </ul> + +</dd> + <dt> + <a name = "bind2"></a> + <strong>bind2 (fn, p)</strong> + </dt> + <dd> + bind the second argument of the function to a value. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">fn</span> + a function of at least two values (may be an operator string) + </li> + <li><span class="parameter">p</span> + a value + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a function such that f(x) is fn(x,p) + </ol> + + <h3>Raises:</h3> + same as <a href="../libraries/pl.utils.html#function_arg">function_arg</a> + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> <span class="keyword">function</span> f(a, b, c) + <span class="global">print</span>(a .. <span class="string">" "</span> .. b .. <span class="string">" "</span> .. c) +<span class="keyword">end</span> + +<span class="keyword">local</span> hello = utils.bind1(f, <span class="string">"world"</span>) + +<span class="global">print</span>(hello(<span class="string">"Hello"</span>, <span class="string">"!"</span>)) <span class="comment">--> "Hello world !" +</span><span class="global">print</span>(hello(<span class="string">"Bye"</span>, <span class="string">"?"</span>)) <span class="comment">--> "Bye world ?"</span></pre> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="Deprecation"></a>Deprecation </h2> + + <dl class="function"> + <dt> + <a name = "set_deprecation_func"></a> + <strong>set_deprecation_func (func)</strong> + </dt> + <dd> + Sets a deprecation warning function. + An application can override this function to support proper output of + deprecation warnings. The warnings can be generated from libraries or + functions by calling <a href="../libraries/pl.utils.html#raise_deprecation">utils.raise_deprecation</a>. The default function + will write to the 'warn' system (introduced in Lua 5.4, or the compatibility + function from the <a href="../libraries/pl.compat.html">compat</a> module for earlier versions).</p> + +<p> Note: only applications should set/change this function, libraries should not. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">func</span> + a callback with signature: <code>function(msg, trace)</code> both arguments are strings, the latter being optional. + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.utils.html#raise_deprecation">utils.raise_deprecation</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="comment">-- write to the Nginx logs with OpenResty +</span>utils.set_deprecation_func(<span class="keyword">function</span>(msg, trace) + ngx.log(ngx.WARN, msg, (trace <span class="keyword">and</span> (<span class="string">" "</span> .. trace) <span class="keyword">or</span> <span class="keyword">nil</span>)) +<span class="keyword">end</span>) + +<span class="comment">-- disable deprecation warnings +</span>utils.set_deprecation_func()</pre> + </ul> + +</dd> + <dt> + <a name = "raise_deprecation"></a> + <strong>raise_deprecation (opts)</strong> + </dt> + <dd> + raises a deprecation warning. + For options see the usage example below.</p> + +<p> Note: the <code>opts.deprecated_after</code> field is the last version in which + a feature or option was NOT YET deprecated! Because when writing the code it + is quite often not known in what version the code will land. But the last + released version is usually known. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">opts</span> + options table + </li> + </ul> + + + + <h3>See also:</h3> + <ul> + <a href="../libraries/pl.utils.html#set_deprecation_func">utils.set_deprecation_func</a> + </ul> + + <h3>Usage:</h3> + <ul> + <pre class="example">warn(<span class="string">"@on"</span>) <span class="comment">-- enable Lua warnings, they are usually off by default +</span> +<span class="keyword">function</span> stringx.islower(str) + raise_deprecation { + source = <span class="string">"Penlight "</span> .. utils._VERSION, <span class="comment">-- optional +</span> message = <span class="string">"function 'islower' was renamed to 'is_lower'"</span>, <span class="comment">-- required +</span> version_removed = <span class="string">"2.0.0"</span>, <span class="comment">-- optional +</span> deprecated_after = <span class="string">"1.2.3"</span>, <span class="comment">-- optional +</span> no_trace = <span class="keyword">true</span>, <span class="comment">-- optional +</span> } + <span class="keyword">return</span> stringx.is_lower(str) +<span class="keyword">end</span> +<span class="comment">-- output: "[Penlight 1.9.2] function 'islower' was renamed to 'is_lower' (deprecated after 1.2.3, scheduled for removal in 2.0.0)"</span></pre> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/libraries/pl.xml.html b/Data/Libraries/Penlight/docs/libraries/pl.xml.html new file mode 100644 index 0000000..fb280ab --- /dev/null +++ b/Data/Libraries/Penlight/docs/libraries/pl.xml.html @@ -0,0 +1,835 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><strong>pl.xml</strong></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>pl.xml</code></h1> +<p>XML LOM Utilities.</p> +<p> This implements some useful things on <a href="http://matthewwild.co.uk/projects/luaexpat/lom.html">LOM</a> documents, such as returned by <code>lxp.lom.parse</code>. + In particular, it can convert LOM back into XML text, with optional pretty-printing control. + It is s based on stanza.lua from <a href="http://hg.prosody.im/trunk/file/4621c92d2368/util/stanza.lua">Prosody</a></p> + + +<pre> +> d = xml.parse <span class="string">"<nodes><node id='1'>alice</node></nodes>"</span> +> = d +<nodes><node id=<span class="string">'1'</span>>alice</node></nodes> +> = xml.<span class="global">tostring</span>(d,<span class="string">''</span>,<span class="string">' '</span>) +<nodes> + <node id=<span class="string">'1'</span>>alice</node> +</nodes> +</pre> + +<p> Can be used as a lightweight one-stop-shop for simple XML processing; a simple XML parser is included + but the default is to use <code>lxp.lom</code> if it can be found. + <pre> + Prosody IM + Copyright (C) 2008-2010 Matthew Wild + Copyright (C) 2008-2010 Waqas Hussain-- + classic Lua XML parser by Roberto Ierusalimschy. + modified to output LOM format. + http://lua-users.org/wiki/LuaXml + </pre> + See <a href="../manual/06-data.md.html#XML">the Guide</a></p> + +<p> Dependencies: <a href="../libraries/pl.utils.html#">pl.utils</a></p> + +<p> Soft Dependencies: <code>lxp.lom</code> (fallback is to use basic Lua parser)</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#new">new (tag, attr)</a></td> + <td class="summary">create a new document node.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#parse">parse (text_or_file, is_file, use_basic)</a></td> + <td class="summary">parse an XML document.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:addtag">Doc:addtag (tag, attrs)</a></td> + <td class="summary">convenient function to add a document node, This updates the last inserted position.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:text">Doc:text (text)</a></td> + <td class="summary">convenient function to add a text node.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:up">Doc:up ()</a></td> + <td class="summary">go up one level in a document</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:add_direct_child">Doc:add_direct_child (child)</a></td> + <td class="summary">append a child to a document directly.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:add_child">Doc:add_child (child)</a></td> + <td class="summary">append a child to a document at the last element added</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:set_attribs">Doc:set_attribs (t)</a></td> + <td class="summary">set attributes of a document node.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:set_attrib">Doc:set_attrib (a, v)</a></td> + <td class="summary">set a single attribute of a document node.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:get_attribs">Doc:get_attribs ()</a></td> + <td class="summary">access the attributes of a document node.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#elem">elem (tag, items)</a></td> + <td class="summary">function to create an element with a given tag name and a set of children.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#tags">tags (list)</a></td> + <td class="summary">given a list of names, return a number of element constructors.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc.subst">Doc.subst (templ, data)</a></td> + <td class="summary">create a substituted copy of a document,</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:child_with_name">Doc:child_with_name (tag)</a></td> + <td class="summary">get the first child with a given tag name.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:get_elements_with_name">Doc:get_elements_with_name (tag, dont_recurse)</a></td> + <td class="summary">get all elements in a document that have a given tag.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:childtags">Doc:childtags ()</a></td> + <td class="summary">iterate over all child elements of a document node.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:maptags">Doc:maptags (callback)</a></td> + <td class="summary">visit child element of a node and call a function, possibility modifying the document.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#tostring">tostring (t, idn, indent, attr_indent, xml)</a></td> + <td class="summary">pretty-print an XML document</td> + </tr> + <tr> + <td class="name" nowrap><a href="#Doc:get_text">Doc:get_text ()</a></td> + <td class="summary">get the full text value of an element</td> + </tr> + <tr> + <td class="name" nowrap><a href="#clone">clone (doc, strsubst)</a></td> + <td class="summary">make a copy of a document</td> + </tr> + <tr> + <td class="name" nowrap><a href="#compare">compare (t1, t2)</a></td> + <td class="summary">compare two documents.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#is_tag">is_tag (d)</a></td> + <td class="summary">is this value a document element?</td> + </tr> + <tr> + <td class="name" nowrap><a href="#walk">walk (doc, depth_first, operation)</a></td> + <td class="summary">call the desired function recursively over the document.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#parsehtml">parsehtml (s)</a></td> + <td class="summary">Parse a well-formed HTML file as a string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#basic_parse">basic_parse (s, all_text, html)</a></td> + <td class="summary">Parse a simple XML document using a pure Lua parser based on Robero Ierusalimschy's original version.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "new"></a> + <strong>new (tag, attr)</strong> + </dt> + <dd> + create a new document node. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tag</span> + the tag name + </li> + <li><span class="parameter">attr</span> + optional attributes (table of name-value pairs) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "parse"></a> + <strong>parse (text_or_file, is_file, use_basic)</strong> + </dt> + <dd> + parse an XML document. By default, this uses lxp.lom.parse, but + falls back to basic<em>parse, or if use</em>basic is true + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">text_or_file</span> + file or string representation + </li> + <li><span class="parameter">is_file</span> + whether text<em>or</em>file is a file name or not + </li> + <li><span class="parameter">use_basic</span> + do a basic parse + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + a parsed LOM document with the document metatatables set</li> + <li> + nil, error the error can either be a file error or a parse error</li> + </ol> + + + + +</dd> + <dt> + <a name = "Doc:addtag"></a> + <strong>Doc:addtag (tag, attrs)</strong> + </dt> + <dd> + convenient function to add a document node, This updates the last inserted position. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tag</span> + a tag name + </li> + <li><span class="parameter">attrs</span> + optional set of attributes (name-string pairs) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Doc:text"></a> + <strong>Doc:text (text)</strong> + </dt> + <dd> + convenient function to add a text node. This updates the last inserted position. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">text</span> + a string + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Doc:up"></a> + <strong>Doc:up ()</strong> + </dt> + <dd> + go up one level in a document + + + + + + + +</dd> + <dt> + <a name = "Doc:add_direct_child"></a> + <strong>Doc:add_direct_child (child)</strong> + </dt> + <dd> + append a child to a document directly. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">child</span> + a child node (either text or a document) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Doc:add_child"></a> + <strong>Doc:add_child (child)</strong> + </dt> + <dd> + append a child to a document at the last element added + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">child</span> + a child node (either text or a document) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Doc:set_attribs"></a> + <strong>Doc:set_attribs (t)</strong> + </dt> + <dd> + set attributes of a document node. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + a table containing attribute/value pairs + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Doc:set_attrib"></a> + <strong>Doc:set_attrib (a, v)</strong> + </dt> + <dd> + set a single attribute of a document node. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">a</span> + attribute + </li> + <li><span class="parameter">v</span> + its value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Doc:get_attribs"></a> + <strong>Doc:get_attribs ()</strong> + </dt> + <dd> + access the attributes of a document node. + + + + + + + +</dd> + <dt> + <a name = "elem"></a> + <strong>elem (tag, items)</strong> + </dt> + <dd> + function to create an element with a given tag name and a set of children. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tag</span> + a tag name + </li> + <li><span class="parameter">items</span> + either text or a table where the hash part is the attributes and the list part is the children. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "tags"></a> + <strong>tags (list)</strong> + </dt> + <dd> + given a list of names, return a number of element constructors. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">list</span> + a list of names, or a comma-separated string. + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> parent,children = doc.tags <span class="string">'parent,children'</span> <br> + doc = parent {child <span class="string">'one'</span>, child <span class="string">'two'</span>}</pre> + </ul> + +</dd> + <dt> + <a name = "Doc.subst"></a> + <strong>Doc.subst (templ, data)</strong> + </dt> + <dd> + create a substituted copy of a document, + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">templ</span> + may be a document or a string representation which will be parsed and cached + </li> + <li><span class="parameter">data</span> + a table of name-value pairs or a list of such tables + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + an XML document + </ol> + + + + +</dd> + <dt> + <a name = "Doc:child_with_name"></a> + <strong>Doc:child_with_name (tag)</strong> + </dt> + <dd> + get the first child with a given tag name. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tag</span> + the tag name + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "Doc:get_elements_with_name"></a> + <strong>Doc:get_elements_with_name (tag, dont_recurse)</strong> + </dt> + <dd> + get all elements in a document that have a given tag. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">tag</span> + a tag name + </li> + <li><span class="parameter">dont_recurse</span> + optionally only return the immediate children with this tag name + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a list of elements + </ol> + + + + +</dd> + <dt> + <a name = "Doc:childtags"></a> + <strong>Doc:childtags ()</strong> + </dt> + <dd> + iterate over all child elements of a document node. + + + + + + + +</dd> + <dt> + <a name = "Doc:maptags"></a> + <strong>Doc:maptags (callback)</strong> + </dt> + <dd> + visit child element of a node and call a function, possibility modifying the document. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">callback</span> + a function passed the node (text or element). If it returns nil, that node will be removed. + If it returns a value, that will replace the current node. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "tostring"></a> + <strong>tostring (t, idn, indent, attr_indent, xml)</strong> + </dt> + <dd> + pretty-print an XML document + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t</span> + an XML document + </li> + <li><span class="parameter">idn</span> + an initial indent (indents are all strings) + </li> + <li><span class="parameter">indent</span> + an indent for each level + </li> + <li><span class="parameter">attr_indent</span> + if given, indent each attribute pair and put on a separate line + </li> + <li><span class="parameter">xml</span> + force prefacing with default or custom <?xml...> + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + a string representation + </ol> + + + + +</dd> + <dt> + <a name = "Doc:get_text"></a> + <strong>Doc:get_text ()</strong> + </dt> + <dd> + get the full text value of an element + + + + + + + +</dd> + <dt> + <a name = "clone"></a> + <strong>clone (doc, strsubst)</strong> + </dt> + <dd> + make a copy of a document + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">doc</span> + the original document + </li> + <li><span class="parameter">strsubst</span> + an optional function for handling string copying which could do substitution, etc. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "compare"></a> + <strong>compare (t1, t2)</strong> + </dt> + <dd> + compare two documents. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">t1</span> + any value + </li> + <li><span class="parameter">t2</span> + any value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "is_tag"></a> + <strong>is_tag (d)</strong> + </dt> + <dd> + is this value a document element? + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">d</span> + any value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "walk"></a> + <strong>walk (doc, depth_first, operation)</strong> + </dt> + <dd> + call the desired function recursively over the document. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">doc</span> + the document + </li> + <li><span class="parameter">depth_first</span> + visit child notes first, then the current node + </li> + <li><span class="parameter">operation</span> + a function which will receive the current tag name and current node. + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "parsehtml"></a> + <strong>parsehtml (s)</strong> + </dt> + <dd> + Parse a well-formed HTML file as a string. + Tags are case-insenstive, DOCTYPE is ignored, and empty elements can be .. empty. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + the HTML + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "basic_parse"></a> + <strong>basic_parse (s, all_text, html)</strong> + </dt> + <dd> + Parse a simple XML document using a pure Lua parser based on Robero Ierusalimschy's original version. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">s</span> + the XML document to be parsed. + </li> + <li><span class="parameter">all_text</span> + if true, preserves all whitespace. Otherwise only text containing non-whitespace is included. + </li> + <li><span class="parameter">html</span> + if true, uses relaxed HTML rules for parsing + </li> + </ul> + + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/manual/01-introduction.md.html b/Data/Libraries/Penlight/docs/manual/01-introduction.md.html new file mode 100644 index 0000000..fe42256 --- /dev/null +++ b/Data/Libraries/Penlight/docs/manual/01-introduction.md.html @@ -0,0 +1,843 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Purpose">Purpose </a></li> +<li><a href="#To_Inject_or_not_to_Inject_">To Inject or not to Inject? </a></li> +<li><a href="#What_are_function_arguments_in_Penlight_">What are function arguments in Penlight? </a></li> +<li><a href="#Pros_and_Cons_of_Loopless_Programming">Pros and Cons of Loopless Programming </a></li> +<li><a href="#Generally_useful_functions">Generally useful functions </a></li> +<li><a href="#Application_Support">Application Support </a></li> +<li><a href="#Simplifying_Object_Oriented_Programming_in_Lua">Simplifying Object-Oriented Programming in Lua </a></li> +</ul> + + +<h2>Manual</h2> +<ul class="nowrap"> + <li><strong>Introduction</strong></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + + +<h2>Introduction</h2> + +<p><a name="Purpose"></a></p> +<h3>Purpose</h3> + +<p>It is often said of Lua that it does not include batteries. That is because the +goal of Lua is to produce a lean expressive language that will be used on all +sorts of machines, (some of which don't even have hierarchical filesystems). The +Lua language is the equivalent of an operating system kernel; the creators of Lua +do not see it as their responsibility to create a full software ecosystem around +the language. That is the role of the community.</p> + +<p>A principle of software design is to recognize common patterns and reuse them. If +you find yourself writing things like `io.write(string.format('the answer is %d +',42))` more than a number of times then it becomes useful just to define a +function <code>printf</code>. This is good, not just because repeated code is harder to +maintain, but because such code is easier to read, once people understand your +libraries.</p> + +<p>Penlight captures many such code patterns, so that the intent of your code +becomes clearer. For instance, a Lua idiom to copy a table is <code>{unpack(t)}</code>, but +this will only work for 'small' tables (for a given value of 'small') so it is +not very robust. Also, the intent is not clear. So <a href="../libraries/pl.tablex.html#deepcopy">tablex.deepcopy</a> is provided, +which will also copy nested tables and and associated metatables, so it can be +used to clone complex objects.</p> + +<p>The default error handling policy follows that of the Lua standard libraries: if +a argument is the wrong type, then an error will be thrown, but otherwise we +return <code>nil,message</code> if there is a problem. There are some exceptions; functions +like <a href="../libraries/pl.input.html#fields">input.fields</a> default to shutting down the program immediately with a +useful message. This is more appropriate behaviour for a <em>script</em> than providing +a stack trace. (However, this default can be changed.) The lexer functions always +throw errors, to simplify coding, and so should be wrapped in <a href="https://www.lua.org/manual/5.1/manual.html#pdf-pcall">pcall</a>.</p> + +<p>If you are used to Python conventions, please note that all indices consistently +start at 1.</p> + +<p>The Lua function <a href="https://www.lua.org/manual/5.1/manual.html#pdf-table.foreach">table.foreach</a> has been deprecated in favour of the <code>for in</code> +statement, but such an operation becomes particularly useful with the +higher-order function support in Penlight. Note that <a href="../libraries/pl.tablex.html#foreach">tablex.foreach</a> reverses +the order, so that the function is passed the value and then the key. Although +perverse, this matches the intended use better.</p> + +<p>The only important external dependence of Penlight is +<a href="http://keplerproject.github.com/luafilesystem/manual.html">LuaFileSystem</a> +(<a href="http://stevedonovan.github.io/lua-stdlibs/modules/lfs.html">lfs</a>), and if you want <a href="../libraries/pl.dir.html#copyfile">dir.copyfile</a> to work cleanly on Windows, you will need +either <a href="http://alien.luaforge.net/">alien</a> or be using +<a href="http://luajit.org">LuaJIT</a> as well. (The fallback is to call the equivalent +shell commands.)</p> + +<p><a name="To_Inject_or_not_to_Inject_"></a></p> +<h3>To Inject or not to Inject?</h3> + +<p>It was realized a long time ago that large programs needed a way to keep names +distinct by putting them into tables (Lua), namespaces (C++) or modules +(Python). It is obviously impossible to run a company where everyone is called +'Bruce', except in Monty Python skits. These 'namespace clashes' are more of a +problem in a simple language like Lua than in C++, because C++ does more +complicated lookup over 'injected namespaces'. However, in a small group of +friends, 'Bruce' is usually unique, so in particular situations it's useful to +drop the formality and not use last names. It depends entirely on what kind of +program you are writing, whether it is a ten line script or a ten thousand line +program.</p> + +<p>So the Penlight library provides the formal way and the informal way, without +imposing any preference. You can do it formally like:</p> + + +<pre> +<span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">'pl.utils'</span> +utils.printf(<span class="string">"%s\n"</span>,<span class="string">"hello, world!"</span>) +</pre> + +<p>or informally like:</p> + + +<pre> +<span class="global">require</span> <span class="string">'pl'</span> +utils.printf(<span class="string">"%s\n"</span>,<span class="string">"That feels better"</span>) +</pre> + +<p><code>require 'pl'</code> makes all the separate Penlight modules available, without needing +to require them each individually.</p> + +<p>Generally, the formal way is better when writing modules, since then there are no +global side-effects and the dependencies of your module are made explicit.</p> + +<p>Andrew Starks has contributed another way, which balances nicely between the +formal need to keep the global table uncluttered and the informal need for +convenience. <code>require'pl.import_into'</code> returns a function, which accepts a table +for injecting Penlight into, or if no table is given, it passes back a new one.</p> + + +<pre> +<span class="keyword">local</span> pl = <span class="global">require</span><span class="string">'pl.import_into'</span>() +</pre> + +<p>The table <a href="../libraries/pl.html#">pl</a> is a 'lazy table' which loads modules as needed, so we can then +use <a href="../libraries/pl.utils.html#printf">pl.utils.printf</a> and so forth, without an explicit `require' or harming any +globals.</p> + +<p>If you are using <code>_ENV</code> with Lua 5.2 to define modules, then here is a way to +make Penlight available within a module:</p> + + +<pre> +<span class="keyword">local</span> _ENV,M = <span class="global">require</span> <span class="string">'pl.import_into'</span> () + +<span class="keyword">function</span> answer () + <span class="comment">-- all the Penlight modules are available! +</span> <span class="keyword">return</span> pretty.write(utils.split <span class="string">'10 20 30'</span>, <span class="string">''</span>) +<span class="keyword">end</span> + +<span class="keyword">return</span> M +</pre> + +<p>The default is to put Penlight into <code>\_ENV</code>, which has the unintended effect of +making it available from the module (much as <code>module(...,package.seeall)</code> does). +To satisfy both convenience and safety, you may pass <code>true</code> to this function, and +then the <em>module</em> <code>M</code> is not the same as <code>\_ENV</code>, but only contains the exported +functions.</p> + +<p>Otherwise, Penlight will <em>not</em> bring in functions into the global table, or +clobber standard tables like 'io'. require('pl') will bring tables like +'utils','tablex',etc into the global table <em>if they are used</em>. This +'load-on-demand' strategy ensures that the whole kitchen sink is not loaded up +front, so this method is as efficient as explicitly loading required modules.</p> + +<p>You have an option to bring the <a href="../libraries/pl.stringx.html#">pl.stringx</a> methods into the standard string +table. All strings have a metatable that allows for automatic lookup in <a href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a>, +so we can say <code>s:upper()</code>. Importing <a href="../libraries/pl.stringx.html#">stringx</a> allows for its functions to also +be called as methods: <code>s:strip()</code>,etc:</p> + + +<pre> +<span class="global">require</span> <span class="string">'pl'</span> +stringx.import() +</pre> + +<p>or, more explicitly:</p> + + +<pre> +<span class="global">require</span>(<span class="string">'pl.stringx'</span>).import() +</pre> + +<p>A more delicate operation is importing tables into the local environment. This is +convenient when the context makes the meaning of a name very clear:</p> + + +<pre> +> <span class="global">require</span> <span class="string">'pl'</span> +> utils.import(<span class="global">math</span>) +> = sin(<span class="number">1.2</span>) +<span class="number">0.93203908596723</span> +</pre> + +<p><a href="../libraries/pl.utils.html#import">utils.import</a> can also be passed a module name as a string, which is first +required and then imported. If used in a module, <code>import</code> will bring the symbols +into the module context.</p> + +<p>Keeping the global scope simple is very necessary with dynamic languages. Using +global variables in a big program is always asking for trouble, especially since +you do not have the spell-checking provided by a compiler. The <a href="../libraries/pl.strict.html#">pl.strict</a> +module enforces a simple rule: globals must be 'declared'. This means that they +must be assigned before use; assigning to <code>nil</code> is sufficient.</p> + + +<pre> +> <span class="global">require</span> <span class="string">'pl.strict'</span> +> <span class="global">print</span>(x) +stdin:<span class="number">1</span>: variable <span class="string">'x'</span> is <span class="keyword">not</span> declared +> x = <span class="keyword">nil</span> +> <span class="global">print</span>(x) +<span class="keyword">nil</span> +</pre> + +<p>The <a href="../libraries/pl.strict.html#">strict</a> module provided by Penlight is compatible with the 'load-on-demand' +scheme used by <code>require 'pl</code>.</p> + +<p><a href="../libraries/pl.strict.html#">strict</a> also disallows assignment to global variables, except in the main +program. Generally, modules have no business messing with global scope; if you +must do it, then use a call to <a href="https://www.lua.org/manual/5.1/manual.html#pdf-rawset">rawset</a>. Similarly, if you have to check for the +existence of a global, use <a href="https://www.lua.org/manual/5.1/manual.html#pdf-rawget">rawget</a>.</p> + +<p>If you wish to enforce strictness globally, then just add <code>require 'pl.strict'</code> +at the end of <code>pl/init.lua</code>, otherwise call it from your main program.</p> + +<p>As from 1.1.0, this module provides a <a href="../libraries/pl.strict.html#module">strict.module</a> function which creates (or +modifies) modules so that accessing an unknown function or field causes an error.</p> + +<p>For example,</p> + + +<pre> +<span class="comment">-- mymod.lua +</span><span class="keyword">local</span> strict = <span class="global">require</span> <span class="string">'pl.strict'</span> +<span class="keyword">local</span> M = strict.<span class="global">module</span> (...) + +<span class="keyword">function</span> M.answer () + <span class="keyword">return</span> <span class="number">42</span> +<span class="keyword">end</span> + +<span class="keyword">return</span> M +</pre> + +<p>If you were to accidently type <code>mymod.Answer()</code>, then you would get a runtime +error: "variable 'Answer' is not declared in 'mymod'".</p> + +<p>This can be applied to existing modules. You may desire to have the same level +of checking for the Lua standard libraries:</p> + + +<pre> +strict.make_all_strict(_G) +</pre> + +<p>Thereafter a typo such as <code>math.cosine</code> will give you an explicit error, rather +than merely returning a <code>nil</code> that will cause problems later.</p> + +<p><a name="What_are_function_arguments_in_Penlight_"></a></p> +<h3>What are function arguments in Penlight?</h3> + +<p>Many functions in Penlight themselves take function arguments, like <code>map</code> which +applies a function to a list, element by element. You can use existing +functions, like <a href="https://www.lua.org/manual/5.1/manual.html#pdf-math.max">math.max</a>, anonymous functions (like `function(x,y) return x > y +end<code> ), or operations by name (e.g '*' or '..'). The module </code>pl.operator` exports +all the standard Lua operations, like the Python module of the same name. +Penlight allows these to be referred to by name, so <a href="../libraries/pl.operator.html#gt">operator.gt</a> can be more +concisely expressed as '>'.</p> + +<p>Note that the <code>map</code> functions pass any extra arguments to the function, so we can +have <code>ls:filter('>',0)</code>, which is a shortcut for +<code>ls:filter(function(x) return x > 0 end)</code>.</p> + +<p>Finally, <a href="../libraries/pl.func.html#">pl.func</a> supports <em>placeholder expressions</em> in the Boost lambda style, +so that an anonymous function to multiply the two arguments can be expressed as +<code>\<em>1*\</em>2</code>.</p> + +<p>To use them directly, note that <em>all</em> function arguments in Penlight go through +<a href="../libraries/pl.utils.html#function_arg">utils.function_arg</a>. <a href="../libraries/pl.func.html#">pl.func</a> registers itself with this function, so that you +can directly use placeholder expressions with standard methods:</p> + + +<pre> +> _1 = func._1 +> = List{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>}:map(_1+<span class="number">1</span>) +{<span class="number">11</span>,<span class="number">21</span>,<span class="number">31</span>} +</pre> + +<p>Another option for short anonymous functions is provided by +<a href="../libraries/pl.utils.html#string_lambda">utils.string_lambda</a>; this is invoked automatically:</p> + + +<pre> +> = List{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>}:map <span class="string">'|x| x + 1'</span> +{<span class="number">11</span>,<span class="number">21</span>,<span class="number">31</span>} +</pre> + +<p><a name="Pros_and_Cons_of_Loopless_Programming"></a></p> +<h3>Pros and Cons of Loopless Programming</h3> + +<p>The standard loops-and-ifs 'imperative' style of programming is dominant, and +often seems to be the 'natural' way of telling a machine what to do. It is in +fact very much how the machine does things, but we need to take a step back and +find ways of expressing solutions in a higher-level way. For instance, applying +a function to all elements of a list is a common operation:</p> + + +<pre> +<span class="keyword">local</span> res = {} +<span class="keyword">for</span> i = <span class="number">1</span>,#ls <span class="keyword">do</span> + res[i] = fun(ls[i]) +<span class="keyword">end</span> +</pre> + +<p>This can be efficiently and succintly expressed as <code>ls:map(fun)</code>. Not only is +there less typing but the intention of the code is clearer. If readers of your +code spend too much time trying to guess your intention by analyzing your loops, +then you have failed to express yourself clearly. Similarly, <code>ls:filter('>',0)</code> +will give you all the values in a list greater than zero. (Of course, if you +don't feel like using <a href="../classes/pl.List.html#">List</a>, or have non-list-like tables, then <a href="../libraries/pl.tablex.html#">pl.tablex</a> +offers the same facilities. In fact, the <a href="../classes/pl.List.html#">List</a> methods are implemented using +<a href="../libraries/pl.tablex.html#">tablex</a> functions.)</p> + +<p>A common observation is that loopless programming is less efficient, particularly +in the way it uses memory. <code>ls1:map2('*',ls2):reduce '+'</code> will give you the dot +product of two lists, but an unnecessary temporary list is created. But +efficiency is relative to the actual situation, it may turn out to be <em>fast +enough</em>, or may not appear in any crucial inner loops, etc.</p> + +<p>Writing loops is 'error-prone and tedious', as Stroustrup says. But any +half-decent editor can be taught to do much of that typing for you. The question +should actually be: is it tedious to <em>read</em> loops? As with natural language, +programmers tend to read chunks at a time. A for-loop causes no surprise, and +probably little brain activity. One argument for loopless programming is the +loops that you <em>do</em> write stand out more, and signal 'something different +happening here'. It should not be an all-or-nothing thing, since most programs +require a mixture of idioms that suit the problem. Some languages (like APL) do +nearly everything with map and reduce operations on arrays, and so solutions can +sometimes seem forced. Wisdom is knowing when a particular idiom makes a +particular problem easy to <em>solve</em> and the solution easy to <em>explain</em> afterwards.</p> + +<p><a name="Generally_useful_functions_"></a></p> +<h3>Generally useful functions.</h3> + +<p>The function <code>printf</code> discussed earlier is included in <a href="../libraries/pl.utils.html#">pl.utils</a> because it +makes properly formatted output easier. (There is an equivalent <code>fprintf</code> which +also takes a file object parameter, just like the C function.)</p> + +<p>Splitting a string using a delimiter is a fairly common operation, hence <code>split</code>.</p> + +<p>Utility functions like <code>is_type</code> help with identifying what +kind of animal you are dealing with. +The Lua <a href="https://www.lua.org/manual/5.1/manual.html#pdf-type">type</a> function handles the basic types, but can't distinguish between +different kinds of objects, which are all tables. So <code>is_type</code> handles both +cases, like <code>is_type(s,"string")</code> and <code>is_type(ls,List)</code>.</p> + +<p>A common pattern when working with Lua varargs is capturing all the arguments in +a table:</p> + + +<pre> +<span class="keyword">function</span> t(...) + <span class="keyword">local</span> args = {...} + ... +<span class="keyword">end</span> +</pre> + +<p>But this will bite you someday when <code>nil</code> is one of the arguments, since this +will put a 'hole' in your table. In particular, <code>#ls</code> will only give you the size +upto the <code>nil</code> value. Hence the need for <a href="https://www.lua.org/manual/5.1/manual.html#pdf-table.pack">table.pack</a> - this is a new Lua 5.2 +function which Penlight defines also for Lua 5.1.</p> + + +<pre> +<span class="keyword">function</span> t(...) + <span class="keyword">local</span> args,n = <span class="global">table</span>.pack(...) + <span class="keyword">for</span> i = <span class="number">1</span>,n <span class="keyword">do</span> + ... + <span class="keyword">end</span> +<span class="keyword">end</span> +</pre> + +<p>The 'memoize' pattern occurs when you have a function which is expensive to call, +but will always return the same value subsequently. <a href="../libraries/pl.utils.html#memoize">utils.memoize</a> is given a +function, and returns another function. This calls the function the first time, +saves the value for that argument, and thereafter for that argument returns the +saved value. This is a more flexible alternative to building a table of values +upfront, since in general you won't know what values are needed.</p> + + +<pre> +sum = utils.memoize(<span class="keyword">function</span>(n) + <span class="keyword">local</span> sum = <span class="number">0</span> + <span class="keyword">for</span> i = <span class="number">1</span>,n <span class="keyword">do</span> sum = sum + i <span class="keyword">end</span> + <span class="keyword">return</span> sum +<span class="keyword">end</span>) +... +s = sum(<span class="number">1e8</span>) <span class="comment">--takes time! +</span>... +s = sum(<span class="number">1e8</span>) <span class="comment">--returned saved value!</span> +</pre> + +<p>Penlight is fully compatible with Lua 5.1, 5.2 and LuaJIT 2. To ensure this, +<a href="../libraries/pl.utils.html#">utils</a> also defines the global Lua 5.2 +<a href="http://www.lua.org/work/doc/manual.html#pdf-load">load</a> function as <code>utils.load</code></p> + +<ul> + <li>the input (either a string or a function)</li> + <li>the source name used in debug information</li> + <li>the mode is a string that can have either or both of 'b' or 't', depending on + whether the source is a binary chunk or text code (default is 'bt')</li> + <li>the environment for the compiled chunk</li> +</ul> + +<p>Using <code>utils.load</code> should reduce the need to call the deprecated function <a href="https://www.lua.org/manual/5.1/manual.html#pdf-setfenv">setfenv</a>, +and make your Lua 5.1 code 5.2-friendly.</p> + +<p>The <a href="../libraries/pl.utils.html#">utils</a> module exports <a href="https://www.lua.org/manual/5.1/manual.html#pdf-getfenv">getfenv</a> and <a href="https://www.lua.org/manual/5.1/manual.html#pdf-setfenv">setfenv</a> for +Lua 5.2 as well, based on code by Sergey Rozhenko. Note that these functions can fail +for functions which don't access any globals.</p> + +<p><a name="Application_Support"></a></p> +<h3>Application Support</h3> + +<p><a href="../libraries/pl.app.html#parse_args">app.parse_args</a> is a simple command-line argument parser. If called without any +arguments, it tries to use the global <code>arg</code> array. It returns the <em>flags</em> +(options begining with '-') as a table of name/value pairs, and the <em>arguments</em> +as an array. It knows about long GNU-style flag names, e.g. <code>--value</code>, and +groups of short flags are understood, so that <code>-ab</code> is short for <code>-a -b</code>. The +flags result would then look like <code>{value=true,a=true,b=true}</code>.</p> + +<p>Flags may take values. The command-line <code>--value=open -n10</code> would result in +<code>{value='open',n='10'}</code>; generally you can use '=' or ':' to separate the flag +from its value, except in the special case where a short flag is followed by an +integer. Or you may specify upfront that some flags have associated values, and +then the values will follow the flag.</p> + + +<pre> +> <span class="global">require</span> <span class="string">'pl'</span> +> flags,args = app.parse_args({<span class="string">'-o'</span>,<span class="string">'fred'</span>,<span class="string">'-n10'</span>,<span class="string">'fred.txt'</span>},{o=<span class="keyword">true</span>}) +> pretty.dump(flags) +{o=<span class="string">'fred'</span>,n=<span class="string">'10'</span>} +</pre> + +<p><code>parse_args</code> is not intelligent or psychic; it will not convert any flag values +or arguments for you, or raise errors. For that, have a look at +<a href="../manual/08-additional.md.html#Command_line_Programs_with_Lapp">Lapp</a>.</p> + +<p>An application which consists of several files usually cannot use <a href="https://www.lua.org/manual/5.1/manual.html#pdf-require">require</a> to +load files in the same directory as the main script. <code>app.require_here()</code> +ensures that the Lua module path is modified so that files found locally are +found first. In the <code>examples</code> directory, <a href="../examples/test-symbols.lua.html#">test-symbols.lua</a> uses this function +to ensure that it can find <a href="../examples/symbols.lua.html#">symbols.lua</a> even if it is not run from this directory.</p> + +<p><a href="../libraries/pl.app.html#appfile">app.appfile</a> will create a filename that your application can use to store its +private data, based on the script name. For example, <code>app.appfile "test.txt"</code> +from a script called <code>testapp.lua</code> produces the following file on my Windows +machine:</p> + +<pre><code>C:\Documents and Settings\SJDonova\.testapp\test.txt +</code></pre> + + +<p>and the equivalent on my Linux machine:</p> + +<pre><code>/home/sdonovan/.testapp/test.txt +</code></pre> + + +<p>If <code>.testapp</code> does not exist, it will be created.</p> + +<p>Penlight makes it convenient to save application data in Lua format. You can use +<code>pretty.dump(t,file)</code> to write a Lua table in a human-readable form to a file, +and <code>pretty.read(file.read(file))</code> to generate the table again, using the +<a href="../libraries/pl.pretty.html#">pretty</a> module.</p> + + +<p><a name="Simplifying_Object_Oriented_Programming_in_Lua"></a></p> +<h3>Simplifying Object-Oriented Programming in Lua</h3> + +<p>Lua is similar to JavaScript in that the concept of class is not directly +supported by the language. In fact, Lua has a very general mechanism for +extending the behaviour of tables which makes it straightforward to implement +classes. A table's behaviour is controlled by its metatable. If that metatable +has a <code>\<em>\</em>index</code> function or table, this will handle looking up anything which is +not found in the original table. A class is just a table with an <code>__index</code> key +pointing to itself. Creating an object involves making a table and setting its +metatable to the class; then when handling <code>obj.fun</code>, Lua first looks up <code>fun</code> in +the table <code>obj</code>, and if not found it looks it up in the class. <code>obj:fun(a)</code> is +just short for <code>obj.fun(obj,a)</code>. So with the metatable mechanism and this bit of +syntactic sugar, it is straightforward to implement classic object orientation.</p> + + +<pre> +<span class="comment">-- animal.lua +</span> +class = <span class="global">require</span> <span class="string">'pl.class'</span> + +class.Animal() + +<span class="keyword">function</span> Animal:_init(name) + self.name = name +<span class="keyword">end</span> + +<span class="keyword">function</span> Animal:__tostring() + <span class="keyword">return</span> self.name..<span class="string">': '</span>..self:speak() +<span class="keyword">end</span> + +class.Dog(Animal) + +<span class="keyword">function</span> Dog:speak() + <span class="keyword">return</span> <span class="string">'bark'</span> +<span class="keyword">end</span> + +class.Cat(Animal) + +<span class="keyword">function</span> Cat:_init(name,breed) + self:super(name) <span class="comment">-- must init base! +</span> self.breed = breed +<span class="keyword">end</span> + +<span class="keyword">function</span> Cat:speak() + <span class="keyword">return</span> <span class="string">'meow'</span> +<span class="keyword">end</span> + +class.Lion(Cat) + +<span class="keyword">function</span> Lion:speak() + <span class="keyword">return</span> <span class="string">'roar'</span> +<span class="keyword">end</span> + +fido = Dog(<span class="string">'Fido'</span>) +felix = Cat(<span class="string">'Felix'</span>,<span class="string">'Tabby'</span>) +leo = Lion(<span class="string">'Leo'</span>,<span class="string">'African'</span>) + +$ lua -i animal.lua +> = fido,felix,leo +Fido: bark Felix: meow Leo: roar +> = leo:is_a(Animal) +<span class="keyword">true</span> +> = leo:is_a(Dog) +<span class="keyword">false</span> +> = leo:is_a(Cat) +<span class="keyword">true</span> +</pre> + +<p>All Animal does is define <code>\<em>\</em>tostring</code>, which Lua will use whenever a string +representation is needed of the object. In turn, this relies on <code>speak</code>, which is +not defined. So it's what C++ people would call an abstract base class; the +specific derived classes like Dog define <code>speak</code>. Please note that <em>if</em> derived +classes have their own constructors, they must explicitly call the base +constructor for their base class; this is conveniently available as the <code>super</code> +method.</p> + +<p>Note that (as always) there are multiple ways to implement OOP in Lua; this method +uses the classic 'a class is the __index of its objects' but does 'fat inheritance'; +methods from the base class are copied into the new class. The advantage of this is +that you are not penalized for long inheritance chains, for the price of larger classes, +but generally objects outnumber classes! (If not, something odd is going on with your design.)</p> + +<p>All such objects will have a <code>is_a</code> method, which looks up the inheritance chain +to find a match. Another form is <code>class_of</code>, which can be safely called on all +objects, so instead of <code>leo:is_a(Animal)</code> one can say <code>Animal:class_of(leo)</code>.</p> + +<p>There are two ways to define a class, either <code>class.Name()</code> or <code>Name = class()</code>; +both work identically, except that the first form will always put the class in +the current environment (whether global or module); the second form provides more +flexibility about where to store the class. The first form does <em>name</em> the class +by setting the <code>_name</code> field, which can be useful in identifying the objects of +this type later. This session illustrates the usefulness of having named classes, +if no <code>__tostring</code> method is explicitly defined.</p> + + +<pre> +> class.Fred() +> a = Fred() +> = a +Fred: <span class="number">00459330</span> +> Alice = class() +> b = Alice() +> = b +<span class="global">table</span>: <span class="number">00459</span>AE8 +> Alice._name = <span class="string">'Alice'</span> +> = b +Alice: <span class="number">00459</span>AE8 +</pre> + +<p>So <code>Alice = class(); Alice._name = 'Alice'</code> is exactly the same as <code>class.Alice()</code>.</p> + +<p>This useful notation is borrowed from Hugo Etchegoyen's +<a href="http://lua-users.org/wiki/MultipleInheritanceClasses">classlib</a> which further +extends this concept to allow for multiple inheritance. Notice that the +more convenient form puts the class name in the <em>current environment</em>! That is, +you may use it safely within modules using the old-fashioned <code>module()</code> +or the new <code>_ENV</code> mechanism.</p> + +<p>There is always more than one way of doing things in Lua; some may prefer this +style for creating classes:</p> + + +<pre> +<span class="keyword">local</span> class = <span class="global">require</span> <span class="string">'pl.class'</span> + +class.Named { + _init = <span class="keyword">function</span>(self,name) + self.name = name + <span class="keyword">end</span>; + + __tostring = <span class="keyword">function</span>(self) + <span class="keyword">return</span> <span class="string">'boo '</span>..self.name + <span class="keyword">end</span>; +} + +b = Named <span class="string">'dog'</span> +<span class="global">print</span>(b) +<span class="comment">--> boo dog</span> +</pre> + +<p>Note that you have to explicitly declare <code>self</code> and end each function definition +with a semi-colon or comma, since this is a Lua table. To inherit from a base class, +set the special field <code>_base</code> to the class in this table.</p> + +<p>Penlight provides a number of useful classes; there is <a href="../classes/pl.List.html#">List</a>, which is a Lua +clone of the standard Python list object, and <a href="../classes/pl.Set.html#">Set</a> which represents sets. There +are three kinds of <em>map</em> defined: <a href="../classes/pl.Map.html#">Map</a>, <a href="../classes/pl.MultiMap.html#">MultiMap</a> (where a key may have +multiple values) and <a href="../classes/pl.OrderedMap.html#">OrderedMap</a> (where the order of insertion is remembered.). +There is nothing special about these classes and you may inherit from them.</p> + +<p>A powerful thing about dynamic languages is that you can redefine existing classes +and functions, which is often called 'monkey patching' It's entertaining and convenient, +but ultimately anti-social; you may modify <a href="../classes/pl.List.html#">List</a> but then any other modules using +this <em>shared</em> resource can no longer be sure about its behaviour. (This is why you +must say <code>stringx.import()</code> explicitly if you want the extended string methods - it +would be a bad default.) Lua is particularly open to modification but the +community is not as tolerant of monkey-patching as the Ruby community, say. You may +wish to add some new methods to <a href="../classes/pl.List.html#">List</a>? Cool, but that's what subclassing is for.</p> + + +<pre> +class.Strings(List) + +<span class="keyword">function</span> Strings:my_method() +... +<span class="keyword">end</span> +</pre> + +<p>It's definitely more useful to define exactly how your objects behave +in <em>unknown</em> conditions. All classes have a <code>catch</code> method you can use to set +a handler for unknown lookups; the function you pass looks exactly like the +<code>__index</code> metamethod.</p> + + +<pre> +Strings:catch(<span class="keyword">function</span>(self,name) + <span class="keyword">return</span> <span class="keyword">function</span>() <span class="global">error</span>(<span class="string">"no such method "</span>..name,<span class="number">2</span>) <span class="keyword">end</span> +<span class="keyword">end</span>) +</pre> + +<p>In this case we're just customizing the error message, but +creative things can be done. Consider this code from <code>test-vector.lua</code>:</p> + + +<pre> +Strings:catch(List.default_map_with(<span class="global">string</span>)) + +ls = Strings{<span class="string">'one'</span>,<span class="string">'two'</span>,<span class="string">'three'</span>} +asserteq(ls:upper(),{<span class="string">'ONE'</span>,<span class="string">'TWO'</span>,<span class="string">'THREE'</span>}) +asserteq(ls:sub(<span class="number">1</span>,<span class="number">2</span>),{<span class="string">'on'</span>,<span class="string">'tw'</span>,<span class="string">'th'</span>}) +</pre> + +<p>So we've converted a unknown method invocation into a map using the function of +that name found in <a href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a>. So for a <code>Vector</code> (which is a specialization of <a href="../classes/pl.List.html#">List</a> +for numbers) it makes sense to make <a href="https://www.lua.org/manual/5.1/manual.html#5.6">math</a> the default map so that <code>v:sin()</code> makes +sense.</p> + +<p>Note that <code>map</code> operations return a object of the same type - this is often called +<em>covariance</em>. So <code>ls:upper()</code> itself returns a <code>Strings</code> object.</p> + +<p>This is not <em>always</em> what you want, but objects can always be cast to the desired type. +(<code>cast</code> doesn't create a new object, but returns the object passed.)</p> + + +<pre> +<span class="keyword">local</span> sizes = ls:map <span class="string">'#'</span> +asserteq(sizes, {<span class="number">3</span>,<span class="number">3</span>,<span class="number">5</span>}) +asserteq(utils.<span class="global">type</span>(sizes),<span class="string">'Strings'</span>) +asserteq(sizes:is_a(Strings),<span class="keyword">true</span>) +sizes = Vector:cast(sizes) +asserteq(utils.<span class="global">type</span>(sizes),<span class="string">'Vector'</span>) +asserteq(sizes+<span class="number">1</span>,{<span class="number">4</span>,<span class="number">4</span>,<span class="number">6</span>}) +</pre> + +<p>About <code>utils.type</code>: it can only return a string for a class type if that class does +in fact have a <code>_name</code> field.</p> + + +<p><em>Properties</em> are a useful object-oriented pattern. We wish to control access to a +field, but don't wish to force the user of the class to say <code>obj:get_field()</code> +etc. This excerpt from <code>tests/test-class.lua</code> shows how it is done:</p> + + + +<pre> +<span class="keyword">local</span> MyProps = class(class.properties) +<span class="keyword">local</span> setted_a, got_b + +<span class="keyword">function</span> MyProps:_init () + self._a = <span class="number">1</span> + self._b = <span class="number">2</span> +<span class="keyword">end</span> + +<span class="keyword">function</span> MyProps:set_a (v) + setted_a = <span class="keyword">true</span> + self._a = v +<span class="keyword">end</span> + +<span class="keyword">function</span> MyProps:get_b () + got_b = <span class="keyword">true</span> + <span class="keyword">return</span> self._b +<span class="keyword">end</span> + +<span class="keyword">local</span> mp = MyProps() + +mp.a = <span class="number">10</span> + +asserteq(mp.a,<span class="number">10</span>) +asserteq(mp.b,<span class="number">2</span>) +asserteq(setted_a <span class="keyword">and</span> got_b, <span class="keyword">true</span>) +</pre> + +<p>The convention is that the internal field name is prefixed with an underscore; +when reading <code>mp.a</code>, first a check for an explicit <em>getter</em> <code>get_a</code> and then only +look for <code>_a</code>. Simularly, writing <code>mp.a</code> causes the <em>setter</em> <code>set_a</code> to be used.</p> + +<p>This is cool behaviour, but like much Lua metaprogramming, it is not free. Method +lookup on such objects goes through <code>\<em>\</em>index</code> as before, but now <code>\<em>\</em>index</code> is a +function which has to explicitly look up methods in the class, before doing any +property indexing, which is not going to be as fast as field lookup. If however, +your accessors actually do non-trivial things, then the extra overhead could be +worth it.</p> + +<p>This is not really intended for <em>access control</em> because external code can write +to <code>mp._a</code> directly. It is possible to have this kind of control in Lua, but it +again comes with run-time costs.</p> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/manual/02-arrays.md.html b/Data/Libraries/Penlight/docs/manual/02-arrays.md.html new file mode 100644 index 0000000..28dc6a2 --- /dev/null +++ b/Data/Libraries/Penlight/docs/manual/02-arrays.md.html @@ -0,0 +1,914 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Python_style_Lists">Python-style Lists </a></li> +<li><a href="#Map_and_Set_classes">Map and Set classes </a></li> +<li><a href="#Useful_Operations_on_Tables">Useful Operations on Tables </a></li> +<li><a href="#Operations_on_two_dimensional_tables">Operations on two-dimensional tables </a></li> +</ul> + + +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><strong>Tables and Arrays</strong></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + + +<h2>Tables and Arrays</h2> + +<p><a id="list"/></p> + +<p><a name="Python_style_Lists"></a></p> +<h3>Python-style Lists</h3> + +<p>One of the elegant things about Lua is that tables do the job of both lists and +dicts (as called in Python) or vectors and maps, (as called in C++), and they do +it efficiently. However, if we are dealing with 'tables with numerical indices' +we may as well call them lists and look for operations which particularly make +sense for lists. The Penlight <a href="../classes/pl.List.html#">List</a> class was originally written by Nick Trout +for Lua 5.0, and translated to 5.1 and extended by myself. It seemed that +borrowing from Python was a good idea, and this eventually grew into Penlight.</p> + +<p>Here is an example showing <a href="../classes/pl.List.html#">List</a> in action; it redefines <code>__tostring</code>, so that +it can print itself out more sensibly:</p> + + +<pre> +> List = <span class="global">require</span> <span class="string">'pl.List'</span> <span class="comment">--> automatic with require 'pl' <--- +</span>> l = List() +> l:append(<span class="number">10</span>) +> l:append(<span class="number">20</span>) +> = l +{<span class="number">10</span>,<span class="number">20</span>} +> l:extend {<span class="number">30</span>,<span class="number">40</span>} +{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>} +> l:insert(<span class="number">1</span>,<span class="number">5</span>) +{<span class="number">5</span>,<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>} +> = l:pop() +<span class="number">40</span> +> = l +{<span class="number">5</span>,<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>} +> = l:index(<span class="number">30</span>) +<span class="number">4</span> +> = l:contains(<span class="number">30</span>) +<span class="keyword">true</span> +> = l:reverse() <span class="comment">---> note: doesn't make a copy! +</span>{<span class="number">30</span>,<span class="number">20</span>,<span class="number">10</span>,<span class="number">5</span>} +</pre> + +<p>Although methods like <code>sort</code> and <code>reverse</code> operate in-place and change the list, +they do return the original list. This makes it possible to do <em>method chaining</em>, +like <code>ls = ls:append(10):append(20):reverse():append(1)</code>. But (and this is an +important but) no extra copy is made, so <code>ls</code> does not change identity. <a href="../classes/pl.List.html#">List</a> +objects (like tables) are <em>mutable</em>, unlike strings. If you want a copy of a +list, then <code>List(ls)</code> will do the job, i.e. it acts like a copy constructor. +However, if passed any other table, <a href="../classes/pl.List.html#">List</a> will just set the metatable of the +table and <em>not</em> make a copy.</p> + +<p>A particular feature of Python lists is <em>slicing</em>. This is fully supported in +this version of <a href="../classes/pl.List.html#">List</a>, except we use 1-based indexing. So <a href="../classes/pl.List.html#List:slice">List.slice</a> works +rather like <a href="https://www.lua.org/manual/5.1/manual.html#pdf-string.sub">string.sub</a>:</p> + + +<pre> +> l = List {<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>} +> = l:slice(<span class="number">1</span>,<span class="number">1</span>) <span class="comment">---> note: creates a new list! +</span>{<span class="number">10</span>} +> = l:slice(<span class="number">2</span>,<span class="number">2</span>) +{<span class="number">20</span>} +> = l:slice(<span class="number">2</span>,<span class="number">3</span>) +{<span class="number">20</span>,<span class="number">30</span>} +> = l:slice(<span class="number">2</span>,-<span class="number">2</span>) +{<span class="number">20</span>,<span class="number">30</span>} +> = l:slice_assign(<span class="number">2</span>,<span class="number">2</span>,{<span class="number">21</span>,<span class="number">22</span>,<span class="number">23</span>}) +{<span class="number">10</span>,<span class="number">21</span>,<span class="number">22</span>,<span class="number">23</span>,<span class="number">30</span>,<span class="number">40</span>} +> = l:chop(<span class="number">1</span>,<span class="number">1</span>) +{<span class="number">21</span>,<span class="number">22</span>,<span class="number">23</span>,<span class="number">30</span>,<span class="number">40</span>} +</pre> + +<p>Functions like <code>slice_assign</code> and <code>chop</code> modify the list; the first is equivalent +to Python<code>l[i1:i2] = seq</code> and the second to <code>del l[i1:i2]</code>.</p> + +<p>List objects are ultimately just Lua 'list-like' tables, but they have extra +operations defined on them, such as equality and concatention. For regular +tables, equality is only true if the two tables are <em>identical objects</em>, whereas +two lists are equal if they have the same contents, i.e. that <code>l1[i]==l2[i]</code> for +all elements.</p> + + +<pre> +> l1 = List {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>} +> l2 = List {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>} +> = l1 == l2 +<span class="keyword">true</span> +> = l1..l2 +{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>} +</pre> + +<p>The <a href="../classes/pl.List.html#">List</a> constructor can be passed a function. If so, it's assumed that this is +an iterator function that can be repeatedly called to generate a sequence. One +such function is <a href="https://www.lua.org/manual/5.1/manual.html#pdf-io.lines">io.lines</a>; the following short, intense little script counts +the number of lines in standard input:</p> + + +<pre> +<span class="comment">-- linecount.lua +</span><span class="global">require</span> <span class="string">'pl'</span> +ls = List(<span class="global">io</span>.lines()) +<span class="global">print</span>(#ls) +</pre> + +<p><a href="../classes/pl.List.html#List.iterate">List.iterate</a> captures what <a href="../classes/pl.List.html#">List</a> considers a sequence. In particular, it can +also iterate over all 'characters' in a string:</p> + + +<pre> +> <span class="keyword">for</span> ch <span class="keyword">in</span> List.iterate <span class="string">'help'</span> <span class="keyword">do</span> <span class="global">io</span>.write(ch,<span class="string">' '</span>) <span class="keyword">end</span> +h e l p > +</pre> + +<p>Since the function <code>iterate</code> is used internally by the <a href="../classes/pl.List.html#">List</a> constructor, +strings can be made into lists of character strings very easily.</p> + +<p>There are a number of operations that go beyond the standard Python methods. For +instance, you can <em>partition</em> a list into a table of sublists using a function. +In the simplest form, you use a predicate (a function returning a boolean value) +to partition the list into two lists, one of elements matching and another of +elements not matching. But you can use any function; if we use <a href="https://www.lua.org/manual/5.1/manual.html#pdf-type">type</a> then the +keys will be the standard Lua type names.</p> + + +<pre> +> ls = List{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>} +> ops = <span class="global">require</span> <span class="string">'pl.operator'</span> +> ls:partition(<span class="keyword">function</span>(x) <span class="keyword">return</span> x > <span class="number">2</span> <span class="keyword">end</span>) +{<span class="keyword">false</span>={<span class="number">1</span>,<span class="number">2</span>},<span class="keyword">true</span>={<span class="number">3</span>,<span class="number">4</span>}} +> ls = List{<span class="string">'one'</span>,<span class="global">math</span>.sin,List{<span class="number">1</span>},<span class="number">10</span>,<span class="number">20</span>,List{<span class="number">1</span>,<span class="number">2</span>}} +> ls:partition(<span class="global">type</span>) +{<span class="keyword">function</span>={<span class="keyword">function</span>: <span class="number">00369110</span>},<span class="global">string</span>={one},number={<span class="number">10</span>,<span class="number">20</span>},<span class="global">table</span>={{<span class="number">1</span>},{<span class="number">1</span>,<span class="number">2</span>}}} +</pre> + +<p>This is one <a href="../classes/pl.List.html#">List</a> method which returns a table which is not a <a href="../classes/pl.List.html#">List</a>. Bear in +mind that you can always call a <a href="../classes/pl.List.html#">List</a> method on a plain table argument, so +<code>List.partition(t,type)</code> works as expected. But these functions will only operate +on the array part of the table.</p> + +<p>The 'nominal' type of the returned table is <code>pl.Multimap</code>, which describes a mapping +between keys and multiple values. This does not mean that <code>pl.Multimap</code> is automatically +loaded whenever you use <code>partition</code> (or <a href="../classes/pl.List.html#">List</a> for that matter); this is one of the +standard metatables which are only filled out when the appropriate module is loaded. +This allows tables to be tagged appropriately without causing excessive coupling.</p> + +<p>Stacks occur everywhere in computing. <a href="../classes/pl.List.html#">List</a> supports stack-like operations; +there is already <code>pop</code> (remove and return last value) and <code>append</code> acts like +<code>push</code> (add a value to the end). <code>push</code> is provided as an alias for <code>append</code>, and +the other stack operation (size) is simply the size operator <code>#</code>. Queues can +also be implemented; you use <code>pop</code> to take values out of the queue, and <code>put</code> to +insert a value at the begining.</p> + +<p>You may derive classes from <a href="../classes/pl.List.html#">List</a>, and since the list-returning methods +are covariant, the result of <code>slice</code> etc will return lists of the derived type, +not <a href="../classes/pl.List.html#">List</a>. For instance, consider the specialization of a <a href="../classes/pl.List.html#">List</a> type that contains +numbers in <code>tests/test-list.lua</code>:</p> + + +<pre> +n1 = NA{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>} +n2 = NA{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>} +ns = n1 + <span class="number">2</span>*n2 +asserteq(ns,{<span class="number">12</span>,<span class="number">24</span>,<span class="number">36</span>}) +min,max = ns:slice(<span class="number">1</span>,<span class="number">2</span>):minmax() +asserteq(T(min,max),T(<span class="number">12</span>,<span class="number">24</span>)) +asserteq(n1:normalize():sum(),<span class="number">1</span>,<span class="number">1e-8</span>) +</pre> + +<p><a name="Map_and_Set_classes"></a></p> +<h3>Map and Set classes</h3> + +<p>The <a href="../classes/pl.Map.html#">Map</a> class exposes what Python would call a 'dict' interface, and accesses +the hash part of the table. The name 'Map' is used to emphasize the interface, +not the implementation; it is an object which maps keys onto values; <code>m['alice']</code> +or the equivalent <code>m.alice</code> is the access operation. This class also provides +explicit <code>set</code> and <code>get</code> methods, which are trivial for regular maps but get +interesting when <a href="../classes/pl.Map.html#">Map</a> is subclassed. The other operation is <code>update</code>, which +extends a map by copying the keys and values from another table, perhaps +overwriting existing keys:</p> + + +<pre> +> Map = <span class="global">require</span> <span class="string">'pl.Map'</span> +> m = Map{one=<span class="number">1</span>,two=<span class="number">2</span>} +> m:update {three=<span class="number">3</span>,four=<span class="number">4</span>,two=<span class="number">20</span>} +> = m == M{one=<span class="number">1</span>,two=<span class="number">20</span>,three=<span class="number">3</span>,four=<span class="number">4</span>} +<span class="keyword">true</span> +</pre> + +<p>The method <code>values</code> returns a list of the values, and <code>keys</code> returns a list of +the keys; there is no guarantee of order. <code>getvalues</code> is given a list of keys and +returns a list of values associated with these keys:</p> + + +<pre> +> m = Map{one=<span class="number">1</span>,two=<span class="number">2</span>,three=<span class="number">3</span>} +> = m:getvalues {<span class="string">'one'</span>,<span class="string">'three'</span>} +{<span class="number">1</span>,<span class="number">3</span>} +> = m:getvalues(m:keys()) == m:values() +<span class="keyword">true</span> +</pre> + +<p>When querying the value of a <a href="../classes/pl.Map.html#">Map</a>, it is best to use the <code>get</code> method:</p> + + +<pre> +> <span class="global">print</span>(m:get <span class="string">'one'</span>, m:get <span class="string">'two'</span>) +<span class="number">1</span> <span class="number">2</span> +</pre> + +<p>The reason is that <code>m[key]</code> can be ambiguous; due to the current implementation, +<code>m["get"]</code> will always succeed, because if a value is not present in the map, it +will be looked up in the <a href="../classes/pl.Map.html#">Map</a> metatable, which contains a method <code>get</code>. There is +currently no simple solution to this annoying restriction.</p> + +<p>There are some useful classes which inherit from <a href="../classes/pl.Map.html#">Map</a>. An <a href="../classes/pl.OrderedMap.html#">OrderedMap</a> behaves +like a <a href="../classes/pl.Map.html#">Map</a> but keeps its keys in order if you use its <code>set</code> method to add keys +and values. Like all the 'container' classes in Penlight, it defines an <code>iter</code> +method for iterating over its values; this will return the keys and values in the +order of insertion; the <code>keys</code> and <code>values</code> methods likewise.</p> + +<p>A <a href="../classes/pl.MultiMap.html#">MultiMap</a> allows multiple values to be associated with a given key. So <code>set</code> +(as before) takes a key and a value, but calling it with the same key and a +different value does not overwrite but adds a new value. <code>get</code> (or using <code>[]</code>) +will return a list of values.</p> + +<p>A <a href="../classes/pl.Set.html#">Set</a> can be seen as a special kind of <a href="../classes/pl.Map.html#">Map</a>, where all the values are <code>true</code>, +the keys are the values, and the order is not important. So in this case +<a href="../classes/pl.Set.html#pl.Set:values">Set.values</a> is defined to return a list of the keys. Sets can display +themselves, and the basic operations like <code>union</code> (<code>+</code>) and <code>intersection</code> (<code>*</code>) +are defined.</p> + + +<pre> +> Set = <span class="global">require</span> <span class="string">'pl.Set'</span> +> = Set{<span class="string">'one'</span>,<span class="string">'two'</span>} == Set{<span class="string">'two'</span>,<span class="string">'one'</span>} +<span class="keyword">true</span> +> fruit = Set{<span class="string">'apple'</span>,<span class="string">'banana'</span>,<span class="string">'orange'</span>} +> = fruit[<span class="string">'banana'</span>] +<span class="keyword">true</span> +> = fruit[<span class="string">'hazelnut'</span>] +<span class="keyword">nil</span> +> = fruit:values() +{apple,orange,banana} +> colours = Set{<span class="string">'red'</span>,<span class="string">'orange'</span>,<span class="string">'green'</span>,<span class="string">'blue'</span>} +> = fruit,colours +[apple,orange,banana] [blue,green,orange,red] +> = fruit+colours +[blue,green,apple,red,orange,banana] +> = fruit*colours +[orange] +</pre> + +<p>There are also the functions <a href="../classes/pl.Set.html#pl.Set:difference">Set.difference</a> and <code>Set.symmetric_difference</code>. The +first answers the question 'what fruits are not colours?' and the second 'what +are fruits and colours but not both?'</p> + + +<pre> +> = fruit - colours +[apple,banana] +> = fruit ^ colours +[blue,green,apple,red,banana] +</pre> + +<p>Adding elements to a set is simply <code>fruit['peach'] = true</code> and removing is +<code>fruit['apple'] = nil</code> . To make this simplicity work properly, the <a href="../classes/pl.Set.html#">Set</a> class has no +methods - either you use the operator forms or explicitly use <code>Set.intersect</code> +etc. In this way we avoid the ambiguity that plagues <a href="../classes/pl.Map.html#">Map</a>.</p> + + +<p>(See <a href="../classes/pl.Map.html#">pl.Map</a> and <a href="../classes/pl.Set.html#">pl.Set</a>)</p> + +<p><a name="Useful_Operations_on_Tables"></a></p> +<h3>Useful Operations on Tables</h3> + + +<p>Some notes on terminology: Lua tables are usually <em>list-like</em> (like an array) or +<em>map-like</em> (like an associative array or dict); they can of course have a +list-like and a map-like part. Some of the table operations only make sense for +list-like tables, and some only for map-like tables. (The usual Lua terminology +is the array part and the hash part of the table, which reflects the actual +implementation used; it is more accurate to say that a Lua table is an +associative map which happens to be particularly efficient at acting like an +array.)</p> + +<p>The functions provided in <a href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> provide all the basic manipulations on Lua +tables, but as we saw with the <a href="../classes/pl.List.html#">List</a> class, it is useful to build higher-level +operations on top of those functions. For instance, to copy a table involves this +kind of loop:</p> + + +<pre> +<span class="keyword">local</span> res = {} +<span class="keyword">for</span> k,v <span class="keyword">in</span> <span class="global">pairs</span>(T) <span class="keyword">do</span> + res[k] = v +<span class="keyword">end</span> +<span class="keyword">return</span> res +</pre> + +<p>The <a href="../libraries/pl.tablex.html#">tablex</a> module provides this as <a href="../libraries/pl.tablex.html#copy">copy</a>, which does a <em>shallow</em> copy of a +table. There is also <a href="../libraries/pl.tablex.html#deepcopy">deepcopy</a> which goes further than a simple loop in two +ways; first, it also gives the copy the same metatable as the original (so it can +copy objects like <a href="../classes/pl.List.html#">List</a> above) and any nested tables will also be copied, to +arbitrary depth. There is also <a href="../libraries/pl.tablex.html#icopy">icopy</a> which operates on list-like tables, where +you can set optionally set the start index of the source and destination as well. +It ensures that any left-over elements will be deleted:</p> + + +<pre> +asserteq(icopy({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>},{<span class="number">20</span>,<span class="number">30</span>}),{<span class="number">20</span>,<span class="number">30</span>}) <span class="comment">-- start at 1 +</span>asserteq(icopy({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>},{<span class="number">20</span>,<span class="number">30</span>},<span class="number">2</span>),{<span class="number">1</span>,<span class="number">20</span>,<span class="number">30</span>}) <span class="comment">-- start at 2 +</span>asserteq(icopy({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>},{<span class="number">20</span>,<span class="number">30</span>},<span class="number">2</span>,<span class="number">2</span>),{<span class="number">1</span>,<span class="number">30</span>}) <span class="comment">-- start at 2, copy from 2</span> +</pre> + +<p>(This code from the <a href="../libraries/pl.tablex.html#">tablex</a> test module shows the use of <a href="../libraries/pl.test.html#asserteq">pl.test.asserteq</a>)</p> + +<p>Whereas, <a href="../libraries/pl.tablex.html#move">move</a> overwrites but does not delete the rest of the destination:</p> + + +<pre> +asserteq(move({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>},{<span class="number">20</span>,<span class="number">30</span>}),{<span class="number">20</span>,<span class="number">30</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>}) +asserteq(move({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>},{<span class="number">20</span>,<span class="number">30</span>},<span class="number">2</span>),{<span class="number">1</span>,<span class="number">20</span>,<span class="number">30</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>}) +asserteq(move({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>},{<span class="number">20</span>,<span class="number">30</span>},<span class="number">2</span>,<span class="number">2</span>),{<span class="number">1</span>,<span class="number">30</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>}) +</pre> + +<p>(The difference is somewhat like that between C's <code>strcpy</code> and <code>memmove</code>.)</p> + +<p>To summarize, use <a href="../libraries/pl.tablex.html#copy">copy</a> or <a href="../libraries/pl.tablex.html#deepcopy">deepcopy</a> to make a copy of an arbitrary table. To +copy into a map-like table, use <a href="../libraries/pl.tablex.html#update">update</a>; to copy into a list-like table use +<a href="../libraries/pl.tablex.html#icopy">icopy</a>, and <a href="../libraries/pl.tablex.html#move">move</a> if you are updating a range in the destination.</p> + +<p>To complete this set of operations, there is <a href="../libraries/pl.tablex.html#insertvalues">insertvalues</a> which works like +<a href="https://www.lua.org/manual/5.1/manual.html#pdf-table.insert">table.insert</a> except that one provides a table of values to be inserted, and +<a href="../libraries/pl.tablex.html#removevalues">removevalues</a> which removes a range of values.</p> + + +<pre> +asserteq(insertvalues({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>},<span class="number">2</span>,{<span class="number">20</span>,<span class="number">30</span>}),{<span class="number">1</span>,<span class="number">20</span>,<span class="number">30</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) +asserteq(insertvalues({<span class="number">1</span>,<span class="number">2</span>},{<span class="number">3</span>,<span class="number">4</span>}),{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) +</pre> + +<p>Another example:</p> + + +<pre> +> T = <span class="global">require</span> <span class="string">'pl.tablex'</span> +> t = {<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>} +> = T.removevalues(t,<span class="number">2</span>,<span class="number">3</span>) +{<span class="number">10</span>,<span class="number">40</span>} +> = T.insertvalues(t,<span class="number">2</span>,{<span class="number">20</span>,<span class="number">30</span>}) +{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>} +</pre> + +<p>In a similar spirit to <a href="../libraries/pl.tablex.html#deepcopy">deepcopy</a>, <a href="../libraries/pl.tablex.html#deepcompare">deepcompare</a> will take two tables and return +true only if they have exactly the same values and structure.</p> + + +<pre> +> t1 = {<span class="number">1</span>,{<span class="number">2</span>,<span class="number">3</span>},<span class="number">4</span>} +> t2 = deepcopy(t1) +> = t1 == t2 +<span class="keyword">false</span> +> = deepcompare(t1,t2) +<span class="keyword">true</span> +</pre> + +<p><a href="../libraries/pl.tablex.html#find">find</a> will return the index of a given value in a list-like table. Note that +like <a href="https://www.lua.org/manual/5.1/manual.html#pdf-string.find">string.find</a> you can specify an index to start searching, so that all +instances can be found. There is an optional fourth argument, which makes the +search start at the end and go backwards, so we could define <a href="../libraries/pl.tablex.html#rfind">rfind</a> like so:</p> + + +<pre> +<span class="keyword">function</span> rfind(t,val,istart) + <span class="keyword">return</span> tablex.find(t,val,istart,<span class="keyword">true</span>) +<span class="keyword">end</span> +</pre> + +<p><a href="../libraries/pl.tablex.html#find">find</a> does a linear search, so it can slow down code that depends on it. If +efficiency is required for large tables, consider using an <em>index map</em>. +<a href="../libraries/pl.tablex.html#index_map">index_map</a> will return a table where the keys are the original values of the +list, and the associated values are the indices. (It is almost exactly the +representation needed for a <em>set</em>.)</p> + + +<pre> +> t = {<span class="string">'one'</span>,<span class="string">'two'</span>,<span class="string">'three'</span>} +> = tablex.find(t,<span class="string">'two'</span>) +<span class="number">2</span> +> = tablex.find(t,<span class="string">'four'</span>) +<span class="keyword">nil</span> +> il = tablex.index_map(t) +> = il[<span class="string">'two'</span>] +<span class="number">2</span> +> = il.two +<span class="number">2</span> +</pre> + +<p>A version of <a href="../libraries/pl.tablex.html#index_map">index_map</a> called <a href="../libraries/pl.tablex.html#makeset">makeset</a> is also provided, where the values are +just <code>true</code>. This is useful because two such sets can be compared for equality +using <a href="../libraries/pl.tablex.html#deepcompare">deepcompare</a>:</p> + + +<pre> +> = deepcompare(makeset {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>},makeset {<span class="number">2</span>,<span class="number">1</span>,<span class="number">3</span>}) +<span class="keyword">true</span> +</pre> + +<p>Consider the problem of determining the new employees that have joined in a +period. Assume we have two files of employee names:</p> + + +<pre> +(last-month.txt) +smith,john +brady,maureen +mongale,thabo + +(this-month.txt) +smith,john +smit,johan +brady,maureen +mogale,thabo +van der Merwe,Piet +</pre> + +<p>To find out differences, just make the employee lists into sets, like so:</p> + + +<pre> +<span class="global">require</span> <span class="string">'pl'</span> + +<span class="keyword">function</span> read_employees(file) + <span class="keyword">local</span> ls = List(<span class="global">io</span>.lines(file)) <span class="comment">-- a list of employees +</span> <span class="keyword">return</span> tablex.makeset(ls) +<span class="keyword">end</span> + +last = read_employees <span class="string">'last-month.txt'</span> +this = read_employees <span class="string">'this-month.txt'</span> + +<span class="comment">-- who is in this but not in last? +</span>diff = tablex.difference(this,last) + +<span class="comment">-- in a set, the keys are the values... +</span><span class="keyword">for</span> e <span class="keyword">in</span> <span class="global">pairs</span>(diff) <span class="keyword">do</span> <span class="global">print</span>(e) <span class="keyword">end</span> + +<span class="comment">-- *output* +</span><span class="comment">-- van der Merwe,Piet +</span><span class="comment">-- smit,johan</span> +</pre> + +<p>The <a href="../libraries/pl.tablex.html#difference">difference</a> operation is easy to write and read:</p> + + +<pre> +<span class="keyword">for</span> e <span class="keyword">in</span> <span class="global">pairs</span>(this) <span class="keyword">do</span> + <span class="keyword">if</span> <span class="keyword">not</span> last[e] <span class="keyword">then</span> + <span class="global">print</span>(e) + <span class="keyword">end</span> +<span class="keyword">end</span> +</pre> + +<p>Using <a href="../libraries/pl.tablex.html#difference">difference</a> here is not that it is a tricky thing to code, it is that you +are stating your intentions clearly to other readers of your code. (And naturally +to your future self, in six months time.)</p> + +<p><a href="../libraries/pl.tablex.html#find_if">find_if</a> will search a table using a function. The optional third argument is a +value which will be passed as a second argument to the function. <a href="../libraries/pl.operator.html#">pl.operator</a> +provides the Lua operators conveniently wrapped as functions, so the basic +comparison functions are available:</p> + + +<pre> +> ops = <span class="global">require</span> <span class="string">'pl.operator'</span> +> = tablex.find_if({<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>},ops.gt,<span class="number">20</span>) +<span class="number">3</span> <span class="keyword">true</span> +</pre> + +<p>Note that <a href="../libraries/pl.tablex.html#find_if">find_if</a> will also return the <em>actual value</em> returned by the function, +which of course is usually just <code>true</code> for a boolean function, but any value +which is not <code>nil</code> and not <code>false</code> can be usefully passed back.</p> + +<p><a href="../libraries/pl.tablex.html#deepcompare">deepcompare</a> does a thorough recursive comparison, but otherwise using the +default equality operator. <a href="../libraries/pl.tablex.html#compare">compare</a> allows you to specify exactly what function +to use when comparing two list-like tables, and <a href="../libraries/pl.tablex.html#compare_no_order">compare_no_order</a> is true if +they contain exactly the same elements. Do note that the latter does not need an +explicit comparison function - in this case the implementation is actually to +compare the two sets, as above:</p> + + +<pre> +> = compare_no_order({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>},{<span class="number">2</span>,<span class="number">1</span>,<span class="number">3</span>}) +<span class="keyword">true</span> +> = compare_no_order({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>},{<span class="number">2</span>,<span class="number">1</span>,<span class="number">3</span>},<span class="string">'=='</span>) +<span class="keyword">true</span> +</pre> + +<p>(Note the special string '==' above; instead of saying <code>ops.gt</code> or <code>ops.eq</code> we +can use the strings '>' or '==' respectively.)</p> + +<p><a href="../libraries/pl.tablex.html#sort">sort</a> and <a href="../libraries/pl.tablex.html#sortv">sortv</a> return iterators that will iterate through the +sorted elements of a table. <a href="../libraries/pl.tablex.html#sort">sort</a> iterates by sorted key order, and +<a href="../libraries/pl.tablex.html#sortv">sortv</a> iterates by sorted value order. For example, given a table +with names and ages, it is trivial to iterate over the elements:</p> + + +<pre> +> t = {john=<span class="number">27</span>,jane=<span class="number">31</span>,mary=<span class="number">24</span>} +> <span class="keyword">for</span> name,age <span class="keyword">in</span> tablex.sort(t) <span class="keyword">do</span> <span class="global">print</span>(name,age) <span class="keyword">end</span> +jane <span class="number">31</span> +john <span class="number">27</span> +mary <span class="number">24</span> +> <span class="keyword">for</span> name,age <span class="keyword">in</span> tablex.sortv(t) <span class="keyword">do</span> <span class="global">print</span>(name,age) <span class="keyword">end</span> +mary <span class="number">24</span> +john <span class="number">27</span> +jane <span class="number">31</span> +</pre> + +<p>There are several ways to merge tables in PL. If they are list-like, then see the +operations defined by <a href="../classes/pl.List.html#">pl.List</a>, like concatenation. If they are map-like, then +<a href="../libraries/pl.tablex.html#merge">merge</a> provides two basic operations. If the third arg is false, then the result +only contains the keys that are in common between the two tables, and if true, +then the result contains all the keys of both tables. These are in fact +generalized set union and intersection operations:</p> + + +<pre> +> S1 = {john=<span class="number">27</span>,jane=<span class="number">31</span>,mary=<span class="number">24</span>} +> S2 = {jane=<span class="number">31</span>,jones=<span class="number">50</span>} +> = tablex.merge(S1, S2, <span class="keyword">false</span>) +{jane=<span class="number">31</span>} +> = tablex.merge(S1, S2, <span class="keyword">true</span>) +{mary=<span class="number">24</span>,jane=<span class="number">31</span>,john=<span class="number">27</span>,jones=<span class="number">50</span>} +</pre> + +<p>When working with tables, you will often find yourself writing loops like in the +first example. Loops are second nature to programmers, but they are often not the +most elegant and self-describing way of expressing an operation. Consider the +<a href="../libraries/pl.tablex.html#map">map</a> function, which creates a new table by applying a function to each element +of the original:</p> + + +<pre> +> = map(<span class="global">math</span>.sin, {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) +{ <span class="number">0.84</span>, <span class="number">0.91</span>, <span class="number">0.14</span>, -<span class="number">0.76</span>} +> = map(<span class="keyword">function</span>(x) <span class="keyword">return</span> x*x <span class="keyword">end</span>, {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) +{<span class="number">1</span>,<span class="number">4</span>,<span class="number">9</span>,<span class="number">16</span>} +</pre> + +<p><a href="../libraries/pl.tablex.html#map">map</a> saves you from writing a loop, and the resulting code is often clearer, as +well as being shorter. This is not to say that 'loops are bad' (although you will +hear that from some extremists), just that it's good to capture standard +patterns. Then the loops you do write will stand out and acquire more significance.</p> + +<p><a href="../libraries/pl.tablex.html#pairmap">pairmap</a> is interesting, because the function works with both the key and the +value.</p> + + +<pre> +> t = {fred=<span class="number">10</span>,bonzo=<span class="number">20</span>,alice=<span class="number">4</span>} +> = pairmap(<span class="keyword">function</span>(k,v) <span class="keyword">return</span> v <span class="keyword">end</span>, t) +{<span class="number">4</span>,<span class="number">10</span>,<span class="number">20</span>} +> = pairmap(<span class="keyword">function</span>(k,v) <span class="keyword">return</span> k <span class="keyword">end</span>, t) +{<span class="string">'alice'</span>,<span class="string">'fred'</span>,<span class="string">'bonzo'</span>} +</pre> + +<p>(These are common enough operations that the first is defined as <a href="../libraries/pl.tablex.html#values">values</a> and the +second as <a href="../libraries/pl.tablex.html#keys">keys</a>.) If the function returns two values, then the <em>second</em> value is +considered to be the new key:</p> + + +<pre> +> = pairmap(t,<span class="keyword">function</span>(k,v) <span class="keyword">return</span> v+<span class="number">10</span>, k:upper() <span class="keyword">end</span>) +{BONZO=<span class="number">30</span>,FRED=<span class="number">20</span>,ALICE=<span class="number">14</span>} +</pre> + +<p><a href="../libraries/pl.tablex.html#map2">map2</a> applies a function to two tables:</p> + + +<pre> +> map2(ops.add,{<span class="number">1</span>,<span class="number">2</span>},{<span class="number">10</span>,<span class="number">20</span>}) +{<span class="number">11</span>,<span class="number">22</span>} +> map2(<span class="string">'*'</span>,{<span class="number">1</span>,<span class="number">2</span>},{<span class="number">10</span>,<span class="number">20</span>}) +{<span class="number">10</span>,<span class="number">40</span>} +</pre> + +<p>The various map operations generate tables; <a href="../libraries/pl.tablex.html#reduce">reduce</a> applies a function of two +arguments over a table and returns the result as a scalar:</p> + + +<pre> +> reduce (<span class="string">'+'</span>, {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}) +<span class="number">6</span> +> reduce (<span class="string">'..'</span>, {<span class="string">'one'</span>,<span class="string">'two'</span>,<span class="string">'three'</span>}) +<span class="string">'onetwothree'</span> +</pre> + +<p>Finally, <a href="../libraries/pl.tablex.html#zip">zip</a> sews different tables together:</p> + + +<pre> +> = zip({<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>},{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>}) +{{<span class="number">1</span>,<span class="number">10</span>},{<span class="number">2</span>,<span class="number">20</span>},{<span class="number">3</span>,<span class="number">30</span>}} +</pre> + +<p>Browsing through the documentation, you will find that <a href="../libraries/pl.tablex.html#">tablex</a> and <a href="../classes/pl.List.html#">List</a> share +methods. For instance, <a href="../libraries/pl.tablex.html#imap">tablex.imap</a> and <a href="../classes/pl.List.html#List:map">List.map</a> are basically the same +function; they both operate over the array-part of the table and generate another +table. This can also be expressed as a <em>list comprehension</em> <code>C 'f(x) for x' (t)</code> +which makes the operation more explicit. So why are there different ways to do +the same thing? The main reason is that not all tables are Lists: the expression +<code>ls:map('#')</code> will return a <em>list</em> of the lengths of any elements of <code>ls</code>. A list +is a thin wrapper around a table, provided by the metatable <a href="../classes/pl.List.html#">List</a>. Sometimes you +may wish to work with ordinary Lua tables; the <a href="../classes/pl.List.html#">List</a> interface is not a +compulsory way to use Penlight table operations.</p> + +<p><a name="Operations_on_two_dimensional_tables"></a></p> +<h3>Operations on two-dimensional tables</h3> + + +<p>Two-dimensional tables are of course easy to represent in Lua, for instance +<code>{{1,2},{3,4}}</code> where we store rows as subtables and index like so <code>A[col][row]</code>. +This is the common representation used by matrix libraries like +<a href="http://lua-users.org/wiki/LuaMatrix">LuaMatrix</a>. <a href="../libraries/pl.array2d.html#">pl.array2d</a> does not provide +matrix operations, since that is the job for a specialized library, but rather +provides generalizations of the higher-level operations provided by <a href="../libraries/pl.tablex.html#">pl.tablex</a> +for one-dimensional arrays.</p> + +<p><a href="../libraries/pl.array2d.html#iter">iter</a> is a useful generalization of <a href="https://www.lua.org/manual/5.1/manual.html#pdf-ipairs">ipairs</a>. (The extra parameter determines +whether you want the indices as well.)</p> + + +<pre> +> a = {{<span class="number">1</span>,<span class="number">2</span>},{<span class="number">3</span>,<span class="number">4</span>}} +> <span class="keyword">for</span> i,j,v <span class="keyword">in</span> array2d.iter(a,<span class="keyword">true</span>) <span class="keyword">do</span> <span class="global">print</span>(i,j,v) <span class="keyword">end</span> +<span class="number">1</span> <span class="number">1</span> <span class="number">1</span> +<span class="number">1</span> <span class="number">2</span> <span class="number">2</span> +<span class="number">2</span> <span class="number">1</span> <span class="number">3</span> +<span class="number">2</span> <span class="number">2</span> <span class="number">4</span> +</pre> + +<p>Note that you can always convert an arbitrary 2D array into a 'list of lists' +with <code>List(tablex.map(List,a))</code></p> + +<p><a href="../libraries/pl.array2d.html#map">map</a> will apply a function over all elements (notice that extra arguments can be +provided, so this operation is in effect <code>function(x) return x-1 end</code>)</p> + + +<pre> +> array2d.map(<span class="string">'-'</span>,a,<span class="number">1</span>) +{{<span class="number">0</span>,<span class="number">1</span>},{<span class="number">2</span>,<span class="number">3</span>}} +</pre> + +<p>2D arrays are stored as an array of rows, but columns can be extracted:</p> + + +<pre> +> array2d.column(a,<span class="number">1</span>) +{<span class="number">1</span>,<span class="number">3</span>} +</pre> + +<p>There are three equivalents to <a href="../libraries/pl.tablex.html#reduce">tablex.reduce</a>. You can either reduce along the +rows (which is the most efficient) or reduce along the columns. Either one will +give you a 1D array. And <a href="../libraries/pl.array2d.html#reduce2">reduce2</a> will apply two operations: the first one +reduces the rows, and the second reduces the result.</p> + + +<pre> +> array2d.reduce_rows(<span class="string">'+'</span>,a) +{<span class="number">3</span>,<span class="number">7</span>} +> array2d.reduce_cols(<span class="string">'+'</span>,a) +{<span class="number">4</span>,<span class="number">6</span>} +> <span class="comment">-- same as tablex.reduce('*',array.reduce_rows('+',a)) +</span>> array2d.reduce2(<span class="string">'*'</span>,<span class="string">'+'</span>,a) +<span class="number">21</span> ` +</pre> + +<p><a href="../libraries/pl.tablex.html#map2">tablex.map2</a> applies an operation to two tables, giving another table. +<a href="../libraries/pl.array2d.html#map2">array2d.map2</a> does this for 2D arrays. Note that you have to provide the <em>rank</em> +of the arrays involved, since it's hard to always correctly deduce this from the +data:</p> + + +<pre> +> b = {{<span class="number">10</span>,<span class="number">20</span>},{<span class="number">30</span>,<span class="number">40</span>}} +> a = {{<span class="number">1</span>,<span class="number">2</span>},{<span class="number">3</span>,<span class="number">4</span>}} +> = array2d.map2(<span class="string">'+'</span>,<span class="number">2</span>,<span class="number">2</span>,a,b) <span class="comment">-- two 2D arrays +</span>{{<span class="number">11</span>,<span class="number">22</span>},{<span class="number">33</span>,<span class="number">44</span>}} +> = array2d.map2(<span class="string">'+'</span>,<span class="number">1</span>,<span class="number">2</span>,{<span class="number">10</span>,<span class="number">100</span>},a) <span class="comment">-- 1D, 2D +</span>{{<span class="number">11</span>,<span class="number">102</span>},{<span class="number">13</span>,<span class="number">104</span>}} +> = array2d.map2(<span class="string">'*'</span>,<span class="number">2</span>,<span class="number">1</span>,a,{<span class="number">1</span>,-<span class="number">1</span>}) <span class="comment">-- 2D, 1D +</span>{{<span class="number">1</span>,-<span class="number">2</span>},{<span class="number">3</span>,-<span class="number">4</span>}} +</pre> + +<p>Of course, you are not limited to simple arithmetic. Say we have a 2D array of +strings, and wish to print it out with proper right justification. The first step +is to create all the string lengths by mapping <a href="https://www.lua.org/manual/5.1/manual.html#pdf-string.len">string.len</a> over the array, the +second is to reduce this along the columns using <a href="https://www.lua.org/manual/5.1/manual.html#pdf-math.max">math.max</a> to get maximum column +widths, and last, apply <a href="../libraries/pl.stringx.html#rjust">stringx.rjust</a> with these widths.</p> + + +<pre> +maxlens = reduce_cols(<span class="global">math</span>.max,map(<span class="string">'#'</span>,lines)) +lines = map2(stringx.rjust,<span class="number">2</span>,<span class="number">1</span>,lines,maxlens) +</pre> + +<p>There is <a href="../libraries/pl.array2d.html#product">product</a> which returns the <em>Cartesian product</em> of two 1D arrays. The +result is a 2D array formed from applying the function to all possible pairs from +the two arrays.</p> + + +<pre> +> array2d.product(<span class="string">'{}'</span>,{<span class="number">1</span>,<span class="number">2</span>},{<span class="string">'a'</span>,<span class="string">'b'</span>}) +{{{<span class="number">1</span>,<span class="string">'b'</span>},{<span class="number">2</span>,<span class="string">'a'</span>}},{{<span class="number">1</span>,<span class="string">'a'</span>},{<span class="number">2</span>,<span class="string">'b'</span>}}} +</pre> + +<p>There is a set of operations which work in-place on 2D arrays. You can +<a href="../libraries/pl.array2d.html#swap_rows">swap_rows</a> and <a href="../libraries/pl.array2d.html#swap_cols">swap_cols</a>; the first really is a simple one-liner, but the idea +here is to give the operation a name. <a href="../libraries/pl.array2d.html#remove_row">remove_row</a> and <a href="../libraries/pl.array2d.html#remove_col">remove_col</a> are +generalizations of <a href="https://www.lua.org/manual/5.1/manual.html#pdf-table.remove">table.remove</a>. Likewise, <a href="../libraries/pl.array2d.html#extract_rows">extract_rows</a> and <a href="../libraries/pl.array2d.html#extract_cols">extract_cols</a> +are given arrays of indices and discard anything else. So, for instance, +<code>extract_cols(A,{2,4})</code> will leave just columns 2 and 4 in the array.</p> + +<p><a href="../classes/pl.List.html#List:slice">List.slice</a> is often useful on 1D arrays; <a href="../libraries/pl.array2d.html#slice">slice</a> does the same thing, but is +generally given a start (row,column) and a end (row,column).</p> + + +<pre> +> A = {{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>},{<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>},{<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>}} +> B = slice(A,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>) +> write(B) + <span class="number">1</span> <span class="number">2</span> + <span class="number">4</span> <span class="number">5</span> +> B = slice(A,<span class="number">2</span>,<span class="number">2</span>) +> write(B,<span class="keyword">nil</span>,<span class="string">'%4.1f'</span>) + <span class="number">5.0</span> <span class="number">6.0</span> + <span class="number">8.0</span> <span class="number">9.0</span> +</pre> + +<p>Here <a href="../libraries/pl.array2d.html#write">write</a> is used to print out an array nicely; the second parameter is <code>nil</code>, +which is the default (stdout) but can be any file object and the third parameter +is an optional format (as used in <a href="https://www.lua.org/manual/5.1/manual.html#pdf-string.format">string.format</a>).</p> + +<p><a href="../libraries/pl.array2d.html#parse_range">parse_range</a> will take a spreadsheet range like 'A1:B2' or 'R1C1:R2C2' and +return the range as four numbers, which can be passed to <a href="../libraries/pl.array2d.html#slice">slice</a>. The rule is +that <a href="../libraries/pl.array2d.html#slice">slice</a> will return an array of the appropriate shape depending on the +range; if a range represents a row or a column, the result is 1D, otherwise 2D.</p> + +<p>This applies to <a href="../libraries/pl.array2d.html#iter">iter</a> as well, which can also optionally be given a range:</p> + + + +<pre> +> <span class="keyword">for</span> i,j,v <span class="keyword">in</span> iter(A,<span class="keyword">true</span>,<span class="number">2</span>,<span class="number">2</span>) <span class="keyword">do</span> <span class="global">print</span>(i,j,v) <span class="keyword">end</span> +<span class="number">2</span> <span class="number">2</span> <span class="number">5</span> +<span class="number">2</span> <span class="number">3</span> <span class="number">6</span> +<span class="number">3</span> <span class="number">2</span> <span class="number">8</span> +<span class="number">3</span> <span class="number">3</span> <span class="number">9</span> +</pre> + +<p><a href="../libraries/pl.array2d.html#new">new</a> will construct a new 2D array with the given dimensions. You provide an +initial value for the elements, which is interpreted as a function if it's +callable. With <code>L</code> being <a href="../libraries/pl.utils.html#string_lambda">utils.string_lambda</a> we then have the following way to +make an <em>identity matrix</em>:</p> + + +<pre> +asserteq( + array.new(<span class="number">3</span>,<span class="number">3</span>,L<span class="string">'|i,j| i==j and 1 or 0'</span>), + {{<span class="number">1</span>,<span class="number">0</span>,<span class="number">0</span>},{<span class="number">0</span>,<span class="number">1</span>,<span class="number">0</span>},{<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>}} +) +</pre> + +<p>Please note that most functions in <a href="../libraries/pl.array2d.html#">array2d</a> are <em>covariant</em>, that is, they +return an array of the same type as they receive. In particular, any objects +created with <a href="../libraries/pl.data.html#new">data.new</a> or <code>matrix.new</code> will remain data or matrix objects when +reshaped or sliced, etc. Data objects have the <a href="../libraries/pl.array2d.html#">array2d</a> functions available as +methods.</p> + + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/manual/03-strings.md.html b/Data/Libraries/Penlight/docs/manual/03-strings.md.html new file mode 100644 index 0000000..a629192 --- /dev/null +++ b/Data/Libraries/Penlight/docs/manual/03-strings.md.html @@ -0,0 +1,397 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Extra_String_Methods">Extra String Methods </a></li> +<li><a href="#String_Templates">String Templates </a></li> +<li><a href="#Another_Style_of_Template">Another Style of Template </a></li> +<li><a href="#File_style_I_O_on_Strings">File-style I/O on Strings </a></li> +</ul> + + +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><strong>Strings. Higher-level operations on strings.</strong></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + + +<h2>Strings. Higher-level operations on strings.</h2> + +<p><a name="Extra_String_Methods"></a></p> +<h3>Extra String Methods</h3> + + +<p>These are convenient borrowings from Python, as described in 3.6.1 of the Python +reference, but note that indices in Lua always begin at one. <a href="../libraries/pl.stringx.html#">stringx</a> defines +functions like <a href="../libraries/pl.stringx.html#isalpha">isalpha</a> and <a href="../libraries/pl.stringx.html#isdigit">isdigit</a>, which return <code>true</code> if s is only composed +of letters or digits respectively. <a href="../libraries/pl.stringx.html#startswith">startswith</a> and <a href="../libraries/pl.stringx.html#endswith">endswith</a> are convenient +ways to find substrings. (<a href="../libraries/pl.stringx.html#endswith">endswith</a> works as in Python 2.5, so that `f:endswith +{'.bat','.exe','.cmd'}` will be true for any filename which ends with these +extensions.) There are justify methods and whitespace trimming functions like +<a href="../libraries/pl.stringx.html#strip">strip</a>.</p> + + +<pre> +> stringx.import() +> (<span class="string">'bonzo.dog'</span>):endswith {<span class="string">'.dog'</span>,<span class="string">'.cat'</span>} +<span class="keyword">true</span> +> (<span class="string">'bonzo.txt'</span>):endswith {<span class="string">'.dog'</span>,<span class="string">'.cat'</span>} +<span class="keyword">false</span> +> (<span class="string">'bonzo.cat'</span>):endswith {<span class="string">'.dog'</span>,<span class="string">'.cat'</span>} +<span class="keyword">true</span> +> (<span class="string">' stuff'</span>):ljust(<span class="number">20</span>,<span class="string">'+'</span>) +<span class="string">'++++++++++++++ stuff'</span> +> (<span class="string">' stuff '</span>):lstrip() +<span class="string">'stuff '</span> +> (<span class="string">' stuff '</span>):rstrip() +<span class="string">' stuff'</span> +> (<span class="string">' stuff '</span>):strip() +<span class="string">'stuff'</span> +> <span class="keyword">for</span> s <span class="keyword">in</span> (<span class="string">'one\ntwo\nthree\n'</span>):lines() <span class="keyword">do</span> <span class="global">print</span>(s) <span class="keyword">end</span> +one +two +three +</pre> + +<p>Most of these can be fairly easily implemented using the Lua string library, +which is more general and powerful. But they are convenient operations to have +easily at hand. Note that can be injected into the <a href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> table if you use +<code>stringx.import</code>, but a simple alias like <code>local stringx = require 'pl.stringx'</code> +is preferrable. This is the recommended practice when writing modules for +consumption by other people, since it is bad manners to change the global state +of the rest of the system. Magic may be used for convenience, but there is always +a price.</p> + + +<p><a name="String_Templates"></a></p> +<h3>String Templates</h3> + + +<p>Another borrowing from Python, string templates allow you to substitute values +looked up in a table:</p> + + +<pre> +<span class="keyword">local</span> Template = <span class="global">require</span> (<span class="string">'pl.text'</span>).Template +t = Template(<span class="string">'${here} is the $answer'</span>) +<span class="global">print</span>(t:substitute {here = <span class="string">'Lua'</span>, answer = <span class="string">'best'</span>}) +==> +Lua is the best +</pre> + +<p>'$ variables' can optionally have curly braces; this form is useful if you are +glueing text together to make variables, e.g <code>${prefix}_name_${postfix}</code>. The +<a href="../libraries/pl.text.html#Template:substitute">substitute</a> method will throw an error if a $ variable is not found in the +table, and the <a href="../libraries/pl.text.html#Template:safe_substitute">safe_substitute</a> method will not.</p> + +<p>The Lua implementation has an extra method, <a href="../libraries/pl.text.html#Template:indent_substitute">indent_substitute</a> which is very +useful for inserting blocks of text, because it adjusts indentation. Consider +this example:</p> + + +<pre> +<span class="comment">-- testtemplate.lua +</span><span class="keyword">local</span> Template = <span class="global">require</span> (<span class="string">'pl.text'</span>).Template + +t = Template <span class="string">[[ + for i = 1,#$t do + $body + end +]]</span> + +body = Template <span class="string">[[ +local row = $t[i] +for j = 1,#row do + fun(row[j]) +end +]]</span> + +<span class="global">print</span>(t:indent_substitute {body=body,t=<span class="string">'tbl'</span>}) +</pre> + +<p>And the output is:</p> + + +<pre> +<span class="keyword">for</span> i = <span class="number">1</span>,#tbl <span class="keyword">do</span> + <span class="keyword">local</span> row = tbl[i] + <span class="keyword">for</span> j = <span class="number">1</span>,#row <span class="keyword">do</span> + fun(row[j]) + <span class="keyword">end</span> +<span class="keyword">end</span> +</pre> + +<p><a href="../libraries/pl.text.html#Template:indent_substitute">indent_substitute</a> can substitute templates, and in which case they themselves +will be substituted using the given table. So in this case, <code>$t</code> was substituted +twice.</p> + +<p><a href="../libraries/pl.text.html#">pl.text</a> also has a number of useful functions like <a href="../libraries/pl.text.html#dedent">dedent</a>, which strips all +the initial indentation from a multiline string. As in Python, this is useful for +preprocessing multiline strings if you like indenting them with your code. The +function <a href="../libraries/pl.text.html#wrap">wrap</a> is passed a long string (a <em>paragraph</em>) and returns a list of +lines that fit into a desired line width. As an extension, there is also <a href="../libraries/pl.text.html#indent">indent</a> +for indenting multiline strings.</p> + +<p>New in Penlight with the 0.9 series is <code>text.format_operator</code>. Calling this +enables Python-style string formating using the modulo operator <code>%</code>:</p> + + +<pre> +> text.format_operator() +> = <span class="string">'%s[%d]'</span> % {<span class="string">'dog'</span>,<span class="number">1</span>} +dog[<span class="number">1</span>] +</pre> + +<p>So in its simplest form it saves the typing involved with <a href="https://www.lua.org/manual/5.1/manual.html#pdf-string.format">string.format</a>; it +will also expand <code>$</code> variables using named fields:</p> + + +<pre> +> = <span class="string">'$animal[$num]'</span> % {animal=<span class="string">'dog'</span>,num=<span class="number">1</span>} +dog[<span class="number">1</span>] +</pre> + +<p>As with <code>stringx.import</code> you have to do this explicitly, since all strings share the same +metatable. But in your own scripts you can feel free to do this.</p> + +<p><a name="Another_Style_of_Template"></a></p> +<h3>Another Style of Template</h3> + +<p>A new module is <a href="../libraries/pl.template.html#">template</a>, which is a version of Rici Lake's <a href="http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor">Lua +Preprocessor</a>. This +allows you to mix Lua code with your templates in a straightforward way. There +are only two rules:</p> + +<ul> + <li>Lines begining with <code>#</code> are Lua</li> + <li>Otherwise, anything inside <code>$()</code> is a Lua expression.</li> +</ul> + +<p>So a template generating an HTML list would look like this:</p> + + +<pre> +<ul> +# <span class="keyword">for</span> i,val <span class="keyword">in</span> <span class="global">ipairs</span>(T) <span class="keyword">do</span> +<li>$(i) = $(val:upper())</li> +# <span class="keyword">end</span> +</ul> +</pre> + +<p>Assume the text is inside <code>tmpl</code>, then the template can be expanded using:</p> + + +<pre> +<span class="keyword">local</span> template = <span class="global">require</span> <span class="string">'pl.template'</span> +<span class="keyword">local</span> my_env = { + <span class="global">ipairs</span> = <span class="global">ipairs</span>, + T = {<span class="string">'one'</span>,<span class="string">'two'</span>,<span class="string">'three'</span>} +} +res = template.substitute(tmpl, my_env) +</pre> + +<p>and we get</p> + + +<pre> +<ul> +<li><span class="number">1</span> = ONE</li> +<li><span class="number">2</span> = TWO</li> +<li><span class="number">3</span> = THREE</li> +</ul> +</pre> + +<p>There is a single function, <a href="../libraries/pl.template.html#substitute">template.substitute</a> which is passed a template +string and an environment table. This table may contain some special fields, +like <code>\_parent</code> which can be set to a table representing a 'fallback' environment +in case a symbol was not found. <code>\_brackets</code> is usually '()' and <code>\_escape</code> is +usually '#' but it's sometimes necessary to redefine these if the defaults +interfere with the target language - for instance, <code>$(V)</code> has another meaning in +Make, and <code>#</code> means a preprocessor line in C/C++.</p> + +<p>Finally, if something goes wrong, passing <code>_debug</code> will cause the intermediate +Lua code to be dumped if there's a problem.</p> + +<p>Here is a C code generation example; something that could easily be extended to +be a minimal Lua extension skeleton generator.</p> + + +<pre> +<span class="keyword">local</span> subst = <span class="global">require</span> <span class="string">'pl.template'</span>.substitute + +<span class="keyword">local</span> templ = <span class="string">[[ +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> + +> for _,f in ipairs(mod) do +static int l_$(f.name) (lua_State *L) { + +} +> end + +static const luaL_reg $(mod.name)[] = { +> for _,f in ipairs(mod) do + {"$(f.name)",l_$(f.name)}, +> end + {NULL,NULL} +}; + +int luaopen_$(mod.name) { + luaL_register (L, "$(mod.name)", $(mod.name)); + return 1; +} +]]</span> + +<span class="global">print</span>(subst(templ,{ + _escape = <span class="string">'>'</span>, + <span class="global">ipairs</span> = <span class="global">ipairs</span>, + mod = { + name = <span class="string">'baggins'</span>; + {name=<span class="string">'frodo'</span>}, + {name=<span class="string">'bilbo'</span>} + } +})) +</pre> + +<p><a name="File_style_I_O_on_Strings"></a></p> +<h3>File-style I/O on Strings</h3> + +<p><a href="../libraries/pl.stringio.html#">pl.stringio</a> provides just three functions; <a href="../libraries/pl.stringio.html#open">stringio.open</a> is passed a string, +and returns a file-like object for reading. It supports a <code>read</code> method, which +takes the same arguments as standard file objects:</p> + + +<pre> +> f = stringio.open <span class="string">'first line\n10 20 30\n'</span> +> = f:read() +first line +> = f:read(<span class="string">'*n'</span>,<span class="string">'*n'</span>,<span class="string">'*n'</span>) +<span class="number">10</span> <span class="number">20</span> <span class="number">30</span> +</pre> + +<p><code>lines</code> and <code>seek</code> are also supported.</p> + +<p><code>stringio.lines</code> is a useful short-cut for iterating over all the lines in a +string.</p> + +<p><a href="../libraries/pl.stringio.html#create">stringio.create</a> creates a writeable file-like object. You then use <code>write</code> to +this stream, and finally extract the builded string using <code>value</code>. This 'string +builder' pattern is useful for efficiently creating large strings.</p> + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/manual/04-paths.md.html b/Data/Libraries/Penlight/docs/manual/04-paths.md.html new file mode 100644 index 0000000..070a3ea --- /dev/null +++ b/Data/Libraries/Penlight/docs/manual/04-paths.md.html @@ -0,0 +1,329 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Working_with_Paths">Working with Paths </a></li> +<li><a href="#File_Operations">File Operations </a></li> +<li><a href="#Directory_Operations">Directory Operations </a></li> +</ul> + + +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><strong>Paths and Directories</strong></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + + +<h2>Paths and Directories</h2> + +<p><a name="Working_with_Paths"></a></p> +<h3>Working with Paths</h3> + +<p>Programs should not depend on quirks of your operating system. They will be +harder to read, and need to be ported for other systems. The worst of course is +hardcoding paths like 'c:\' in programs, and wondering why Vista complains so +much. But even something like <code>dir..'\'..file</code> is a problem, since Unix can't +understand backslashes in this way. <code>dir..'/'..file</code> is <em>usually</em> portable, but +it's best to put this all into a simple function, <a href="../libraries/pl.path.html#join">path.join</a>. If you +consistently use <a href="../libraries/pl.path.html#join">path.join</a>, then it's much easier to write cross-platform code, +since it handles the directory separator for you.</p> + +<p><a href="../libraries/pl.path.html#">pl.path</a> provides the same functionality as Python's <code>os.path</code> module (11.1).</p> + + +<pre> +> p = <span class="string">'c:\\bonzo\\DOG.txt'</span> +> = path.normcase (p) <span class="comment">---> only makes sense on Windows +</span>c:\bonzo\dog.txt +> = path.splitext (p) +c:\bonzo\DOG .txt +> = path.extension (p) +.txt +> = path.basename (p) +DOG.txt +> = path.exists(p) +<span class="keyword">false</span> +> = path.join (<span class="string">'fred'</span>,<span class="string">'alice.txt'</span>) +fred\alice.txt +> = path.exists <span class="string">'pretty.lua'</span> +<span class="keyword">true</span> +> = path.getsize <span class="string">'pretty.lua'</span> +<span class="number">2125</span> +> = path.isfile <span class="string">'pretty.lua'</span> +<span class="keyword">true</span> +> = path.isdir <span class="string">'pretty.lua'</span> +<span class="keyword">false</span> +</pre> + +<p>It is very important for all programmers, not just on Unix, to only write to +where they are allowed to write. <a href="../libraries/pl.path.html#expanduser">path.expanduser</a> will expand '~' (tilde) into +the home directory. Depending on your OS, this will be a guaranteed place where +you can create files:</p> + + +<pre> +> = path.expanduser <span class="string">'~/mydata.txt'</span> +<span class="string">'C:\Documents and Settings\SJDonova/mydata.txt'</span> + +> = path.expanduser <span class="string">'~/mydata.txt'</span> +/home/sdonovan/mydata.txt +</pre> + +<p>Under Windows, <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.tmpname">os.tmpname</a> returns a path which leads to your drive root full of +temporary files. (And increasingly, you do not have access to this root folder.) +This is corrected by <a href="../libraries/pl.path.html#tmpname">path.tmpname</a>, which uses the environment variable TMP:</p> + + +<pre> +> <span class="global">os</span>.tmpname() <span class="comment">-- not a good place to put temporary files! +</span><span class="string">'\s25g.'</span> +> path.tmpname() +<span class="string">'C:\DOCUME~1\SJDonova\LOCALS~1\Temp\s25g.1'</span> +</pre> + +<p>A useful extra function is <a href="../libraries/pl.path.html#package_path">pl.path.package_path</a>, which will tell you the path +of a particular Lua module. So on my system, <code>package_path('pl.path')</code> returns +'C:\Program Files\Lua\5.1\lualibs\pl\path.lua', and <code>package_path('ifs')</code> returns +'C:\Program Files\Lua\5.1\clibs\lfs.dll'. It is implemented in terms of +<a href="https://www.lua.org/manual/5.1/manual.html#pdf-package.searchpath">package.searchpath</a>, which is a new function in Lua 5.2 which has been +implemented for Lua 5.1 in Penlight.</p> + +<p><a name="File_Operations"></a></p> +<h3>File Operations</h3> + +<p><a href="../libraries/pl.file.html#">pl.file</a> is a new module that provides more sensible names for common file +operations. For instance, <a href="https://www.lua.org/manual/5.1/manual.html#pdf-file:read">file.read</a> and <a href="https://www.lua.org/manual/5.1/manual.html#pdf-file:write">file.write</a> are aliases for +<a href="../libraries/pl.utils.html#readfile">utils.readfile</a> and <a href="../libraries/pl.utils.html#writefile">utils.writefile</a>.</p> + +<p>Smaller files can be efficiently read and written in one operation. <a href="https://www.lua.org/manual/5.1/manual.html#pdf-file:read">file.read</a> +is passed a filename and returns the contents as a string, if successful; if not, +then it returns <code>nil</code> and the actual error message. There is an optional boolean +parameter if you want the file to be read in binary mode (this makes no +difference on Unix but remains important with Windows.)</p> + +<p>In previous versions of Penlight, <a href="../libraries/pl.utils.html#readfile">utils.readfile</a> would read standard input if +the file was not specified, but this can lead to nasty bugs; use <code>io.read '*a'</code> +to grab all of standard input.</p> + +<p>Similarly, <a href="https://www.lua.org/manual/5.1/manual.html#pdf-file:write">file.write</a> takes a filename and a string which will be written to +that file.</p> + +<p>For example, this little script converts a file into upper case:</p> + + +<pre> +<span class="global">require</span> <span class="string">'pl'</span> +<span class="global">assert</span>(#arg == <span class="number">2</span>, <span class="string">'supply two filenames'</span>) +text = <span class="global">assert</span>(file.read(arg[<span class="number">1</span>])) +<span class="global">assert</span>(file.write(arg[<span class="number">2</span>],text:upper())) +</pre> + +<p>Copying files is suprisingly tricky. <a href="../libraries/pl.file.html#copy">file.copy</a> and <a href="../libraries/pl.file.html#move">file.move</a> attempt to use +the best implementation possible. On Windows, they link to the API functions +<code>CopyFile</code> and <code>MoveFile</code>, but only if the <code>alien</code> package is installed (this is +true for Lua for Windows.) Otherwise, the system copy command is used. This can +be ugly when writing Windows GUI applications, because of the dreaded flashing +black-box problem with launching processes.</p> + +<p><a name="Directory_Operations"></a></p> +<h3>Directory Operations</h3> + +<p><a href="../libraries/pl.dir.html#">pl.dir</a> provides some useful functions for working with directories. <code>fnmatch</code> +will match a filename against a shell pattern, and <code>filter</code> will return any files +in the supplied list which match the given pattern, which correspond to the +functions in the Python <code>fnmatch</code> module. <code>getdirectories</code> will return all +directories contained in a directory, and <code>getfiles</code> will return all files in a +directory which match a shell pattern. These functions return the files as a +table, unlike <a href="http://stevedonovan.github.io/lua-stdlibs/modules/lfs.html#dir">lfs.dir</a> which returns an iterator.)</p> + +<p><a href="../libraries/pl.dir.html#makepath">dir.makepath</a> can create a full path, creating subdirectories as necessary; +<code>rmtree</code> is the Nuclear Option of file deleting functions, since it will +recursively clear out and delete all directories found begining at a path (there +is a similar function with this name in the Python <code>shutils</code> module.)</p> + + +<pre> +> = dir.makepath <span class="string">'t\\temp\\bonzo'</span> +> = path.isdir <span class="string">'t\\temp\\bonzo'</span> +<span class="keyword">true</span> +> = dir.rmtree <span class="string">'t'</span> +</pre> + +<p><a href="../libraries/pl.dir.html#rmtree">dir.rmtree</a> depends on <a href="../libraries/pl.dir.html#walk">dir.walk</a>, which is a powerful tool for scanning a whole +directory tree. Here is the implementation of <a href="../libraries/pl.dir.html#rmtree">dir.rmtree</a>:</p> + + +<pre> +<span class="comment">--- remove a whole directory tree. +</span><span class="comment">-- @param path A directory path +</span><span class="keyword">function</span> dir.rmtree(fullpath) + <span class="keyword">for</span> root,dirs,files <span class="keyword">in</span> dir.walk(fullpath) <span class="keyword">do</span> + <span class="keyword">for</span> i,f <span class="keyword">in</span> <span class="global">ipairs</span>(files) <span class="keyword">do</span> + <span class="global">os</span>.remove(path.join(root,f)) + <span class="keyword">end</span> + lfs.rmdir(root) + <span class="keyword">end</span> +<span class="keyword">end</span> +</pre> + +<p><a href="../libraries/pl.dir.html#clonetree">dir.clonetree</a> clones directory trees. The first argument is a path that must +exist, and the second path is the path to be cloned. (Note that this path cannot +be <em>inside</em> the first path, since this leads to madness.) By default, it will +then just recreate the directory structure. You can in addition provide a +function, which will be applied for all files found.</p> + + +<pre> +<span class="comment">-- make a copy of my libs folder +</span><span class="global">require</span> <span class="string">'pl'</span> +p1 = <span class="string">[[d:\dev\lua\libs]]</span> +p2 = <span class="string">[[D:\dev\lua\libs\..\tests]]</span> +dir.clonetree(p1,p2,dir.copyfile) +</pre> + +<p>A more sophisticated version, which only copies files which have been modified:</p> + + +<pre> +<span class="comment">-- p1 and p2 as before, or from arg[1] and arg[2] +</span>dir.clonetree(p1,p2,<span class="keyword">function</span>(f1,f2) + <span class="keyword">local</span> res + <span class="keyword">local</span> t1,t2 = path.getmtime(f1),path.getmtime(f2) + <span class="comment">-- f2 might not exist, so be careful about t2 +</span> <span class="keyword">if</span> <span class="keyword">not</span> t2 <span class="keyword">or</span> t1 > t2 <span class="keyword">then</span> + res = dir.copyfile(f1,f2) + <span class="keyword">end</span> + <span class="keyword">return</span> res <span class="comment">-- indicates successful operation +</span><span class="keyword">end</span>) +</pre> + +<p><a href="../libraries/pl.dir.html#clonetree">dir.clonetree</a> uses <a href="../libraries/pl.path.html#common_prefix">path.common_prefix</a>. With <code>p1</code> and <code>p2</code> defined above, the +common path is 'd:\dev\lua'. So 'd:\dev\lua\libs\testfunc.lua' is copied to +'d:\dev\lua\test\testfunc.lua', etc.</p> + +<p>If you need to find the common path of list of files, then <a href="../libraries/pl.tablex.html#reduce">tablex.reduce</a> will +do the job:</p> + + +<pre> +> p3 = <span class="string">[[d:\dev]]</span> +> = tablex.reduce(path.common_prefix,{p1,p2,p3}) +<span class="string">'d:\dev'</span> +</pre> + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/manual/05-dates.md.html b/Data/Libraries/Penlight/docs/manual/05-dates.md.html new file mode 100644 index 0000000..c04b036 --- /dev/null +++ b/Data/Libraries/Penlight/docs/manual/05-dates.md.html @@ -0,0 +1,269 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Creating_and_Displaying_Dates">Creating and Displaying Dates </a></li> +</ul> + + +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><strong>Date and Time</strong></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + + +<h2>Date and Time</h2> + +<p><a id="date"></a></p> + +<p>NOTE: the Date module is deprecated</p> + +<p><a name="Creating_and_Displaying_Dates"></a></p> +<h3>Creating and Displaying Dates</h3> + +<p>The <a href="../classes/pl.Date.html#">Date</a> class provides a simplified way to work with <a href="http://www.lua.org/pil/22.1.html">date and +time</a> in Lua; it leans heavily on the functions +<a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.date">os.date</a> and <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.time">os.time</a>.</p> + +<p>A <a href="../classes/pl.Date.html#">Date</a> object can be constructed from a table, just like with <a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.time">os.time</a>. +Methods are provided to get and set the various parts of the date.</p> + + +<pre> +> d = Date {year = <span class="number">2011</span>, month = <span class="number">3</span>, day = <span class="number">2</span> } +> = d +<span class="number">2011</span>-<span class="number">03</span>-<span class="number">02</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +> = d:month(),d:year(),d:day() +<span class="number">3</span> <span class="number">2011</span> <span class="number">2</span> +> d:month(<span class="number">4</span>) +> = d +<span class="number">2011</span>-<span class="number">04</span>-<span class="number">02</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +> d:add {day=<span class="number">1</span>} +> = d +<span class="number">2011</span>-<span class="number">04</span>-<span class="number">03</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +</pre> + +<p><code>add</code> takes a table containing one of the date table fields.</p> + + +<pre> +> = d:weekday_name() +Sun +> = d:last_day() +<span class="number">2011</span>-<span class="number">04</span>-<span class="number">30</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +> = d:month_name(<span class="keyword">true</span>) +April +</pre> + +<p>There is a default conversion to text for date objects, but <a href="../classes/pl.Date.html#Date.Format">Date.Format</a> gives +you full control of the format for both parsing and displaying dates:</p> + + +<pre> +> iso = Date.Format <span class="string">'yyyy-mm-dd'</span> +> d = iso:parse <span class="string">'2010-04-10'</span> +> amer = Date.Format <span class="string">'mm/dd/yyyy'</span> +> = amer:<span class="global">tostring</span>(d) +<span class="number">04</span>/<span class="number">10</span>/<span class="number">2010</span> +</pre> + +<p>With the 0.9.7 relase, the <a href="../classes/pl.Date.html#">Date</a> constructor has become more flexible. You may +omit any of the 'year', 'month' or 'day' fields:</p> + + +<pre> +> = Date { year = <span class="number">2008</span> } +<span class="number">2008</span>-<span class="number">01</span>-<span class="number">01</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +> = Date { month = <span class="number">3</span> } +<span class="number">2011</span>-<span class="number">03</span>-<span class="number">01</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +> = Date { day = <span class="number">20</span> } +<span class="number">2011</span>-<span class="number">10</span>-<span class="number">20</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +> = Date { hour = <span class="number">14</span>, min = <span class="number">30</span> } +<span class="number">2011</span>-<span class="number">10</span>-<span class="number">13</span> <span class="number">14</span>:<span class="number">30</span>:<span class="number">00</span> +</pre> + +<p>If 'year' is omitted, then the current year is assumed, and likewise for 'month'.</p> + +<p>To set the time on such a partial date, you can use the fact that the 'setter' +methods return the date object and so you can 'chain' these methods.</p> + + +<pre> +> d = Date { day = <span class="number">03</span> } +> = d:hour(<span class="number">18</span>):min(<span class="number">30</span>) +<span class="number">2011</span>-<span class="number">10</span>-<span class="number">03</span> <span class="number">18</span>:<span class="number">30</span>:<span class="number">00</span> +</pre> + +<p>Finally, <a href="../classes/pl.Date.html#">Date</a> also now accepts positional arguments:</p> + + +<pre> +> = Date(<span class="number">2011</span>,<span class="number">10</span>,<span class="number">3</span>) +<span class="number">2011</span>-<span class="number">10</span>-<span class="number">03</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +> = Date(<span class="number">2011</span>,<span class="number">10</span>,<span class="number">3</span>,<span class="number">18</span>,<span class="number">30</span>,<span class="number">23</span>) +<span class="number">2011</span>-<span class="number">10</span>-<span class="number">03</span> <span class="number">18</span>:<span class="number">30</span>:<span class="number">23</span> +</pre> + +<p><code>Date.format</code> has been extended. If you construct an instance without a pattern, +then it will try to match against a set of known formats. This is useful for +human-input dates since keeping to a strict format is not one of the strong +points of users. It assumes that there will be a date, and then a date.</p> + + +<pre> +> df = Date.Format() +> = df:parse <span class="string">'5.30pm'</span> +<span class="number">2011</span>-<span class="number">10</span>-<span class="number">13</span> <span class="number">17</span>:<span class="number">30</span>:<span class="number">00</span> +> = df:parse <span class="string">'1730'</span> +<span class="keyword">nil</span> day out of range: <span class="number">1730</span> is <span class="keyword">not</span> between <span class="number">1</span> <span class="keyword">and</span> <span class="number">31</span> +> = df:parse <span class="string">'17.30'</span> +<span class="number">2011</span>-<span class="number">10</span>-<span class="number">13</span> <span class="number">17</span>:<span class="number">30</span>:<span class="number">00</span> +> = df:parse <span class="string">'mar'</span> +<span class="number">2011</span>-<span class="number">03</span>-<span class="number">01</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +> = df:parse <span class="string">'3 March'</span> +<span class="number">2011</span>-<span class="number">03</span>-<span class="number">03</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +> = df:parse <span class="string">'15 March'</span> +<span class="number">2011</span>-<span class="number">03</span>-<span class="number">15</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +> = df:parse <span class="string">'15 March 2008'</span> +<span class="number">2008</span>-<span class="number">03</span>-<span class="number">15</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +> = df:parse <span class="string">'15 March 2008 1.30pm'</span> +<span class="number">2008</span>-<span class="number">03</span>-<span class="number">15</span> <span class="number">13</span>:<span class="number">30</span>:<span class="number">00</span> +> = df:parse <span class="string">'2008-10-03 15:30:23'</span> +<span class="number">2008</span>-<span class="number">10</span>-<span class="number">03</span> <span class="number">15</span>:<span class="number">30</span>:<span class="number">23</span> +</pre> + +<p>ISO date format is of course a good idea if you need to deal with users from +different countries. Here is the default behaviour for 'short' dates:</p> + + +<pre> +> = df:parse <span class="string">'24/02/12'</span> +<span class="number">2012</span>-<span class="number">02</span>-<span class="number">24</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +</pre> + +<p>That's not what Americans expect! It's tricky to work out in a cross-platform way +exactly what the expected format is, so there is an explicit flag:</p> + + +<pre> +> df:US_order(<span class="keyword">true</span>) +> = df:parse <span class="string">'9/11/01'</span> +<span class="number">2001</span>-<span class="number">11</span>-<span class="number">09</span> <span class="number">12</span>:<span class="number">00</span>:<span class="number">00</span> +</pre> + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/manual/06-data.md.html b/Data/Libraries/Penlight/docs/manual/06-data.md.html new file mode 100644 index 0000000..585e23e --- /dev/null +++ b/Data/Libraries/Penlight/docs/manual/06-data.md.html @@ -0,0 +1,1633 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Reading_Data_Files">Reading Data Files </a></li> +<li><a href="#Reading_Unstructured_Text_Data">Reading Unstructured Text Data </a></li> +<li><a href="#Reading_Columnar_Data">Reading Columnar Data </a></li> +<li><a href="#Reading_Configuration_Files">Reading Configuration Files </a></li> +<li><a href="#Lexical_Scanning">Lexical Scanning </a></li> +<li><a href="#XML">XML </a></li> +</ul> + + +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><strong>Data</strong></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + + +<h2>Data</h2> + +<p><a name="Reading_Data_Files"></a></p> +<h3>Reading Data Files</h3> + +<p>The first thing to consider is this: do you actually need to write a custom file +reader? And if the answer is yes, the next question is: can you write the reader +in as clear a way as possible? Correctness, Robustness, and Speed; pick the first +two and the third can be sorted out later, <em>if necessary</em>.</p> + +<p>A common sort of data file is the configuration file format commonly used on Unix +systems. This format is often called a <em>property</em> file in the Java world.</p> + + +<pre> +# Read timeout <span class="keyword">in</span> seconds +read.timeout=<span class="number">10</span> + +# Write timeout <span class="keyword">in</span> seconds +write.timeout=<span class="number">10</span> +</pre> + +<p>Here is a simple Lua implementation:</p> + + +<pre> +<span class="comment">-- property file parsing with Lua string patterns +</span>props = [] +<span class="keyword">for</span> line <span class="keyword">in</span> <span class="global">io</span>.lines() <span class="keyword">do</span> + <span class="keyword">if</span> line:find(<span class="string">'#'</span>,<span class="number">1</span>,<span class="keyword">true</span>) ~= <span class="number">1</span> <span class="keyword">and</span> <span class="keyword">not</span> line:find(<span class="string">'^%s*$'</span>) <span class="keyword">then</span> + <span class="keyword">local</span> var,value = line:match(<span class="string">'([^=]+)=(.*)'</span>) + props[var] = value + <span class="keyword">end</span> +<span class="keyword">end</span> +</pre> + +<p>Very compact, but it suffers from a similar disease in equivalent Perl programs; +it uses odd string patterns which are 'lexically noisy'. Noisy code like this +slows the casual reader down. (For an even more direct way of doing this, see the +next section, 'Reading Configuration Files')</p> + +<p>Another implementation, using the Penlight libraries:</p> + + +<pre> +<span class="comment">-- property file parsing with extended string functions +</span><span class="global">require</span> <span class="string">'pl'</span> +stringx.import() +props = [] +<span class="keyword">for</span> line <span class="keyword">in</span> <span class="global">io</span>.lines() <span class="keyword">do</span> + <span class="keyword">if</span> <span class="keyword">not</span> line:startswith(<span class="string">'#'</span>) <span class="keyword">and</span> <span class="keyword">not</span> line:isspace() <span class="keyword">then</span> + <span class="keyword">local</span> var,value = line:splitv(<span class="string">'='</span>) + props[var] = value + <span class="keyword">end</span> +<span class="keyword">end</span> +</pre> + +<p>This is more self-documenting; it is generally better to make the code express +the <em>intention</em>, rather than having to scatter comments everywhere - comments are +necessary, of course, but mostly to give the higher view of your intention that +cannot be expressed in code. It is slightly slower, true, but in practice the +speed of this script is determined by I/O, so further optimization is unnecessary.</p> + +<p><a name="Reading_Unstructured_Text_Data"></a></p> +<h3>Reading Unstructured Text Data</h3> + +<p>Text data is sometimes unstructured, for example a file containing words. The +<a href="../libraries/pl.input.html#">pl.input</a> module has a number of functions which makes processing such files +easier. For example, a script to count the number of words in standard input +using <code>import.words</code>:</p> + + +<pre> +<span class="comment">-- countwords.lua +</span><span class="global">require</span> <span class="string">'pl'</span> +<span class="keyword">local</span> k = <span class="number">1</span> +<span class="keyword">for</span> w <span class="keyword">in</span> input.words(<span class="global">io</span>.stdin) <span class="keyword">do</span> + k = k + <span class="number">1</span> +<span class="keyword">end</span> +<span class="global">print</span>(<span class="string">'count'</span>,k) +</pre> + +<p>Or this script to calculate the average of a set of numbers using <a href="../libraries/pl.input.html#numbers">input.numbers</a>:</p> + + +<pre> +<span class="comment">-- average.lua +</span><span class="global">require</span> <span class="string">'pl'</span> +<span class="keyword">local</span> k = <span class="number">1</span> +<span class="keyword">local</span> sum = <span class="number">0</span> +<span class="keyword">for</span> n <span class="keyword">in</span> input.numbers(<span class="global">io</span>.stdin) <span class="keyword">do</span> + sum = sum + n + k = k + <span class="number">1</span> +<span class="keyword">end</span> +<span class="global">print</span>(<span class="string">'average'</span>,sum/k) +</pre> + +<p>These scripts can be improved further by <em>eliminating loops</em> In the last case, +there is a perfectly good function <a href="../libraries/pl.seq.html#sum">seq.sum</a> which can already take a sequence of +numbers and calculate these numbers for us:</p> + + +<pre> +<span class="comment">-- average2.lua +</span><span class="global">require</span> <span class="string">'pl'</span> +<span class="keyword">local</span> total,n = seq.sum(input.numbers()) +<span class="global">print</span>(<span class="string">'average'</span>,total/n) +</pre> + +<p>A further simplification here is that if <code>numbers</code> or <code>words</code> are not passed an +argument, they will grab their input from standard input. The first script can +be rewritten:</p> + + +<pre> +<span class="comment">-- countwords2.lua +</span><span class="global">require</span> <span class="string">'pl'</span> +<span class="global">print</span>(<span class="string">'count'</span>,seq.count(input.words())) +</pre> + +<p>A useful feature of a sequence generator like <code>numbers</code> is that it can read from +a string source. Here is a script to calculate the sums of the numbers on each +line in a file:</p> + + +<pre> +<span class="comment">-- sums.lua +</span><span class="keyword">for</span> line <span class="keyword">in</span> <span class="global">io</span>.lines() <span class="keyword">do</span> + <span class="global">print</span>(seq.sum(input.numbers(line)) +<span class="keyword">end</span> +</pre> + +<p><a name="Reading_Columnar_Data"></a></p> +<h3>Reading Columnar Data</h3> + +<p>It is very common to find data in columnar form, either space or comma-separated, +perhaps with an initial set of column headers. Here is a typical example:</p> + + +<pre> +EventID Magnitude LocationX LocationY LocationZ +<span class="number">981124001</span> <span class="number">2.0</span> <span class="number">18988.4</span> <span class="number">10047.1</span> <span class="number">4149.7</span> +<span class="number">981125001</span> <span class="number">0.8</span> <span class="number">19104.0</span> <span class="number">9970.4</span> <span class="number">5088.7</span> +<span class="number">981127003</span> <span class="number">0.5</span> <span class="number">19012.5</span> <span class="number">9946.9</span> <span class="number">3831.2</span> +... +</pre> + +<p><a href="../libraries/pl.input.html#fields">input.fields</a> is designed to extract several columns, given some delimiter +(default to whitespace). Here is a script to calculate the average X location of +all the events:</p> + + +<pre> +<span class="comment">-- avg-x.lua +</span><span class="global">require</span> <span class="string">'pl'</span> +<span class="global">io</span>.read() <span class="comment">-- skip the header line +</span><span class="keyword">local</span> sum,count = seq.sum(input.fields {<span class="number">3</span>}) +<span class="global">print</span>(sum/count) +</pre> + +<p><a href="../libraries/pl.input.html#fields">input.fields</a> is passed either a field count, or a list of column indices, +starting at one as usual. So in this case we're only interested in column 3. If +you pass it a field count, then you get every field up to that count:</p> + + +<pre> +<span class="keyword">for</span> id,mag,locX,locY,locZ <span class="keyword">in</span> input.fields (<span class="number">5</span>) <span class="keyword">do</span> +.... +<span class="keyword">end</span> +</pre> + +<p><a href="../libraries/pl.input.html#fields">input.fields</a> by default tries to convert each field to a number. It will skip +lines which clearly don't match the pattern, but will abort the script if there +are any fields which cannot be converted to numbers.</p> + +<p>The second parameter is a delimiter, by default spaces. ' ' is understood to mean +'any number of spaces', i.e. '%s+'. Any Lua string pattern can be used.</p> + +<p>The third parameter is a <em>data source</em>, by default standard input (defined by +<a href="../libraries/pl.input.html#create_getter">input.create_getter</a>.) It assumes that the data source has a <code>read</code> method which +brings in the next line, i.e. it is a 'file-like' object. As a special case, a +string will be split into its lines:</p> + + +<pre> +> <span class="keyword">for</span> x,y <span class="keyword">in</span> input.fields(<span class="number">2</span>,<span class="string">' '</span>,<span class="string">'10 20\n30 40\n'</span>) <span class="keyword">do</span> <span class="global">print</span>(x,y) <span class="keyword">end</span> +<span class="number">10</span> <span class="number">20</span> +<span class="number">30</span> <span class="number">40</span> +</pre> + +<p>Note the default behaviour for bad fields, which is to show the offending line +number:</p> + + +<pre> +> <span class="keyword">for</span> x,y <span class="keyword">in</span> input.fields(<span class="number">2</span>,<span class="string">' '</span>,<span class="string">'10 20\n30 40x\n'</span>) <span class="keyword">do</span> <span class="global">print</span>(x,y) <span class="keyword">end</span> +<span class="number">10</span> <span class="number">20</span> +line <span class="number">2</span>: cannot convert <span class="string">'40x'</span> to number +</pre> + +<p>This behaviour of <a href="../libraries/pl.input.html#fields">input.fields</a> is appropriate for a script which you want to +fail immediately with an appropriate <em>user</em> error message if conversion fails. +The fourth optional parameter is an options table: <code>{no_fail=true}</code> means that +conversion is attempted but if it fails it just returns the string, rather as AWK +would operate. You are then responsible for checking the type of the returned +field. <code>{no_convert=true}</code> switches off conversion altogether and all fields are +returned as strings.</p> + + +<p>Sometimes it is useful to bring a whole dataset into memory, for operations such +as extracting columns. Penlight provides a flexible reader specifically for +reading this kind of data, using the <a href="../libraries/pl.data.html#">data</a> module. Given a file looking like this:</p> + + +<pre> +x,y +<span class="number">10</span>,<span class="number">20</span> +<span class="number">2</span>,<span class="number">5</span> +<span class="number">40</span>,<span class="number">50</span> +</pre> + +<p>Then <a href="../libraries/pl.data.html#read">data.read</a> will create a table like this, with each row represented by a +sublist:</p> + + +<pre> +> t = data.read <span class="string">'test.txt'</span> +> pretty.dump(t) +{{<span class="number">10</span>,<span class="number">20</span>},{<span class="number">2</span>,<span class="number">5</span>},{<span class="number">40</span>,<span class="number">50</span>},fieldnames={<span class="string">'x'</span>,<span class="string">'y'</span>},delim=<span class="string">','</span>} +</pre> + +<p>You can now analyze this returned table using the supplied methods. For instance, +the method <a href="../libraries/pl.data.html#Data.column_by_name">column_by_name</a> returns a table of all the values of that column.</p> + + +<pre> +<span class="comment">-- testdata.lua +</span><span class="global">require</span> <span class="string">'pl'</span> +d = data.read(<span class="string">'fev.txt'</span>) +<span class="keyword">for</span> _,name <span class="keyword">in</span> <span class="global">ipairs</span>(d.fieldnames) <span class="keyword">do</span> + <span class="keyword">local</span> col = d:column_by_name(name) + <span class="keyword">if</span> <span class="global">type</span>(col[<span class="number">1</span>]) == <span class="string">'number'</span> <span class="keyword">then</span> + <span class="keyword">local</span> total,n = seq.sum(col) + utils.printf(<span class="string">"Average for %s is %f\n"</span>,name,total/n) + <span class="keyword">end</span> +<span class="keyword">end</span> +</pre> + +<p><a href="../libraries/pl.data.html#read">data.read</a> tries to be clever when given data; by default it expects a first +line of column names, unless any of them are numbers. It tries to deduce the +column delimiter by looking at the first line. Sometimes it guesses wrong; these +things can be specified explicitly. The second optional parameter is an options +table: can override <code>delim</code> (a string pattern), <code>fieldnames</code> (a list or +comma-separated string), specify <code>no_convert</code> (default is to convert), numfields +(indices of columns known to be numbers, as a list) and <code>thousands_dot</code> (when the +thousands separator in Excel CSV is '.')</p> + +<p>A very powerful feature is a way to execute SQL-like queries on such data:</p> + + +<pre> +<span class="comment">-- queries on tabular data +</span><span class="global">require</span> <span class="string">'pl'</span> +<span class="keyword">local</span> d = data.read(<span class="string">'xyz.txt'</span>) +<span class="keyword">local</span> q = d:<span class="global">select</span>(<span class="string">'x,y,z where x > 3 and z < 2 sort by y'</span>) +<span class="keyword">for</span> x,y,z <span class="keyword">in</span> q <span class="keyword">do</span> + <span class="global">print</span>(x,y,z) +<span class="keyword">end</span> +</pre> + +<p>Please note that the format of queries is restricted to the following syntax:</p> + + +<pre> +FIELDLIST [ <span class="string">'where'</span> CONDITION ] [ <span class="string">'sort by'</span> FIELD [asc|desc]] +</pre> + +<p>Any valid Lua code can appear in <code>CONDITION</code>; remember it is <em>not</em> SQL and you +have to use <code>==</code> (this warning comes from experience.)</p> + +<p>For this to work, <em>field names must be Lua identifiers</em>. So <a href="../libraries/pl.data.html#read">read</a> will massage +fieldnames so that all non-alphanumeric chars are replaced with underscores. +However, the <code>original_fieldnames</code> field always contains the original un-massaged +fieldnames.</p> + +<p><a href="../libraries/pl.data.html#read">read</a> can handle standard CSV files fine, although doesn't try to be a +full-blown CSV parser. With the <code>csv=true</code> option, it's possible to have +double-quoted fields, which may contain commas; then trailing commas become +significant as well.</p> + +<p>Spreadsheet programs are not always the best tool to +process such data, strange as this might seem to some people. This is a toy CSV +file; to appreciate the problem, imagine thousands of rows and dozens of columns +like this:</p> + + +<pre> +Department Name,Employee ID,Project,Hours Booked +sales,<span class="number">1231</span>,overhead,<span class="number">4</span> +sales,<span class="number">1255</span>,overhead,<span class="number">3</span> +engineering,<span class="number">1501</span>,development,<span class="number">5</span> +engineering,<span class="number">1501</span>,maintenance,<span class="number">3</span> +engineering,<span class="number">1433</span>,maintenance,<span class="number">10</span> +</pre> + +<p>The task is to reduce the dataset to a relevant set of rows and columns, perhaps +do some processing on row data, and write the result out to a new CSV file. The +<a href="../libraries/pl.data.html#Data.write_row">write_row</a> method uses the delimiter to write the row to a file; +<code>Data.select_row</code> is like <code>Data.select</code>, except it iterates over <em>rows</em>, not +fields; this is necessary if we are dealing with a lot of columns!</p> + + +<pre> +names = {[<span class="number">1501</span>]=<span class="string">'don'</span>,[<span class="number">1433</span>]=<span class="string">'dilbert'</span>} +keepcols = {<span class="string">'Employee_ID'</span>,<span class="string">'Hours_Booked'</span>} +t:write_row (outf,{<span class="string">'Employee'</span>,<span class="string">'Hours_Booked'</span>}) +q = t:select_row { + fields=keepcols, + where=<span class="keyword">function</span>(row) <span class="keyword">return</span> row[<span class="number">1</span>]==<span class="string">'engineering'</span> <span class="keyword">end</span> +} +<span class="keyword">for</span> row <span class="keyword">in</span> q <span class="keyword">do</span> + row[<span class="number">1</span>] = names[row[<span class="number">1</span>]] + t:write_row(outf,row) +<span class="keyword">end</span> +</pre> + +<p><code>Data.select_row</code> and <code>Data.select</code> can be passed a table specifying the query; a +list of field names, a function defining the condition and an optional parameter +<code>sort_by</code>. It isn't really necessary here, but if we had a more complicated row +condition (such as belonging to a specified set) then it is not generally +possible to express such a condition as a query string, without resorting to +hackery such as global variables.</p> + +<p>With 1.0.3, you can specify explicit conversion functions for selected columns. +For instance, this is a log file with a Unix date stamp:</p> + + +<pre> +Time Message +<span class="number">1266840760</span> +# EE7C0600006F0D00C00F06010302054000000308010A00002B00407B00 +<span class="number">1266840760</span> closure data <span class="number">0.000000</span> <span class="number">1972</span> <span class="number">1972</span> <span class="number">0</span> +<span class="number">1266840760</span> ++ <span class="number">1266840760</span> EE <span class="number">1</span> +<span class="number">1266840760</span> +# EE7C0600006F0D00C00F06010302054000000408020A00002B00407B00 +<span class="number">1266840764</span> closure data <span class="number">0.000000</span> <span class="number">1972</span> <span class="number">1972</span> <span class="number">0</span> +</pre> + +<p>We would like the first column as an actual date object, so the <code>convert</code> +field sets an explicit conversion for column 1. (Note that we have to explicitly +convert the string to a number first.)</p> + + +<pre> +Date = <span class="global">require</span> <span class="string">'pl.Date'</span> + +<span class="keyword">function</span> date_convert (ds) + <span class="keyword">return</span> Date(<span class="global">tonumber</span>(ds)) +<span class="keyword">end</span> + +d = data.read(f,{convert={[<span class="number">1</span>]=date_convert},last_field_collect=<span class="keyword">true</span>}) +</pre> + +<p>This gives us a two-column dataset, where the first column contains <a href="../classes/pl.Date.html#">Date</a> objects +and the second column contains the rest of the line. Queries can then easily +pick out events on a day of the week:</p> + + +<pre> +q = d:<span class="global">select</span> <span class="string">"Time,Message where Time:weekday_name()=='Sun'"</span> +</pre> + +<p>Data does not have to come from files, nor does it necessarily come from the lab +or the accounts department. On Linux, <code>ps aux</code> gives you a full listing of all +processes running on your machine. It is straightforward to feed the output of +this command into <a href="../libraries/pl.data.html#read">data.read</a> and perform useful queries on it. Notice that +non-identifier characters like '%' get converted into underscores:</p> + + +<pre> +<span class="global">require</span> <span class="string">'pl'</span> +f = <span class="global">io</span>.popen <span class="string">'ps aux'</span> +s = data.read (f,{last_field_collect=<span class="keyword">true</span>}) +f:close() +<span class="global">print</span>(s.fieldnames) +<span class="global">print</span>(s:column_by_name <span class="string">'USER'</span>) +qs = <span class="string">'COMMAND,_MEM where _MEM > 5 and USER=="steve"'</span> +<span class="keyword">for</span> name,mem <span class="keyword">in</span> s:<span class="global">select</span>(qs) <span class="keyword">do</span> + <span class="global">print</span>(mem,name) +<span class="keyword">end</span> +</pre> + +<p>I've always been an admirer of the AWK programming language; with <a href="../libraries/pl.data.html#filter">filter</a> you +can get Lua programs which are just as compact:</p> + + +<pre> +<span class="comment">-- printxy.lua +</span><span class="global">require</span> <span class="string">'pl'</span> +data.filter <span class="string">'x,y where x > 3'</span> +</pre> + +<p>It is common enough to have data files without headers of field names. +<a href="../libraries/pl.data.html#read">data.read</a> makes a special exception for such files if all fields are numeric. +Since there are no column names to use in query expressions, you can use AWK-like +column indexes, e.g. '$1,$2 where $1 > 3'. I have a little executable script on +my system called <code>lf</code> which looks like this:</p> + + +<pre> +#!/usr/bin/env lua +<span class="global">require</span> <span class="string">'pl.data'</span>.filter(arg[<span class="number">1</span>]) +</pre> + +<p>And it can be used generally as a filter command to extract columns from data. +(The column specifications may be expressions or even constants.)</p> + + +<pre> +$ lf <span class="string">'$1,$5/10'</span> < test.dat +</pre> + +<p>(As with AWK, please note the single-quotes used in this command; this prevents +the shell trying to expand the column indexes. If you are on Windows, then you +must quote the expression in double-quotes so +it is passed as one argument to your batch file.)</p> + +<p>As a tutorial resource, have a look at <a href="../examples/test-data.lua.html#">test-data.lua</a> in the PL tests directory +for other examples of use, plus comments.</p> + +<p>The data returned by <a href="../libraries/pl.data.html#read">read</a> or constructed by <code>Data.copy_select</code> from a query is +basically just an array of rows: <code>{{1,2},{3,4}}</code>. So you may use <a href="../libraries/pl.data.html#read">read</a> to pull +in any array-like dataset, and process with any function that expects such a +implementation. In particular, the functions in <a href="../libraries/pl.array2d.html#">array2d</a> will work fine with +this data. In fact, these functions are available as methods; e.g. +<a href="../libraries/pl.array2d.html#flatten">array2d.flatten</a> can be called directly like so to give us a one-dimensional list:</p> + + +<pre> +v = data.read(<span class="string">'dat.txt'</span>):flatten() +</pre> + +<p>The data is also in exactly the right shape to be treated as matrices by +<a href="http://lua-users.org/wiki/LuaMatrix">LuaMatrix</a>:</p> + + +<pre> +> matrix = <span class="global">require</span> <span class="string">'matrix'</span> +> m = matrix(data.read <span class="string">'mat.txt'</span>) +> = m +<span class="number">1</span> <span class="number">0.2</span> <span class="number">0.3</span> +<span class="number">0.2</span> <span class="number">1</span> <span class="number">0.1</span> +<span class="number">0.1</span> <span class="number">0.2</span> <span class="number">1</span> +> = m^<span class="number">2</span> <span class="comment">-- same as m*m +</span><span class="number">1.07</span> <span class="number">0.46</span> <span class="number">0.62</span> +<span class="number">0.41</span> <span class="number">1.06</span> <span class="number">0.26</span> +<span class="number">0.24</span> <span class="number">0.42</span> <span class="number">1.05</span> +</pre> + +<p><a href="../libraries/pl.data.html#write">write</a> will write matrices back to files for you.</p> + +<p>Finally, for the curious, the global variable <code>_DEBUG</code> can be used to print out +the actual iterator function which a query generates and dynamically compiles. By +using code generation, we can get pretty much optimal performance out of +arbitrary queries.</p> + + +<pre> +> lua -lpl -e <span class="string">"_DEBUG=true"</span> -e <span class="string">"data.filter 'x,y where x > 4 sort by x'"</span> < test.txt +<span class="keyword">return</span> <span class="keyword">function</span> (t) + <span class="keyword">local</span> i = <span class="number">0</span> + <span class="keyword">local</span> v + <span class="keyword">local</span> ls = {} + <span class="keyword">for</span> i,v <span class="keyword">in</span> <span class="global">ipairs</span>(t) <span class="keyword">do</span> + <span class="keyword">if</span> v[<span class="number">1</span>] > <span class="number">4</span> <span class="keyword">then</span> + ls[#ls+<span class="number">1</span>] = v + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="global">table</span>.sort(ls,<span class="keyword">function</span>(v1,v2) + <span class="keyword">return</span> v1[<span class="number">1</span>] < v2[<span class="number">1</span>] + <span class="keyword">end</span>) + <span class="keyword">local</span> n = #ls + <span class="keyword">return</span> <span class="keyword">function</span>() + i = i + <span class="number">1</span> + v = ls[i] + <span class="keyword">if</span> i > n <span class="keyword">then</span> <span class="keyword">return</span> <span class="keyword">end</span> + <span class="keyword">return</span> v[<span class="number">1</span>],v[<span class="number">2</span>] + <span class="keyword">end</span> +<span class="keyword">end</span> + +<span class="number">10</span>,<span class="number">20</span> +<span class="number">40</span>,<span class="number">50</span> +</pre> + +<p><a name="Reading_Configuration_Files"></a></p> +<h3>Reading Configuration Files</h3> + +<p>The <a href="../libraries/pl.config.html#">config</a> module provides a simple way to convert several kinds of +configuration files into a Lua table. Consider the simple example:</p> + + +<pre> +# test.config +# Read timeout <span class="keyword">in</span> seconds +read.timeout=<span class="number">10</span> + +# Write timeout <span class="keyword">in</span> seconds +write.timeout=<span class="number">5</span> + +#acceptable ports +ports = <span class="number">1002</span>,<span class="number">1003</span>,<span class="number">1004</span> +</pre> + +<p>This can be easily brought in using <a href="../libraries/pl.config.html#read">config.read</a> and the result shown using +<a href="../libraries/pl.pretty.html#write">pretty.write</a>:</p> + + +<pre> +<span class="comment">-- readconfig.lua +</span><span class="keyword">local</span> config = <span class="global">require</span> <span class="string">'pl.config'</span> +<span class="keyword">local</span> pretty= <span class="global">require</span> <span class="string">'pl.pretty'</span> + +<span class="keyword">local</span> t = config.read(arg[<span class="number">1</span>]) +<span class="global">print</span>(pretty.write(t)) +</pre> + +<p>and the output of <code>lua readconfig.lua test.config</code> is:</p> + + +<pre> +{ + ports = { + <span class="number">1002</span>, + <span class="number">1003</span>, + <span class="number">1004</span> + }, + write_timeout = <span class="number">5</span>, + read_timeout = <span class="number">10</span> +} +</pre> + +<p>That is, <a href="../libraries/pl.config.html#read">config.read</a> will bring in all key/value pairs, ignore # comments, and +ensure that the key names are proper Lua identifiers by replacing non-identifier +characters with '_'. If the values are numbers, then they will be converted. (So +the value of <code>t.write_timeout</code> is the number 5). In addition, any values which +are separated by commas will be converted likewise into an array.</p> + +<p>Any line can be continued with a backslash. So this will all be considered one +line:</p> + + +<pre> +names=one,two,three, \ +four,five,six,seven, \ +eight,nine,ten +</pre> + +<p>Windows-style INI files are also supported. The section structure of INI files +translates naturally to nested tables in Lua:</p> + + +<pre> +; test.ini +[timeouts] +read=<span class="number">10</span> ; Read timeout <span class="keyword">in</span> seconds +write=<span class="number">5</span> ; Write timeout <span class="keyword">in</span> seconds +[portinfo] +ports = <span class="number">1002</span>,<span class="number">1003</span>,<span class="number">1004</span> +</pre> + +<p> The output is:</p> + + +<pre> +{ + portinfo = { + ports = { + <span class="number">1002</span>, + <span class="number">1003</span>, + <span class="number">1004</span> + } + }, + timeouts = { + write = <span class="number">5</span>, + read = <span class="number">10</span> + } +} +</pre> + +<p>You can now refer to the write timeout as <code>t.timeouts.write</code>.</p> + +<p>As a final example of the flexibility of <a href="../libraries/pl.config.html#read">config.read</a>, if passed this simple +comma-delimited file</p> + + +<pre> +one,two,three +<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span> +<span class="number">40</span>,<span class="number">50</span>,<span class="number">60</span> +<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span> +</pre> + +<p>it will produce the following table:</p> + + +<pre> +{ + { <span class="string">"one"</span>, <span class="string">"two"</span>, <span class="string">"three"</span> }, + { <span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span> }, + { <span class="number">40</span>, <span class="number">50</span>, <span class="number">60</span> }, + { <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span> } +} +</pre> + +<p><a href="../libraries/pl.config.html#read">config.read</a> isn't designed to read all CSV files in general, but intended to +support some Unix configuration files not structured as key-value pairs, such as +'/etc/passwd'.</p> + +<p>This function is intended to be a Swiss Army Knife of configuration readers, but +it does have to make assumptions, and you may not like them. So there is an +optional extra parameter which allows some control, which is table that may have +the following fields:</p> + + +<pre> +{ + variablilize = <span class="keyword">true</span>, + convert_numbers = <span class="global">tonumber</span>, + trim_space = <span class="keyword">true</span>, + list_delim = <span class="string">','</span>, + trim_quotes = <span class="keyword">true</span>, + ignore_assign = <span class="keyword">false</span>, + keysep = <span class="string">'='</span>, + smart = <span class="keyword">false</span>, +} +</pre> + +<p><code>variablilize</code> is the option that converted <code>write.timeout</code> in the first example +to the valid Lua identifier <code>write_timeout</code>. If <code>convert_numbers</code> is true, then +an attempt is made to convert any string that starts like a number. You can +specify your own function (say one that will convert a string like '5224 kb' into +a number.)</p> + +<p><code>trim_space</code> ensures that there is no starting or trailing whitespace with +values, and <code>list_delim</code> is the character that will be used to decide whether to +split a value up into a list (it may be a Lua string pattern such as '%s+'.)</p> + +<p>For instance, the password file in Unix is colon-delimited:</p> + + +<pre> +t = config.read(<span class="string">'/etc/passwd'</span>,{list_delim=<span class="string">':'</span>}) +</pre> + +<p>This produces the following output on my system (only last two lines shown):</p> + + +<pre> +{ + ... + { + <span class="string">"user"</span>, + <span class="string">"x"</span>, + <span class="string">"1000"</span>, + <span class="string">"1000"</span>, + <span class="string">"user,,,"</span>, + <span class="string">"/home/user"</span>, + <span class="string">"/bin/bash"</span> + }, + { + <span class="string">"sdonovan"</span>, + <span class="string">"x"</span>, + <span class="string">"1001"</span>, + <span class="string">"1001"</span>, + <span class="string">"steve donovan,28,,"</span>, + <span class="string">"/home/sdonovan"</span>, + <span class="string">"/bin/bash"</span> + } +} +</pre> + +<p>You can get this into a more sensible format, where the usernames are the keys, +with this (the <a href="../libraries/pl.tablex.html#pairmap">tablex.pairmap</a> function must return value, key!)</p> + + +<pre> +t = tablex.pairmap(<span class="keyword">function</span>(k,v) <span class="keyword">return</span> v,v[<span class="number">1</span>] <span class="keyword">end</span>,t) +</pre> + +<p>and you get:</p> + + +<pre> +{ ... + sdonovan = { + <span class="string">"sdonovan"</span>, + <span class="string">"x"</span>, + <span class="string">"1001"</span>, + <span class="string">"1001"</span>, + <span class="string">"steve donovan,28,,"</span>, + <span class="string">"/home/sdonovan"</span>, + <span class="string">"/bin/bash"</span> + } +... +} +</pre> + +<p>Many common Unix configuration files can be read by tweaking these parameters. +For <code>/etc/fstab</code>, the options <code>{list_delim='%s+',ignore_assign=true}</code> will +correctly separate the columns. It's common to find 'KEY VALUE' assignments in +files such as <code>/etc/ssh/ssh_config</code>; the options <code>{keysep=' '}</code> make +<a href="../libraries/pl.config.html#read">config.read</a> return a table where each KEY has a value VALUE.</p> + +<p>Files in the Linux <code>procfs</code> usually use ':` as the field delimiter:</p> + + +<pre> +> t = config.read(<span class="string">'/proc/meminfo'</span>,{keysep=<span class="string">':'</span>}) +> = t.MemFree +<span class="number">220140</span> kB +</pre> + +<p>That result is a string, since <a href="https://www.lua.org/manual/5.1/manual.html#pdf-tonumber">tonumber</a> doesn't like it, but defining the +<code>convert_numbers</code> option as `function(s) return tonumber((s:gsub(' kB$',''))) +end` will get the memory figures as actual numbers in the result. (The extra +parentheses are necessary so that <a href="https://www.lua.org/manual/5.1/manual.html#pdf-tonumber">tonumber</a> only gets the first result from +<code>gsub</code>). From `tests/test-config.lua':</p> + + +<pre> +testconfig(<span class="string">[[ +MemTotal: 1024748 kB +MemFree: 220292 kB +]]</span>, +{ MemTotal = <span class="number">1024748</span>, MemFree = <span class="number">220292</span> }, +{ + keysep = <span class="string">':'</span>, + convert_numbers = <span class="keyword">function</span>(s) + s = s:gsub(<span class="string">' kB$'</span>,<span class="string">''</span>) + <span class="keyword">return</span> <span class="global">tonumber</span>(s) + <span class="keyword">end</span> + } +) +</pre> + +<p>The <code>smart</code> option lets <a href="../libraries/pl.config.html#read">config.read</a> make a reasonable guess for you; there +are examples in <code>tests/test-config.lua</code>, but basically these common file +formats (and those following the same pattern) can be processed directly in +smart mode: 'etc/fstab', '/proc/XXXX/status', 'ssh_config' and 'pdatedb.conf'.</p> + +<p>Please note that <a href="../libraries/pl.config.html#read">config.read</a> can be passed a <em>file-like object</em>; if it's not a +string and supports the <a href="../libraries/pl.data.html#read">read</a> method, then that will be used. For instance, to +read a configuration from a string, use <a href="../libraries/pl.stringio.html#open">stringio.open</a>.</p> + + +<p><a id="lexer"/></p> + +<p><a name="Lexical_Scanning"></a></p> +<h3>Lexical Scanning</h3> + +<p>Although Lua's string pattern matching is very powerful, there are times when +something more powerful is needed. <a href="../libraries/pl.lexer.html#scan">pl.lexer.scan</a> provides lexical scanners +which <em>tokenize</em> a string, classifying tokens into numbers, strings, etc.</p> + + +<pre> +> lua -lpl +Lua <span class="number">5.1</span>.<span class="number">4</span> Copyright (C) <span class="number">1994</span>-<span class="number">2008</span> Lua.org, PUC-Rio +> tok = lexer.scan <span class="string">'alpha = sin(1.5)'</span> +> = tok() +iden alpha +> = tok() += = +> = tok() +iden sin +> = tok() +( ( +> = tok() +number <span class="number">1.5</span> +> = tok() +) ) +> = tok() +(<span class="keyword">nil</span>) +</pre> + +<p>The scanner is a function, which is repeatedly called and returns the <em>type</em> and +<em>value</em> of the token. Recognized basic types are 'iden','string','number', and +'space'. and everything else is represented by itself. Note that by default the +scanner will skip any 'space' tokens.</p> + +<p>'comment' and 'keyword' aren't applicable to the plain scanner, which is not +language-specific, but a scanner which understands Lua is available. It +recognizes the Lua keywords, and understands both short and long comments and +strings.</p> + + +<pre> +> <span class="keyword">for</span> t,v <span class="keyword">in</span> lexer.lua <span class="string">'for i=1,n do'</span> <span class="keyword">do</span> <span class="global">print</span>(t,v) <span class="keyword">end</span> +keyword <span class="keyword">for</span> +iden i += = +number <span class="number">1</span> +, , +iden n +keyword <span class="keyword">do</span> +</pre> + +<p>A lexical scanner is useful where you have highly-structured data which is not +nicely delimited by newlines. For example, here is a snippet of a in-house file +format which it was my task to maintain:</p> + + +<pre> +points + (<span class="number">818344.1</span>,-<span class="number">20389.7</span>,-<span class="number">0.1</span>),(<span class="number">818337.9</span>,-<span class="number">20389.3</span>,-<span class="number">0.1</span>),(<span class="number">818332.5</span>,-<span class="number">20387.8</span>,-<span class="number">0.1</span>) + ,(<span class="number">818327.4</span>,-<span class="number">20388</span>,-<span class="number">0.1</span>),(<span class="number">818322</span>,-<span class="number">20387.7</span>,-<span class="number">0.1</span>),(<span class="number">818316.3</span>,-<span class="number">20388.6</span>,-<span class="number">0.1</span>) + ,(<span class="number">818309.7</span>,-<span class="number">20389.4</span>,-<span class="number">0.1</span>),(<span class="number">818303.5</span>,-<span class="number">20390.6</span>,-<span class="number">0.1</span>),(<span class="number">818295.8</span>,-<span class="number">20388.3</span>,-<span class="number">0.1</span>) + ,(<span class="number">818290.5</span>,-<span class="number">20386.9</span>,-<span class="number">0.1</span>),(<span class="number">818285.2</span>,-<span class="number">20386.1</span>,-<span class="number">0.1</span>),(<span class="number">818279.3</span>,-<span class="number">20383.6</span>,-<span class="number">0.1</span>) + ,(<span class="number">818274</span>,-<span class="number">20381.2</span>,-<span class="number">0.1</span>),(<span class="number">818274</span>,-<span class="number">20380.7</span>,-<span class="number">0.1</span>); +</pre> + +<p>Here is code to extract the points using <a href="../libraries/pl.lexer.html#">pl.lexer</a>:</p> + + +<pre> +<span class="comment">-- assume 's' contains the text above... +</span><span class="keyword">local</span> lexer = <span class="global">require</span> <span class="string">'pl.lexer'</span> +<span class="keyword">local</span> expecting = lexer.expecting +<span class="keyword">local</span> append = <span class="global">table</span>.insert + +<span class="keyword">local</span> tok = lexer.scan(s) + +<span class="keyword">local</span> points = {} +<span class="keyword">local</span> t,v = tok() <span class="comment">-- should be 'iden','points' +</span> +<span class="keyword">while</span> t ~= <span class="string">';'</span> <span class="keyword">do</span> + c = {} + expecting(tok,<span class="string">'('</span>) + c.x = expecting(tok,<span class="string">'number'</span>) + expecting(tok,<span class="string">','</span>) + c.y = expecting(tok,<span class="string">'number'</span>) + expecting(tok,<span class="string">','</span>) + c.z = expecting(tok,<span class="string">'number'</span>) + expecting(tok,<span class="string">')'</span>) + t,v = tok() <span class="comment">-- either ',' or ';' +</span> append(points,c) +<span class="keyword">end</span> +</pre> + +<p>The <code>expecting</code> function grabs the next token and if the type doesn't match, it +throws an error. (<a href="../libraries/pl.lexer.html#">pl.lexer</a>, unlike other PL libraries, raises errors if +something goes wrong, so you should wrap your code in <a href="https://www.lua.org/manual/5.1/manual.html#pdf-pcall">pcall</a> to catch the error +gracefully.)</p> + +<p>The scanners all have a second optional argument, which is a table which controls +whether you want to exclude spaces and/or comments. The default for <a href="../libraries/pl.lexer.html#lua">lexer.lua</a> +is <code>{space=true,comments=true}</code>. There is a third optional argument which +determines how string and number tokens are to be processsed.</p> + +<p>The ultimate highly-structured data is of course, program source. Here is a +snippet from 'text-lexer.lua':</p> + + +<pre> +<span class="global">require</span> <span class="string">'pl'</span> + +lines = <span class="string">[[ +for k,v in pairs(t) do + if type(k) == 'number' then + print(v) -- array-like case + else + print(k,v) + end +end +]]</span> + +ls = List() +<span class="keyword">for</span> tp,val <span class="keyword">in</span> lexer.lua(lines,{space=<span class="keyword">true</span>,comments=<span class="keyword">true</span>}) <span class="keyword">do</span> + <span class="global">assert</span>(tp ~= <span class="string">'space'</span> <span class="keyword">and</span> tp ~= <span class="string">'comment'</span>) + <span class="keyword">if</span> tp == <span class="string">'keyword'</span> <span class="keyword">then</span> ls:append(val) <span class="keyword">end</span> +<span class="keyword">end</span> +test.asserteq(ls,List{<span class="string">'for'</span>,<span class="string">'in'</span>,<span class="string">'do'</span>,<span class="string">'if'</span>,<span class="string">'then'</span>,<span class="string">'else'</span>,<span class="string">'end'</span>,<span class="string">'end'</span>}) +</pre> + +<p>Here is a useful little utility that identifies all common global variables found +in a lua module (ignoring those declared locally for the moment):</p> + + +<pre> +<span class="comment">-- testglobal.lua +</span><span class="global">require</span> <span class="string">'pl'</span> + +<span class="keyword">local</span> txt,err = utils.readfile(arg[<span class="number">1</span>]) +<span class="keyword">if</span> <span class="keyword">not</span> txt <span class="keyword">then</span> <span class="keyword">return</span> <span class="global">print</span>(err) <span class="keyword">end</span> + +<span class="keyword">local</span> globals = List() +<span class="keyword">for</span> t,v <span class="keyword">in</span> lexer.lua(txt) <span class="keyword">do</span> + <span class="keyword">if</span> t == <span class="string">'iden'</span> <span class="keyword">and</span> _G[v] <span class="keyword">then</span> + globals:append(v) + <span class="keyword">end</span> +<span class="keyword">end</span> +pretty.dump(seq.count_map(globals)) +</pre> + +<p>Rather then dumping the whole list, with its duplicates, we pass it through +<a href="../libraries/pl.seq.html#count_map">seq.count_map</a> which turns the list into a table where the keys are the values, +and the associated values are the number of times those values occur in the +sequence. Typical output looks like this:</p> + + +<pre> +{ + <span class="global">type</span> = <span class="number">2</span>, + <span class="global">pairs</span> = <span class="number">2</span>, + <span class="global">table</span> = <span class="number">2</span>, + <span class="global">print</span> = <span class="number">3</span>, + <span class="global">tostring</span> = <span class="number">2</span>, + <span class="global">require</span> = <span class="number">1</span>, + <span class="global">ipairs</span> = <span class="number">4</span> +} +</pre> + +<p>You could further pass this through <a href="../libraries/pl.tablex.html#keys">tablex.keys</a> to get a unique list of +symbols. This can be useful when writing 'strict' Lua modules, where all global +symbols must be defined as locals at the top of the file.</p> + +<p>For a more detailed use of <a href="../libraries/pl.lexer.html#scan">lexer.scan</a>, please look at <a href="../examples/testxml.lua.html#">testxml.lua</a> in the +examples directory.</p> + +<p><a name="XML"></a></p> +<h3>XML</h3> + +<p>New in the 0.9.7 release is some support for XML. This is a large topic, and +Penlight does not provide a full XML stack, which is properly the task of a more +specialized library.</p> + +<h4>Parsing and Pretty-Printing</h4> + +<p>The semi-standard XML parser in the Lua universe is <a href="http://matthewwild.co.uk/projects/luaexpat/">lua-expat</a>. +In particular, +it has a function called <code>lxp.lom.parse</code> which will parse XML into the Lua Object +Model (LOM) format. However, it does not provide a way to convert this data back +into XML text. <a href="../libraries/pl.xml.html#parse">xml.parse</a> will use this function, <em>if</em> <code>lua-expat</code> is +available, and otherwise switches back to a pure Lua parser originally written by +Roberto Ierusalimschy.</p> + +<p>The resulting document object knows how to render itself as a string, which is +useful for debugging:</p> + + +<pre> +> d = xml.parse <span class="string">"<nodes><node id='1'>alice</node></nodes>"</span> +> = d +<nodes><node id=<span class="string">'1'</span>>alice</node></nodes> +> pretty.dump (d) +{ + { + <span class="string">"alice"</span>, + attr = { + <span class="string">"id"</span>, + id = <span class="string">"1"</span> + }, + tag = <span class="string">"node"</span> + }, + attr = { + }, + tag = <span class="string">"nodes"</span> +} +</pre> + +<p>Looking at the actual shape of the data reveals the structure of LOM:</p> + +<ul> + <li>every element has a <code>tag</code> field with its name</li> + <li>plus a <code>attr</code> field which is a table containing the attributes as fields, and + also as an array. It is always present.</li> + <li>the children of the element are the array part of the element, so <code>d[1]</code> is + the first child of <code>d</code>, etc.</li> +</ul> + +<p>It could be argued that having attributes also as the array part of <code>attr</code> is not +essential (you cannot depend on attribute order in XML) but that's how +it goes with this standard.</p> + +<p><code>lua-expat</code> is another <em>soft dependency</em> of Penlight; generally, the fallback +parser is good enough for straightforward XML as is commonly found in +configuration files, etc. <code>doc.basic_parse</code> is not intended to be a proper +conforming parser (it's only sixty lines) but it handles simple kinds of +documents that do not have comments or DTD directives. It is intelligent enough +to ignore the <code><?xml</code> directive and that is about it.</p> + +<p>You can get pretty-printing by explicitly calling <a href="../libraries/pl.xml.html#tostring">xml.tostring</a> and passing it +the initial indent and the per-element indent:</p> + + +<pre> +> = xml.<span class="global">tostring</span>(d,<span class="string">''</span>,<span class="string">' '</span>) + +<nodes> + <node id=<span class="string">'1'</span>>alice</node> +</nodes> +</pre> + +<p>There is a fourth argument which is the <em>attribute indent</em>:</p> + + +<pre> +> a = xml.parse <span class="string">"<frodo name='baggins' age='50' type='hobbit'/>"</span> +> = xml.<span class="global">tostring</span>(a,<span class="string">''</span>,<span class="string">' '</span>,<span class="string">' '</span>) + +<frodo + <span class="global">type</span>=<span class="string">'hobbit'</span> + name=<span class="string">'baggins'</span> + age=<span class="string">'50'</span> +/> +</pre> + +<h4>Parsing and Working with Configuration Files</h4> + +<p>It's common to find configurations expressed with XML these days. It's +straightforward to 'walk' the <a href="http://matthewwild.co.uk/projects/luaexpat/lom.html">LOM</a> +data and extract the data in the form you want:</p> + + +<pre> +<span class="global">require</span> <span class="string">'pl'</span> + +<span class="keyword">local</span> config = <span class="string">[[ +<config> + <alpha>1.3</alpha> + <beta>10</beta> + <name>bozo</name> +</config> +]]</span> +<span class="keyword">local</span> d,err = xml.parse(config) + +<span class="keyword">local</span> t = {} +<span class="keyword">for</span> item <span class="keyword">in</span> d:childtags() <span class="keyword">do</span> + t[item.tag] = item[<span class="number">1</span>] +<span class="keyword">end</span> + +pretty.dump(t) +<span class="comment">---> +</span>{ + beta = <span class="string">"10"</span>, + alpha = <span class="string">"1.3"</span>, + name = <span class="string">"bozo"</span> +} +</pre> + +<p>The only gotcha is that here we must use the <code>Doc:childtags</code> method, which will +skip over any text elements.</p> + +<p>A more involved example is this excerpt from <code>serviceproviders.xml</code>, which is +usually found at <code>/usr/share/mobile-broadband-provider-info/serviceproviders.xml</code> +on Debian/Ubuntu Linux systems.</p> + + +<pre> +d = xml.parse <span class="string">[[ +<serviceproviders format="2.0"> +... +<country code="za"> + <provider> + <name>Cell-c</name> + <gsm> + <network-id mcc="655" mnc="07"/> + <apn value="internet"> + <username>Cellcis</username> + <dns>196.7.0.138</dns> + <dns>196.7.142.132</dns> + </apn> + </gsm> + </provider> + <provider> + <name>MTN</name> + <gsm> + <network-id mcc="655" mnc="10"/> + <apn value="internet"> + <dns>196.11.240.241</dns> + <dns>209.212.97.1</dns> + </apn> + </gsm> + </provider> + <provider> + <name>Vodacom</name> + <gsm> + <network-id mcc="655" mnc="01"/> + <apn value="internet"> + <dns>196.207.40.165</dns> + <dns>196.43.46.190</dns> + </apn> + <apn value="unrestricted"> + <name>Unrestricted</name> + <dns>196.207.32.69</dns> + <dns>196.43.45.190</dns> + </apn> + </gsm> + </provider> + <provider> + <name>Virgin Mobile</name> + <gsm> + <apn value="vdata"> + <dns>196.7.0.138</dns> + <dns>196.7.142.132</dns> + </apn> + </gsm> + </provider> +</country> +.... +</serviceproviders> +]]</span> +</pre> + +<p>Getting the names of the providers per-country is straightforward:</p> + + +<pre> +<span class="keyword">local</span> t = {} +<span class="keyword">for</span> country <span class="keyword">in</span> d:childtags() <span class="keyword">do</span> + <span class="keyword">local</span> providers = {} + t[country.attr.code] = providers + <span class="keyword">for</span> provider <span class="keyword">in</span> country:childtags() <span class="keyword">do</span> + <span class="global">table</span>.insert(providers,provider:child_with_name(<span class="string">'name'</span>):get_text()) + <span class="keyword">end</span> +<span class="keyword">end</span> + +pretty.dump(t) +<span class="comment">--> +</span>{ + za = { + <span class="string">"Cell-c"</span>, + <span class="string">"MTN"</span>, + <span class="string">"Vodacom"</span>, + <span class="string">"Virgin Mobile"</span> + } + .... +} +</pre> + +<h4>Generating XML with 'xmlification'</h4> + +<p>This feature is inspired by the <code>htmlify</code> function used by +<a href="http://keplerproject.github.com/orbit/">Orbit</a> to simplify HTML generation, +except that no function environment magic is used; the <code>tags</code> function returns a +set of <em>constructors</em> for elements of the given tag names.</p> + + +<pre> +> nodes, node = xml.tags <span class="string">'nodes, node'</span> +> = node <span class="string">'alice'</span> +<node>alice</node> +> = nodes { node {id=<span class="string">'1'</span>,<span class="string">'alice'</span>}} +<nodes><node id=<span class="string">'1'</span>>alice</node></nodes> +</pre> + +<p>The flexibility of Lua tables is very useful here, since both the attributes and +the children of an element can be encoded naturally. The argument to these tag +constructors is either a single value (like a string) or a table where the +attributes are the named keys and the children are the array values.</p> + +<h4>Generating XML using Templates</h4> + +<p>A template is a little XML document which contains dollar-variables. The <code>subst</code> +method on a document is fed an array of tables containing values for these +variables. Note how the parent tag name is specified:</p> + + +<pre> +> templ = xml.parse <span class="string">"<node id='$id'>$name</node>"</span> +> = templ:subst {tag=<span class="string">'nodes'</span>, {id=<span class="number">1</span>,name=<span class="string">'alice'</span>},{id=<span class="number">2</span>,name=<span class="string">'john'</span>}} +<nodes><node id=<span class="string">'1'</span>>alice</node><node id=<span class="string">'2'</span>>john</node></nodes> +</pre> + +<p>Substitution is very related to <em>filtering</em> documents. One of the annoying things +about XML is that it is a document markup language first, and a data language +second. Standard parsers will assume you really care about all those extra +text elements. Consider this fragment, which has been changed by a five-year old:</p> + + +<pre> +T = <span class="string">[[ + <weather> + boops! + <current_conditions> + <condition data='$condition'/> + <temp_c data='$temp'/> + <bo>whoops!</bo> + </current_conditions> + </weather> +]]</span> +</pre> + +<p>Conformant parsers will give you text elements with the line feed after <code><current_conditions></code> +although it makes handling the data more irritating.</p> + + +<pre> +<span class="keyword">local</span> <span class="keyword">function</span> parse (str) + <span class="keyword">return</span> xml.parse(str,<span class="keyword">false</span>,<span class="keyword">true</span>) +<span class="keyword">end</span> +</pre> + +<p>Second argument means 'string, not file' and third argument means use the built-in +Lua parser (instead of LuaExpat if available) which <em>by default</em> is not interested in +keeping such strings.</p> + +<p>How to remove the string <code>boops!</code>? <code>clone</code> (also called <a href="../libraries/pl.data.html#filter">filter</a> when called as a +method) copies a LOM document. It can be passed a filter function, which is applied +to each string found. The powerful thing about this is that this function receives +structural information - the parent node, and whether this was a tag name, a text +element or a attribute name:</p> + + +<pre> +d = parse (T) +c = d:filter(<span class="keyword">function</span>(s,kind,parent) + <span class="global">print</span>(stringx.strip(s),kind,parent <span class="keyword">and</span> parent.tag <span class="keyword">or</span> <span class="string">'?'</span>) + <span class="keyword">if</span> kind == <span class="string">'*TEXT'</span> <span class="keyword">and</span> #parent > <span class="number">1</span> <span class="keyword">then</span> <span class="keyword">return</span> <span class="keyword">nil</span> <span class="keyword">end</span> + <span class="keyword">return</span> s +<span class="keyword">end</span>) +<span class="comment">---> +</span>weather *TAG ? +boops! *TEXT weather +current_conditions *TAG weather +condition *TAG current_conditions +$condition data condition +temp_c *TAG current_conditions +$temp data temp_c +bo *TAG current_conditions +whoops! *TEXT bo +</pre> + +<p>We can pull out 'boops' and not 'whoops' by discarding text elements which are not +the single child of an element.</p> + + + +<h4>Extracting Data using Templates</h4> + +<p>Matching goes in the opposite direction. We have a document, and would like to +extract values from it using a pattern.</p> + +<p>A common use of this is parsing the XML result of API queries. The +<a href="http://blog.programmableweb.com/2010/02/08/googles-secret-weather-api/">(undocumented and subsequently discontinued) Google Weather +API</a> is a +good example. Grabbing the result of +`http://www.google.com/ig/api?weather=Johannesburg,ZA" we get something like +this, after pretty-printing:</p> + + +<pre> +<xml_api_reply version=<span class="string">'1'</span>> + <weather module_id=<span class="string">'0'</span> tab_id=<span class="string">'0'</span> mobile_zipped=<span class="string">'1'</span> section=<span class="string">'0'</span> row=<span class="string">'0'</span> +</pre> + +<p>mobile_row='0'></p> + +<pre> +<forecast_information> + <city data=<span class="string">'Johannesburg, Gauteng'</span>/> + <postal_code data=<span class="string">'Johannesburg,ZA'</span>/> + <latitude_e6 data=<span class="string">''</span>/> + <longitude_e6 data=<span class="string">''</span>/> + <forecast_date data=<span class="string">'2010-10-02'</span>/> + <current_date_time data=<span class="string">'2010-10-02 18:30:00 +0000'</span>/> + <unit_system data=<span class="string">'US'</span>/> +</forecast_information> +<current_conditions> + <condition data=<span class="string">'Clear'</span>/> + <temp_f data=<span class="string">'75'</span>/> + <temp_c data=<span class="string">'24'</span>/> + <humidity data=<span class="string">'Humidity: 19%'</span>/> + <icon data=<span class="string">'/ig/images/weather/sunny.gif'</span>/> + <wind_condition data=<span class="string">'Wind: NW at 7 mph'</span>/> +</current_conditions> +<forecast_conditions> + <day_of_week data=<span class="string">'Sat'</span>/> + <low data=<span class="string">'60'</span>/> + <high data=<span class="string">'89'</span>/> + <icon data=<span class="string">'/ig/images/weather/sunny.gif'</span>/> + <condition data=<span class="string">'Clear'</span>/> +</forecast_conditions> +.... +/weather> +l_api_reply> +</pre> + +<p>Assume that the above XML has been read into <code>google</code>. The idea is to write a +pattern looking like a template, and use it to extract some values of interest:</p> + + +<pre> +t = <span class="string">[[ + <weather> + <current_conditions> + <condition data='$condition'/> + <temp_c data='$temp'/> + </current_conditions> + </weather> +]]</span> + +<span class="keyword">local</span> res, ret = google:match(t) +pretty.dump(res) +</pre> + +<p>And the output is:</p> + + +<pre> +{ + condition = <span class="string">"Clear"</span>, + temp = <span class="string">"24"</span> +} +</pre> + +<p>The <code>match</code> method can be passed a LOM document or some text, which will be +parsed first.</p> + +<p>But what if we need to extract values from repeated elements? Match templates may +contain 'array matches' which are enclosed in '{{..}}':</p> + + +<pre> +<weather> + {{<forecast_conditions> + <day_of_week data=<span class="string">'$day'</span>/> + <low data=<span class="string">'$low'</span>/> + <high data=<span class="string">'$high'</span>/> + <condition data=<span class="string">'$condition'</span>/> + </forecast_conditions>}} +</weather> +</pre> + +<p>And the match result is:</p> + + +<pre> +{ + { + low = <span class="string">"60"</span>, + high = <span class="string">"89"</span>, + day = <span class="string">"Sat"</span>, + condition = <span class="string">"Clear"</span>, + }, + { + low = <span class="string">"53"</span>, + high = <span class="string">"86"</span>, + day = <span class="string">"Sun"</span>, + condition = <span class="string">"Clear"</span>, + }, + { + low = <span class="string">"57"</span>, + high = <span class="string">"87"</span>, + day = <span class="string">"Mon"</span>, + condition = <span class="string">"Clear"</span>, + }, + { + low = <span class="string">"60"</span>, + high = <span class="string">"84"</span>, + day = <span class="string">"Tue"</span>, + condition = <span class="string">"Clear"</span>, + } +} +</pre> + +<p>With this array of tables, you can use <a href="../libraries/pl.tablex.html#">tablex</a> or <a href="../classes/pl.List.html#">List</a> +to reshape into the desired form, if you choose. Just as with reading a Unix password +file with <a href="../libraries/pl.config.html#">config</a>, you can make the array into a map of days to conditions using:</p> + + +<pre> +<span class="backtick"><a href="../libraries/pl.tablex.html#pairmap">tablex.pairmap</a></span>(<span class="string">'|k,v| v,v.day'</span>,conditions) +</pre> + +<p>(Here using the alternative string lambda option)</p> + +<p>However, xml matches can shape the structure of the output. By replacing the <code>day_of_week</code> +line of the template with <code><day_of_week data='$_'/></code> we get the same effect; <code>$_</code> is +a special symbol that means that this captured value (or simply <em>capture</em>) becomes the key.</p> + +<p>Note that <code>$NUMBER</code> means a numerical index, so +that <code>$1</code> is the first element of the resulting array, and so forth. You can mix +numbered and named captures, but it's strongly advised to make the numbered captures +form a proper array sequence (everything from <code>1</code> to <code>n</code> inclusive). <code>$0</code> has a +special meaning; if it is the only capture (<code>{[0]='foo'}</code>) then the table is +collapsed into 'foo'.</p> + + +<pre> +<weather> + {{<forecast_conditions> + <day_of_week data=<span class="string">'$_'</span>/> + <low data=<span class="string">'$1'</span>/> + <high data=<span class="string">'$2'</span>/> + <condition data=<span class="string">'$3'</span>/> + </forecast_conditions>}} +</weather> +</pre> + +<p>Now the result is:</p> + + +<pre> +{ + Tue = { + <span class="string">"60"</span>, + <span class="string">"84"</span>, + <span class="string">"Clear"</span> + }, + Sun = { + <span class="string">"53"</span>, + <span class="string">"86"</span>, + <span class="string">"Clear"</span> + }, + Sat = { + <span class="string">"60"</span>, + <span class="string">"89"</span>, + <span class="string">"Clear"</span> + }, + Mon = { + <span class="string">"57"</span>, + <span class="string">"87"</span>, + <span class="string">"Clear"</span> + } +} +</pre> + +<p>Applying matches to this config file poses another problem, because the actual +tags matched are themselves meaningful.</p> + + +<pre> +<config> + <alpha><span class="number">1.3</span></alpha> + <beta><span class="number">10</span></beta> + <name>bozo</name> +</config> +</pre> + +<p>So there are tag 'wildcards' which are element names ending with a hyphen.</p> + + +<pre> +<config> + {{<key->$value</key->}} +</config> +</pre> + +<p>You will then get <code>{{alpha='1.3'},...}</code>. The most convenient format would be +returned by this (note that <code>_-</code> behaves just like <code>$_</code>):</p> + + +<pre> +<config> + {{<_->$<span class="number">0</span></_->}} +</config> +</pre> + +<p>which would return <code>{alpha='1.3',beta='10',name='bozo'}</code>.</p> + +<p>We could play this game endlessly, and encode ways of converting captures, but +the scheme is complex enough, and it's easy to do the conversion later</p> + + +<pre> +<span class="keyword">local</span> numbers = {alpha=<span class="keyword">true</span>,beta=<span class="keyword">true</span>} +<span class="keyword">for</span> k,v <span class="keyword">in</span> <span class="global">pairs</span>(res) <span class="keyword">do</span> + <span class="keyword">if</span> numbers[v] <span class="keyword">then</span> res[k] = <span class="global">tonumber</span>(v) <span class="keyword">end</span> +<span class="keyword">end</span> +</pre> + +<h4>HTML Parsing</h4> + +<p>HTML is an unusually degenerate form of XML, and Dennis Schridde has contributed +a feature which makes parsing it easier. For instance, from the tests:</p> + + +<pre> +doc = xml.parsehtml <span class="string">[[ +<BODY> +Hello dolly<br> +HTML is <b>slack</b><br> +</BODY> +]]</span> + +asserteq(xml.<span class="global">tostring</span>(doc),<span class="string">[[ +<body> +Hello dolly<br/> +HTML is <b>slack</b><br/></body>]]</span>) +</pre> + +<p>That is, all tags are converted to lowercase, and empty HTML elements like <code>br</code> +are properly closed; attributes do not need to be quoted.</p> + +<p>Also, DOCTYPE directives and comments are skipped. For truly badly formed HTML, +this is not the tool for you!</p> + + + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/manual/07-functional.md.html b/Data/Libraries/Penlight/docs/manual/07-functional.md.html new file mode 100644 index 0000000..d4ca655 --- /dev/null +++ b/Data/Libraries/Penlight/docs/manual/07-functional.md.html @@ -0,0 +1,834 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Sequences">Sequences </a></li> +<li><a href="#Sequence_Wrappers">Sequence Wrappers </a></li> +<li><a href="#List_Comprehensions">List Comprehensions </a></li> +<li><a href="#Creating_Functions_from_Functions">Creating Functions from Functions </a></li> +<li><a href="#Placeholder_Expressions">Placeholder Expressions </a></li> +</ul> + + +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><strong>Functional Programming</strong></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + + +<h2>Functional Programming</h2> + +<p><a name="Sequences"></a></p> +<h3>Sequences</h3> + + +<p>A Lua iterator (in its simplest form) is a function which can be repeatedly +called to return a set of one or more values. The <code>for in</code> statement understands +these iterators, and loops until the function returns <code>nil</code>. There are standard +sequence adapters for tables in Lua (<a href="https://www.lua.org/manual/5.1/manual.html#pdf-ipairs">ipairs</a> and <a href="https://www.lua.org/manual/5.1/manual.html#pdf-pairs">pairs</a>), and <a href="https://www.lua.org/manual/5.1/manual.html#pdf-io.lines">io.lines</a> +returns an iterator over all the lines in a file. In the Penlight libraries, such +iterators are also called <em>sequences</em>. A sequence of single values (say from +<a href="https://www.lua.org/manual/5.1/manual.html#pdf-io.lines">io.lines</a>) is called <em>single-valued</em>, whereas the sequence defined by <a href="https://www.lua.org/manual/5.1/manual.html#pdf-pairs">pairs</a> is +<em>double-valued</em>.</p> + +<p><a href="../libraries/pl.seq.html#">pl.seq</a> provides a number of useful iterators, and some functions which operate +on sequences. At first sight this example looks like an attempt to write Python +in Lua, (with the sequence being inclusive):</p> + + +<pre> +> <span class="keyword">for</span> i <span class="keyword">in</span> seq.range(<span class="number">1</span>,<span class="number">4</span>) <span class="keyword">do</span> <span class="global">print</span>(i) <span class="keyword">end</span> +<span class="number">1</span> +<span class="number">2</span> +<span class="number">3</span> +<span class="number">4</span> +</pre> + +<p>But <a href="../libraries/pl.seq.html#range">range</a> is actually equivalent to Python's <code>xrange</code>, since it generates a +sequence, not a list. To get a list, use <code>seq.copy(seq.range(1,10))</code>, which +takes any single-value sequence and makes a table from the result. <a href="../libraries/pl.seq.html#list">seq.list</a> is +like <a href="https://www.lua.org/manual/5.1/manual.html#pdf-ipairs">ipairs</a> except that it does not give you the index, just the value.</p> + + +<pre> +> <span class="keyword">for</span> x <span class="keyword">in</span> seq.list {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>} <span class="keyword">do</span> <span class="global">print</span>(x) <span class="keyword">end</span> +<span class="number">1</span> +<span class="number">2</span> +<span class="number">3</span> +</pre> + +<p><a href="../libraries/pl.seq.html#enum">enum</a> takes a sequence and turns it into a double-valued sequence consisting of +a sequence number and the value, so <code>enum(list(ls))</code> is actually equivalent to +<a href="https://www.lua.org/manual/5.1/manual.html#pdf-ipairs">ipairs</a>. A more interesting example prints out a file with line numbers:</p> + + +<pre> +<span class="keyword">for</span> i,v <span class="keyword">in</span> seq.enum(<span class="global">io</span>.lines(fname)) <span class="keyword">do</span> <span class="global">print</span>(i..<span class="string">' '</span>..v) <span class="keyword">end</span> +</pre> + +<p>Sequences can be <em>combined</em>, either by 'zipping' them or by concatenating them.</p> + + +<pre> +> <span class="keyword">for</span> x,y <span class="keyword">in</span> seq.zip(l1,l2) <span class="keyword">do</span> <span class="global">print</span>(x,y) <span class="keyword">end</span> +<span class="number">10</span> <span class="number">1</span> +<span class="number">20</span> <span class="number">2</span> +<span class="number">30</span> <span class="number">3</span> +> <span class="keyword">for</span> x <span class="keyword">in</span> seq.splice(l1,l2) <span class="keyword">do</span> <span class="global">print</span>(x) <span class="keyword">end</span> +<span class="number">10</span> +<span class="number">20</span> +<span class="number">30</span> +<span class="number">1</span> +<span class="number">2</span> +<span class="number">3</span> +</pre> + +<p><a href="../libraries/pl.seq.html#printall">seq.printall</a> is useful for printing out single-valued sequences, and provides +some finer control over formating, such as a delimiter, the number of fields per +line, and a format string to use (@see string.format)</p> + + +<pre> +> seq.printall(seq.random(<span class="number">10</span>)) +<span class="number">0.0012512588885159</span> <span class="number">0.56358531449324</span> <span class="number">0.19330423902097</span> .... +> seq.printall(seq.random(<span class="number">10</span>), <span class="string">','</span>, <span class="number">4</span>, <span class="string">'%4.2f'</span>) +<span class="number">0.17</span>,<span class="number">0.86</span>,<span class="number">0.71</span>,<span class="number">0.51</span> +<span class="number">0.30</span>,<span class="number">0.01</span>,<span class="number">0.09</span>,<span class="number">0.36</span> +<span class="number">0.15</span>,<span class="number">0.17</span>, +</pre> + +<p><a href="../libraries/pl.seq.html#map">map</a> will apply a function to a sequence.</p> + + +<pre> +> seq.printall(seq.map(<span class="global">string</span>.upper, {<span class="string">'one'</span>,<span class="string">'two'</span>})) +ONE TWO +> seq.printall(seq.map(<span class="string">'+'</span>, {<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>}, <span class="number">1</span>)) +<span class="number">11</span> <span class="number">21</span> <span class="number">31</span> +</pre> + +<p><a href="../libraries/pl.seq.html#filter">filter</a> will filter a sequence using a boolean function (often called a +<em>predicate</em>). For instance, this code only prints lines in a file which are +composed of digits:</p> + + +<pre> +<span class="keyword">for</span> l <span class="keyword">in</span> seq.filter(<span class="global">io</span>.lines(file), stringx.isdigit) <span class="keyword">do</span> <span class="global">print</span>(l) <span class="keyword">end</span> +</pre> + +<p>The following returns a table consisting of all the positive values in the +original table (equivalent to <code>tablex.filter(ls, '>', 0)</code>)</p> + + +<pre> +ls = seq.copy(seq.filter(ls, <span class="string">'>'</span>, <span class="number">0</span>)) +</pre> + +<p>We're already encounted <a href="../libraries/pl.seq.html#sum">seq.sum</a> when discussing <a href="../libraries/pl.input.html#numbers">input.numbers</a>. This can also +be expressed with <a href="../libraries/pl.seq.html#reduce">seq.reduce</a>:</p> + + +<pre> +> seq.reduce(<span class="keyword">function</span>(x,y) <span class="keyword">return</span> x + y <span class="keyword">end</span>, seq.list{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) +<span class="number">10</span> +</pre> + +<p><a href="../libraries/pl.seq.html#reduce">seq.reduce</a> applies a binary function in a recursive fashion, so that:</p> + + +<pre> +reduce(op,{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}) => op(<span class="number">1</span>,reduce(op,{<span class="number">2</span>,<span class="number">3</span>}) => op(<span class="number">1</span>,op(<span class="number">2</span>,<span class="number">3</span>)) +</pre> + +<p>it's now possible to easily generate other cumulative operations; the standard +operations declared in <a href="../libraries/pl.operator.html#">pl.operator</a> are useful here:</p> + + +<pre> +> ops = <span class="global">require</span> <span class="string">'pl.operator'</span> +> <span class="comment">-- can also say '*' instead of ops.mul +</span>> = seq.reduce(ops.mul,input.numbers <span class="string">'1 2 3 4'</span>) +<span class="number">24</span> +</pre> + +<p>There are functions to extract statistics from a sequence of numbers:</p> + + +<pre> +> l1 = List {<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>} +> l2 = List {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>} +> = seq.minmax(l1) +<span class="number">10</span> <span class="number">30</span> +> = seq.sum(l1) +<span class="number">60</span> <span class="number">3</span> +</pre> + +<p>It is common to get sequences where values are repeated, say the words in a file. +<a href="../libraries/pl.seq.html#count_map">count_map</a> will take such a sequence and count the values, returning a table +where the <em>keys</em> are the unique values, and the value associated with each key is +the number of times they occurred:</p> + + +<pre> +> t = seq.count_map {<span class="string">'one'</span>,<span class="string">'fred'</span>,<span class="string">'two'</span>,<span class="string">'one'</span>,<span class="string">'two'</span>,<span class="string">'two'</span>} +> = t +{one=<span class="number">2</span>,fred=<span class="number">1</span>,two=<span class="number">3</span>} +</pre> + +<p>This will also work on numerical sequences, but you cannot expect the result to +be a proper list, i.e. having no 'holes'. Instead, you always need to use <a href="https://www.lua.org/manual/5.1/manual.html#pdf-pairs">pairs</a> +to iterate over the result - note that there is a hole at index 5:</p> + + +<pre> +> t = seq.count_map {<span class="number">1</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">6</span>} +> <span class="keyword">for</span> k,v <span class="keyword">in</span> <span class="global">pairs</span>(t) <span class="keyword">do</span> <span class="global">print</span>(k,v) <span class="keyword">end</span> +<span class="number">1</span> <span class="number">1</span> +<span class="number">2</span> <span class="number">4</span> +<span class="number">3</span> <span class="number">1</span> +<span class="number">4</span> <span class="number">2</span> +<span class="number">6</span> <span class="number">1</span> +</pre> + +<p><code>unique</code> uses <a href="../libraries/pl.seq.html#count_map">count_map</a> to return a list of the unique values, that is, just +the keys of the resulting table.</p> + +<p><a href="../libraries/pl.seq.html#last">last</a> turns a single-valued sequence into a double-valued sequence with the +current value and the last value:</p> + + +<pre> +> <span class="keyword">for</span> current,last <span class="keyword">in</span> seq.last {<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>,<span class="number">40</span>} <span class="keyword">do</span> <span class="global">print</span> (current,last) <span class="keyword">end</span> +<span class="number">20</span> <span class="number">10</span> +<span class="number">30</span> <span class="number">20</span> +<span class="number">40</span> <span class="number">30</span> +</pre> + +<p>This makes it easy to do things like identify repeated lines in a file, or +construct differences between values. <a href="../libraries/pl.seq.html#filter">filter</a> can handle double-valued sequences +as well, so one could filter such a sequence to only return cases where the +current value is less than the last value by using <a href="../libraries/pl.operator.html#lt">operator.lt</a> or just '<'. +This code then copies the resulting code into a table.</p> + + +<pre> +> ls = {<span class="number">10</span>,<span class="number">9</span>,<span class="number">10</span>,<span class="number">3</span>} +> = seq.copy(seq.filter(seq.last(s),<span class="string">'<'</span>)) +{<span class="number">9</span>,<span class="number">3</span>} +</pre> + +<p><a name="Sequence_Wrappers"></a></p> +<h3>Sequence Wrappers</h3> + +<p>The functions in <a href="../libraries/pl.seq.html#">pl.seq</a> cover the common patterns when dealing with sequences, +but chaining these functions together can lead to ugly code. Consider the last +example of the previous section; <a href="../libraries/pl.seq.html#">seq</a> is repeated three times and the resulting +expression has to be read right-to-left. The first issue can be helped by local +aliases, so that the expression becomes <code>copy(filter(last(s),'<'))</code> but the +second issue refers to the somewhat unnatural order of functional application. +We tend to prefer reading operations from left to right, which is one reason why +object-oriented notation has become popular. Sequence adapters allow this +expression to be written like so:</p> + + +<pre> +seq(s):last():filter(<span class="string">'<'</span>):copy() +</pre> + +<p>With this notation, the operation becomes a chain of method calls running from +left to right.</p> + +<p>'Sequence' is not a basic Lua type, they are generally functions or callable +objects. The expression <code>seq(s)</code> wraps a sequence in a <em>sequence wrapper</em>, which +is an object which understands all the functions in <a href="../libraries/pl.seq.html#">pl.seq</a> as methods. This +object then explicitly represents sequences.</p> + +<p>As a special case, the constructor (which is when you call the table <a href="../libraries/pl.seq.html#">seq</a>) will +make a wrapper for a plain list-like table. Here we apply the length operator to +a sequence of strings, and print them out.</p> + + +<pre> +> seq{<span class="string">'one'</span>,<span class="string">'tw'</span>,<span class="string">'t'</span>} :map <span class="string">'#'</span> :printall() +<span class="number">3</span> <span class="number">2</span> <span class="number">1</span> +</pre> + +<p>As a convenience, there is a function <a href="../libraries/pl.seq.html#lines">seq.lines</a> which behaves just like +<a href="https://www.lua.org/manual/5.1/manual.html#pdf-io.lines">io.lines</a> except it wraps the result as an explicit sequence type. This takes +the first 10 lines from standard input, makes it uppercase, turns it into a +sequence with a count and the value, glues these together with the concatenation +operator, and finally prints out the sequence delimited by a newline.</p> + + +<pre> +seq.lines():take(<span class="number">10</span>):upper():enum():map(<span class="string">'..'</span>):printall <span class="string">'\n'</span> +</pre> + +<p>Note the method <code>upper</code>, which is not a <a href="../libraries/pl.seq.html#">seq</a> function. if an unknown method is +called, sequence wrappers apply that method to all the values in the sequence +(this is implicit use of <a href="../libraries/pl.seq.html#mapmethod">mapmethod</a>)</p> + +<p>It is straightforward to create custom sequences that can be used in this way. On +Unix, <code>/dev/random</code> gives you an <em>endless</em> sequence of random bytes, so we use +<a href="../libraries/pl.seq.html#take">take</a> to limit the sequence, and then <a href="../libraries/pl.seq.html#map">map</a> to scale the result into the desired +range. The key step is to use <a href="../libraries/pl.seq.html#">seq</a> to wrap the iterator function:</p> + + +<pre> +<span class="comment">-- random.lua +</span><span class="keyword">local</span> seq = <span class="global">require</span> <span class="string">'pl.seq'</span> + +<span class="keyword">function</span> dev_random() + <span class="keyword">local</span> f = <span class="global">io</span>.open(<span class="string">'/dev/random'</span>) + <span class="keyword">local</span> byte = <span class="global">string</span>.byte + <span class="keyword">return</span> seq(<span class="keyword">function</span>() + <span class="comment">-- read two bytes into a string and convert into a 16-bit number +</span> <span class="keyword">local</span> s = f:read(<span class="number">2</span>) + <span class="keyword">return</span> byte(s,<span class="number">1</span>) + <span class="number">256</span>*byte(s,<span class="number">2</span>) + <span class="keyword">end</span>) +<span class="keyword">end</span> + +<span class="comment">-- print 10 random numbers from 0 to 1 ! +</span>dev_random():take(<span class="number">10</span>):map(<span class="string">'%'</span>,<span class="number">100</span>):map(<span class="string">'/'</span>,<span class="number">100</span>):printall <span class="string">','</span> +</pre> + +<p>Another Linux one-liner depends on the <code>/proc</code> filesystem and makes a list of all +the currently running processes:</p> + + +<pre> +pids = seq(lfs.dir <span class="string">'/proc'</span>):filter(stringx.isdigit):map(<span class="global">tonumber</span>):copy() +</pre> + +<p>This version of Penlight has an experimental feature which relies on the fact +that <em>all</em> Lua types can have metatables, including functions. This makes +<em>implicit sequence wrapping</em> possible:</p> + + +<pre> +> seq.import() +> seq.random(<span class="number">5</span>):printall(<span class="string">','</span>,<span class="number">5</span>,<span class="string">'%4.1f'</span>) + <span class="number">0.0</span>, <span class="number">0.1</span>, <span class="number">0.4</span>, <span class="number">0.1</span>, <span class="number">0.2</span> +</pre> + +<p>This avoids the awkward <code>seq(seq.random(5))</code> construction. Or the iterator can +come from somewhere else completely:</p> + + +<pre> +> (<span class="string">'one two three'</span>):gfind(<span class="string">'%a+'</span>):printall(<span class="string">','</span>) +one,two,three, +</pre> + +<p>After <code>seq.import</code>, it is no longer necessary to explicitly wrap sequence +functions.</p> + +<p>But there is a price to pay for this convenience. <em>Every</em> function is affected, +so that any function can be used, appropriate or not:</p> + + +<pre> +> <span class="global">math</span>.sin:printall() +..seq.lua:<span class="number">287</span>: bad argument #<span class="number">1</span> to <span class="string">'(for generator)'</span> (number expected, got <span class="keyword">nil</span>) +> a = <span class="global">tostring</span> +> = a:find(<span class="string">' '</span>) +<span class="keyword">function</span>: <span class="number">0042</span>C920 +</pre> + +<p>What function is returned? It's almost certain to be something that makes no +sense in the current context. So implicit sequences may make certain kinds of +programming mistakes harder to catch - they are best used for interactive +exploration and small scripts.</p> + +<p><a id="comprehensions"/></p> + +<p><a name="List_Comprehensions"></a></p> +<h3>List Comprehensions</h3> + +<p>List comprehensions are a compact way to create tables by specifying their +elements. In Python, you can say this:</p> + + +<pre> +ls = [x <span class="keyword">for</span> x <span class="keyword">in</span> range(<span class="number">5</span>)] # == [<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>] +</pre> + +<p>In Lua, using <a href="../libraries/pl.comprehension.html#">pl.comprehension</a>:</p> + + +<pre> +> C = <span class="global">require</span>(<span class="string">'pl.comprehension'</span>).new() +> = C (<span class="string">'x for x=1,10'</span>) () +{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>,<span class="number">10</span>} +</pre> + +<p><code>C</code> is a function which compiles a list comprehension <em>string</em> into a <em>function</em>. +In this case, the function has no arguments. The parentheses are redundant for a +function taking a string argument, so this works as well:</p> + + +<pre> +> = C <span class="string">'x^2 for x=1,4'</span> () +{<span class="number">1</span>,<span class="number">4</span>,<span class="number">9</span>,<span class="number">16</span>} +> = C <span class="string">'{x,x^2} for x=1,4'</span> () +{{<span class="number">1</span>,<span class="number">1</span>},{<span class="number">2</span>,<span class="number">4</span>},{<span class="number">3</span>,<span class="number">9</span>},{<span class="number">4</span>,<span class="number">16</span>}} +</pre> + +<p>Note that the expression can be <em>any</em> function of the variable <code>x</code>!</p> + +<p>The basic syntax so far is <code><expr> for <set></code>, where <code><set></code> can be anything that +the Lua <code>for</code> statement understands. <code><set></code> can also just be the variable, in +which case the values will come from the <em>argument</em> of the comprehension. Here +I'm emphasizing that a comprehension is a function which can take a list argument:</p> + + +<pre> +> = C <span class="string">'2*x for x'</span> {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>} +{<span class="number">2</span>,<span class="number">4</span>,<span class="number">6</span>} +> dbl = C <span class="string">'2*x for x'</span> +> = dbl {<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>} +{<span class="number">20</span>,<span class="number">40</span>,<span class="number">60</span>} +</pre> + +<p>Here is a somewhat more explicit way of saying the same thing; <code>_1</code> is a +<em>placeholder</em> refering to the <em>first</em> argument passed to the comprehension.</p> + + +<pre> +> = C <span class="string">'2*x for _,x in pairs(_1)'</span> {<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>} +{<span class="number">20</span>,<span class="number">40</span>,<span class="number">60</span>} +> = C <span class="string">'_1(x) for x'</span>(<span class="global">tostring</span>,{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) +{<span class="string">'1'</span>,<span class="string">'2'</span>,<span class="string">'3'</span>,<span class="string">'4'</span>} +</pre> + +<p>This extended syntax is useful when you wish to collect the result of some +iterator, such as <a href="https://www.lua.org/manual/5.1/manual.html#pdf-io.lines">io.lines</a>. This comprehension creates a function which creates +a table of all the lines in a file:</p> + + +<pre> +> f = <span class="global">io</span>.open(<span class="string">'array.lua'</span>) +> lines = C <span class="string">'line for line in _1:lines()'</span> (f) +> = #lines +<span class="number">118</span> +</pre> + +<p>There are a number of functions that may be applied to the result of a +comprehension:</p> + + +<pre> +> = C <span class="string">'min(x for x)'</span> {<span class="number">1</span>,<span class="number">44</span>,<span class="number">0</span>} +<span class="number">0</span> +> = C <span class="string">'max(x for x)'</span> {<span class="number">1</span>,<span class="number">44</span>,<span class="number">0</span>} +<span class="number">44</span> +> = C <span class="string">'sum(x for x)'</span> {<span class="number">1</span>,<span class="number">44</span>,<span class="number">0</span>} +<span class="number">45</span> +</pre> + +<p>(These are equivalent to a reduce operation on a list.)</p> + +<p>After the <code>for</code> part, there may be a condition, which filters the output. This +comprehension collects the even numbers from a list:</p> + + +<pre> +> = C <span class="string">'x for x if x % 2 == 0'</span> {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>} +{<span class="number">2</span>,<span class="number">4</span>} +</pre> + +<p>There may be a number of <code>for</code> parts:</p> + + +<pre> +> = C <span class="string">'{x,y} for x = 1,2 for y = 1,2'</span> () +{{<span class="number">1</span>,<span class="number">1</span>},{<span class="number">1</span>,<span class="number">2</span>},{<span class="number">2</span>,<span class="number">1</span>},{<span class="number">2</span>,<span class="number">2</span>}} +> = C <span class="string">'{x,y} for x for y'</span> ({<span class="number">1</span>,<span class="number">2</span>},{<span class="number">10</span>,<span class="number">20</span>}) +{{<span class="number">1</span>,<span class="number">10</span>},{<span class="number">1</span>,<span class="number">20</span>},{<span class="number">2</span>,<span class="number">10</span>},{<span class="number">2</span>,<span class="number">20</span>}} +</pre> + +<p>These comprehensions are useful when dealing with functions of more than one +variable, and are not so easily achieved with the other Penlight functional forms.</p> + +<p><a id="func"/></p> + +<p><a name="Creating_Functions_from_Functions"></a></p> +<h3>Creating Functions from Functions</h3> + + +<p>Lua functions may be treated like any other value, although of course you cannot +multiply or add them. One operation that makes sense is <em>function composition</em>, +which chains function calls (so <code>(f * g)(x)</code> is <code>f(g(x))</code>.)</p> + + +<pre> +> func = <span class="global">require</span> <span class="string">'pl.func'</span> +> printf = func.compose(<span class="global">io</span>.write,<span class="global">string</span>.format) +> printf(<span class="string">"hello %s\n"</span>,<span class="string">'world'</span>) +hello world +<span class="keyword">true</span> +</pre> + +<p>Many functions require you to pass a function as an argument, say to apply to all +values of a sequence or as a callback. Often useful functions have the wrong +number of arguments. So there is a need to construct a function of one argument +from one of two arguments, <em>binding</em> the extra argument to a given value.</p> + +<p><em>partial application</em> takes a function of n arguments and returns a function of n-1 +arguments where the first argument is bound to some value:</p> + + +<pre> +> p2 = func.bind1(<span class="global">print</span>,<span class="string">'start>'</span>) +> p2(<span class="string">'hello'</span>,<span class="number">2</span>) +start> hello <span class="number">2</span> +> ops = <span class="global">require</span> <span class="string">'pl.operator'</span> +> = tablex.filter({<span class="number">1</span>,-<span class="number">2</span>,<span class="number">10</span>,-<span class="number">1</span>,<span class="number">2</span>},bind1(ops.gt,<span class="number">0</span>)) +{-<span class="number">2</span>,-<span class="number">1</span>} +> tablex.filter({<span class="number">1</span>,-<span class="number">2</span>,<span class="number">10</span>,-<span class="number">1</span>,<span class="number">2</span>},bind1(ops.le,<span class="number">0</span>)) +{<span class="number">1</span>,<span class="number">10</span>,<span class="number">2</span>} +</pre> + +<p>The last example unfortunately reads backwards, because <a href="../libraries/pl.func.html#bind1">bind1</a> alway binds the +first argument! Also unfortunately, in my youth I confused 'currying' with +'partial application', so the old name for <a href="../libraries/pl.func.html#bind1">bind1</a> is <code>curry</code> - this alias still exists.</p> + +<p>This is a specialized form of function argument binding. Here is another way +to say the <a href="https://www.lua.org/manual/5.1/manual.html#pdf-print">print</a> example:</p> + + +<pre> +> p2 = func.bind(<span class="global">print</span>,<span class="string">'start>'</span>,func._1,func._2) +> p2(<span class="string">'hello'</span>,<span class="number">2</span>) +start> hello <span class="number">2</span> +</pre> + +<p>where <code>_1</code> and <code>_2</code> are <em>placeholder variables</em>, corresponding to the first and +second argument respectively.</p> + +<p>Having <a href="../libraries/pl.func.html#">func</a> all over the place is distracting, so it's useful to pull all of +<a href="../libraries/pl.func.html#">pl.func</a> into the local context. Here is the filter example, this time the right +way around:</p> + + +<pre> +> utils.import <span class="string">'pl.func'</span> +> tablex.filter({<span class="number">1</span>,-<span class="number">2</span>,<span class="number">10</span>,-<span class="number">1</span>,<span class="number">2</span>},bind(ops.gt, _1, <span class="number">0</span>)) +{<span class="number">1</span>,<span class="number">10</span>,<span class="number">2</span>} +</pre> + +<p><a href="../libraries/pl.tablex.html#merge">tablex.merge</a> does a general merge of two tables. This example shows the +usefulness of binding the last argument of a function.</p> + + +<pre> +> S1 = {john=<span class="number">27</span>, jane=<span class="number">31</span>, mary=<span class="number">24</span>} +> S2 = {jane=<span class="number">31</span>, jones=<span class="number">50</span>} +> intersection = bind(tablex.merge, _1, _2, <span class="keyword">false</span>) +> union = bind(tablex.merge, _1, _2, <span class="keyword">true</span>) +> = intersection(S1,S2) +{jane=<span class="number">31</span>} +> = union(S1,S2) +{mary=<span class="number">24</span>,jane=<span class="number">31</span>,john=<span class="number">27</span>,jones=<span class="number">50</span>} +</pre> + +<p>When using <a href="../libraries/pl.func.html#bind">bind</a> with <a href="https://www.lua.org/manual/5.1/manual.html#pdf-print">print</a>, we got a function of precisely two arguments, +whereas we really want our function to use varargs like <a href="https://www.lua.org/manual/5.1/manual.html#pdf-print">print</a>. This is the role +of <code>_0</code>:</p> + + +<pre> +> _DEBUG = <span class="keyword">true</span> +> p = bind(<span class="global">print</span>,<span class="string">'start>'</span>, _0) +<span class="keyword">return</span> <span class="keyword">function</span> (fn,_v1) + <span class="keyword">return</span> <span class="keyword">function</span>(...) <span class="keyword">return</span> fn(_v1,...) <span class="keyword">end</span> +<span class="keyword">end</span> + +> p(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>) +start> <span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span> +</pre> + +<p>I've turned on the global <code>_DEBUG</code> flag, so that the function generated is +printed out. It is actually a function which <em>generates</em> the required function; +the first call <em>binds the value</em> of <code>_v1</code> to 'start>'.</p> + +<p><a name="Placeholder_Expressions"></a></p> +<h3>Placeholder Expressions</h3> + +<p>A common pattern in Penlight is a function which applies another function to all +elements in a table or a sequence, such as <a href="../libraries/pl.tablex.html#map">tablex.map</a> or <a href="../libraries/pl.seq.html#filter">seq.filter</a>. Lua does +anonymous functions well, although they can be a bit tedious to type:</p> + + +<pre> +> = tablex.map(<span class="keyword">function</span>(x) <span class="keyword">return</span> x*x <span class="keyword">end</span>, {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) +{<span class="number">1</span>,<span class="number">4</span>,<span class="number">9</span>,<span class="number">16</span>} +</pre> + +<p><a href="../libraries/pl.func.html#">pl.func</a> allows you to define <em>placeholder expressions</em>, which can cut down on +the typing required, and also make your intent clearer. First, we bring contents +of <a href="../libraries/pl.func.html#">pl.func</a> into our context, and then supply an expression using placeholder +variables, such as <code>_1</code>,<code>_2</code>,etc. (C++ programmers will recognize this from the +Boost libraries.)</p> + + +<pre> +> utils.import <span class="string">'pl.func'</span> +> = tablex.map(_1*_1, {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) +{<span class="number">1</span>,<span class="number">4</span>,<span class="number">9</span>,<span class="number">16</span>} +</pre> + +<p>Functions of up to 5 arguments can be generated.</p> + + +<pre> +> = tablex.map2(_1+_2,{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>}, {<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>}) +{<span class="number">11</span>,<span class="number">22</span>,<span class="number">33</span>} +</pre> + +<p>These expressions can use arbitrary functions, altho they must first be +registered with the functional library. <a href="../libraries/pl.func.html#register">func.register</a> brings in a single +function, and <a href="../libraries/pl.func.html#import">func.import</a> brings in a whole table of functions, such as <a href="https://www.lua.org/manual/5.1/manual.html#5.6">math</a>.</p> + + +<pre> +> sin = register(<span class="global">math</span>.sin) +> = tablex.map(sin(_1), {<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) +{<span class="number">0.8414709848079</span>,<span class="number">0.90929742682568</span>,<span class="number">0.14112000805987</span>,-<span class="number">0.75680249530793</span>} +> import <span class="string">'math'</span> +> = tablex.map(cos(<span class="number">2</span>*_1),{<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>}) +{-<span class="number">0.41614683654714</span>,-<span class="number">0.65364362086361</span>,<span class="number">0.96017028665037</span>,-<span class="number">0.14550003380861</span>} +</pre> + +<p>A common operation is calling a method of a set of objects:</p> + + +<pre> +> = tablex.map(_1:sub(<span class="number">1</span>,<span class="number">1</span>), {<span class="string">'one'</span>,<span class="string">'four'</span>,<span class="string">'x'</span>}) +{<span class="string">'o'</span>,<span class="string">'f'</span>,<span class="string">'x'</span>} +</pre> + +<p>There are some restrictions on what operators can be used in PEs. For instance, +because the <code>__len</code> metamethod cannot be overriden by plain Lua tables, we need +to define a special function to express `#_1':</p> + + +<pre> +> = tablex.map(Len(_1), {<span class="string">'one'</span>,<span class="string">'four'</span>,<span class="string">'x'</span>}) +{<span class="number">3</span>,<span class="number">4</span>,<span class="number">1</span>} +</pre> + +<p>Likewise for comparison operators, which cannot be overloaded for <em>different</em> +types, and thus also have to be expressed as a special function:</p> + + +<pre> +> = tablex.filter(Gt(_1,<span class="number">0</span>), {<span class="number">1</span>,-<span class="number">1</span>,<span class="number">2</span>,<span class="number">4</span>,-<span class="number">3</span>}) +{<span class="number">1</span>,<span class="number">2</span>,<span class="number">4</span>} +</pre> + +<p>It is useful to express the fact that a function returns multiple values. For +instance, <a href="../libraries/pl.tablex.html#pairmap">tablex.pairmap</a> expects a function that will be called with the key +and the value, and returns the new value and the key, in that order.</p> + + +<pre> +> = pairmap(Args(_2,_1:upper()),{fred=<span class="number">1</span>,alice=<span class="number">2</span>}) +{ALICE=<span class="number">2</span>,FRED=<span class="number">1</span>} +</pre> + +<p>PEs cannot contain <code>nil</code> values, since PE function arguments are represented as +an array. Instead, a special value called <code>Nil</code> is provided. So say +<code>_1:f(Nil,1)</code> instead of <code>_1:f(nil,1)</code>.</p> + +<p>A placeholder expression cannot be automatically used as a Lua function. The +technical reason is that the call operator must be overloaded to construct +function calls like <code>_1(1)</code>. If you want to force a PE to return a function, use +<a href="../libraries/pl.func.html#I">func.I</a>.</p> + + +<pre> +> = tablex.map(_1(<span class="number">10</span>),{I(<span class="number">2</span>*_1),I(_1*_1),I(_1+<span class="number">2</span>)}) +{<span class="number">20</span>,<span class="number">100</span>,<span class="number">12</span>} +</pre> + +<p>Here we make a table of functions taking a single argument, and then call them +all with a value of 10.</p> + +<p>The essential idea with PEs is to 'quote' an expression so that it is not +immediately evaluated, but instead turned into a function that can be applied +later to some arguments. The basic mechanism is to wrap values and placeholders +so that the usual Lua operators have the effect of building up an <em>expression +tree</em>. (It turns out that you can do <em>symbolic algebra</em> using PEs, see +<a href="../examples/symbols.lua.html#">symbols.lua</a> in the examples directory, and its test runner <code>testsym.lua</code>, which +demonstrates symbolic differentiation.)</p> + +<p>The rule is that if any operator has a PE operand, the result will be quoted. +Sometimes we need to quote things explicitly. For instance, say we want to pass a +function to a filter that must return true if the element value is in a set. +<code>set[_1]</code> is the obvious expression, but it does not give the desired result, +since it evaluates directly, giving <code>nil</code>. Indexing works differently than a +binary operation like addition (set+_1 <em>is</em> properly quoted) so there is a need +for an explicit quoting or wrapping operation. This is the job of the <code>_</code> +function; the PE in this case should be <code>_(set)[_1]</code>. This works for functions +as well, as a convenient alternative to registering functions: <code>_(math.sin)(_1)</code>. +This is equivalent to using the `lines' method:</p> + + +<pre> +<span class="keyword">for</span> line <span class="keyword">in</span> I(_(f):read()) <span class="keyword">do</span> <span class="global">print</span>(line) <span class="keyword">end</span> +</pre> + +<p>Now this will work for <em>any</em> 'file-like' object which which has a <code>read</code> method +returning the next line. If you had a LuaSocket client which was being 'pushed' +by lines sent from a server, then <code>_(s):receive '*l'</code> would create an iterator +for accepting input. These forms can be convenient for adapting your data flow so +that it can be passed to the sequence functions in `pl.seq'.</p> + +<p>Placeholder expressions can be mixed with sequence wrapper expressions. +<a href="../libraries/pl.lexer.html#lua">lexer.lua</a> will give us a double-valued sequence of tokens, where the first +value is a type, and the second is a value. We filter out only the values where +the type is 'iden', extract the actual value using <code>map</code>, get the unique values +and finally copy to a list.</p> + + +<pre> +> str = <span class="string">'for i=1,10 do for j = 1,10 do print(i,j) end end'</span> +> = seq(lexer.lua(str)):filter(<span class="string">'=='</span>,<span class="string">'iden'</span>):map(_2):unique():copy() +{i,<span class="global">print</span>,j} +</pre> + +<p>This is a particularly intense line (and I don't always suggest making everything +a one-liner!); the key is the behaviour of <code>map</code>, which will take both values of +the sequence, so <code>_2</code> returns the value part. (Since <code>filter</code> here takes extra +arguments, it only operates on the type values.)</p> + +<p>There are some performance considerations to using placeholder expressions. +Instantiating a PE requires constructing and compiling a function, which is not +such a fast operation. So to get best performance, factor out PEs from loops like +this;</p> + + +<pre> +<span class="keyword">local</span> fn = I(_1:f() + _2:g()) +<span class="keyword">for</span> i = <span class="number">1</span>,n <span class="keyword">do</span> + res[i] = tablex.map2(fn,first[i],second[i]) +<span class="keyword">end</span> +</pre> + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/manual/08-additional.md.html b/Data/Libraries/Penlight/docs/manual/08-additional.md.html new file mode 100644 index 0000000..d13ac6e --- /dev/null +++ b/Data/Libraries/Penlight/docs/manual/08-additional.md.html @@ -0,0 +1,815 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Simple_Input_Patterns">Simple Input Patterns </a></li> +<li><a href="#Command_line_Programs_with_Lapp">Command-line Programs with Lapp </a></li> +<li><a href="#Simple_Test_Framework">Simple Test Framework </a></li> +</ul> + + +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><strong>Additional Libraries</strong></li> + <li><a href="../manual/09-discussion.md.html">Technical Choices</a></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + + +<h2>Additional Libraries</h2> + +<p>Libraries in this section are no longer considered to be part of the Penlight +core, but still provide specialized functionality when needed.</p> + +<p><a id="sip"/></p> + +<p><a name="Simple_Input_Patterns"></a></p> +<h3>Simple Input Patterns</h3> + +<p>Lua string pattern matching is very powerful, and usually you will not need a +traditional regular expression library. Even so, sometimes Lua code ends up +looking like Perl, which happens because string patterns are not always the +easiest things to read, especially for the casual reader. Here is a program +which needs to understand three distinct date formats:</p> + + +<pre> +<span class="comment">-- parsing dates using Lua string patterns +</span>months={Jan=<span class="number">1</span>,Feb=<span class="number">2</span>,Mar=<span class="number">3</span>,Apr=<span class="number">4</span>,May=<span class="number">5</span>,Jun=<span class="number">6</span>, +Jul=<span class="number">7</span>,Aug=<span class="number">8</span>,Sep=<span class="number">9</span>,Oct=<span class="number">10</span>,Nov=<span class="number">11</span>,Dec=<span class="number">12</span>} + +<span class="keyword">function</span> check_and_process(d,m,y) + d = <span class="global">tonumber</span>(d) + m = <span class="global">tonumber</span>(m) + y = <span class="global">tonumber</span>(y) + .... +<span class="keyword">end</span> + +<span class="keyword">for</span> line <span class="keyword">in</span> f:lines() <span class="keyword">do</span> + <span class="comment">-- ordinary (English) date format +</span> <span class="keyword">local</span> d,m,y = line:match(<span class="string">'(%d+)/(%d+)/(%d+)'</span>) + <span class="keyword">if</span> d <span class="keyword">then</span> + check_and_process(d,m,y) + <span class="keyword">else</span> <span class="comment">-- ISO date?? +</span> y,m,d = line:match(<span class="string">'(%d+)%-(%d+)%-(%d+)'</span>) + <span class="keyword">if</span> y <span class="keyword">then</span> + check_and_process(d,m,y) + <span class="keyword">else</span> <span class="comment">-- <day> <month-name> <year>? +</span> d,mm,y = line:match(<span class="string">'%(d+)%s+(%a+)%s+(%d+)'</span>) + m = months[mm] + check_and_process(d,m,y) + <span class="keyword">end</span> + <span class="keyword">end</span> +<span class="keyword">end</span> +</pre> + +<p>These aren't particularly difficult patterns, but already typical issues are +appearing, such as having to escape '-'. Also, <a href="https://www.lua.org/manual/5.1/manual.html#pdf-string.match">string.match</a> returns its +captures, so that we're forced to use a slightly awkward nested if-statement.</p> + +<p>Verification issues will further cloud the picture, since regular expression +people try to enforce constraints (like year cannot be more than four digits) +using regular expressions, on the usual grounds that you shouldn't stop using a +hammer when you are enjoying yourself.</p> + +<p><a href="../libraries/pl.sip.html#">pl.sip</a> provides a simple, intuitive way to detect patterns in strings and +extract relevant parts.</p> + + +<pre> +> sip = <span class="global">require</span> <span class="string">'pl.sip'</span> +> dump = <span class="global">require</span>(<span class="string">'pl.pretty'</span>).dump +> res = {} +> c = sip.compile <span class="string">'ref=$S{file}:$d{line}'</span> +> = c(<span class="string">'ref=hello.c:10'</span>,res) +<span class="keyword">true</span> +> dump(res) +{ + line = <span class="number">10</span>, + file = <span class="string">"hello.c"</span> +} +> = c(<span class="string">'ref=long name, no line'</span>,res) +<span class="keyword">false</span> +</pre> + +<p><a href="../libraries/pl.sip.html#compile">sip.compile</a> creates a pattern matcher function, which takes a string and a +table as arguments. If the string matches the pattern, then <code>true</code> is returned +and the table is populated according to the captures within the pattern.</p> + +<p>Here is another version of the date parser:</p> + + +<pre> +<span class="comment">-- using SIP patterns +</span><span class="keyword">function</span> check(t) + check_and_process(t.day,t.month,t.year) +<span class="keyword">end</span> + +shortdate = sip.compile(<span class="string">'$d{day}/$d{month}/$d{year}'</span>) +longdate = sip.compile(<span class="string">'$d{day} $v{mon} $d{year}'</span>) +isodate = sip.compile(<span class="string">'$d{year}-$d{month}-$d{day}'</span>) + +<span class="keyword">for</span> line <span class="keyword">in</span> f:lines() <span class="keyword">do</span> + <span class="keyword">local</span> res = {} + <span class="keyword">if</span> shortdate(str,res) <span class="keyword">then</span> + check(res) + <span class="keyword">elseif</span> isodate(str,res) <span class="keyword">then</span> + check(res) + <span class="keyword">elseif</span> longdate(str,res) <span class="keyword">then</span> + res.month = months[res.mon] + check(res) + <span class="keyword">end</span> +<span class="keyword">end</span> +</pre> + +<p>SIP captures start with '$', then a one-character type, and then an +optional variable name in curly braces.</p> + + +<pre> +Type Meaning +v identifier +i possibly signed integer +f floating-point number +r rest of line +q quoted <span class="global">string</span> (quoted using either ' <span class="keyword">or</span> ") +p a path name +( anything inside balanced parentheses +[ anything inside balanced brackets +{ anything inside balanced curly brackets +< anything inside balanced angle brackets +</pre> + +<p>If a type is not one of the above, then it's assumed to be one of the standard +Lua character classes, and will match one or more repetitions of that class. +Any spaces you leave in your pattern will match any number of spaces, including +zero, unless the spaces are between two identifier characters or patterns +matching them; in that case, at least one space will be matched.</p> + +<p>SIP captures (like <code>$v{mon}</code>) do not have to be named. You can use just <code>$v</code>, but +you have to be consistent; if a pattern contains unnamed captures, then all +captures must be unnamed. In this case, the result table is a simple list of +values.</p> + +<p><a href="../libraries/pl.sip.html#match">sip.match</a> is a useful shortcut if you want to compile and match in one call, +without saving the compiled pattern. It caches the result, so it is not much +slower than explicitly using <a href="../libraries/pl.sip.html#compile">sip.compile</a>.</p> + + +<pre> +> sip.match(<span class="string">'($q{first},$q{second})'</span>,<span class="string">'("john","smith")'</span>,res) +<span class="keyword">true</span> +> res +{second=<span class="string">'smith'</span>,first=<span class="string">'john'</span>} +> res = {} +> sip.match(<span class="string">'($q,$q)'</span>,<span class="string">'("jan","smit")'</span>,res) <span class="comment">-- unnamed captures +</span><span class="keyword">true</span> +> res +{<span class="string">'jan'</span>,<span class="string">'smit'</span>} +> sip.match(<span class="string">'($q,$q)'</span>,<span class="string">'("jan", "smit")'</span>,res) +<span class="keyword">false</span> <span class="comment">---> oops! Can't handle extra space! +</span>> sip.match(<span class="string">'( $q , $q )'</span>,<span class="string">'("jan", "smit")'</span>,res) +<span class="keyword">true</span> +</pre> + +<p>As a general rule, allow for whitespace in your patterns.</p> + +<p>Finally, putting a '$' at the end of a pattern means 'capture the rest of the +line, starting at the first non-space'. It is a shortcut for '$r{rest}', +or just '$r' if no named captures are used.</p> + + +<pre> +> sip.match(<span class="string">'( $q , $q ) $'</span>,<span class="string">'("jan", "smit") and a string'</span>,res) +<span class="keyword">true</span> +> res +{<span class="string">'jan'</span>,<span class="string">'smit'</span>,<span class="string">'and a string'</span>} +> res = {} +> sip.match(<span class="string">'( $q{first} , $q{last} ) $'</span>,<span class="string">'("jan", "smit") and a string'</span>,res) +<span class="keyword">true</span> +> res +{first=<span class="string">'jan'</span>,rest=<span class="string">'and a string'</span>,last=<span class="string">'smit'</span>} +</pre> + +<p><a id="lapp"/></p> + +<p><a name="Command_line_Programs_with_Lapp"></a></p> +<h3>Command-line Programs with Lapp</h3> + +<p><a href="../libraries/pl.lapp.html#">pl.lapp</a> is a small and focused Lua module which aims to make standard +command-line parsing easier and intuitive. It implements the standard GNU style, +i.e. short flags with one letter start with '-', and there may be an additional +long flag which starts with '--'. Generally options which take an argument expect +to find it as the next parameter (e.g. 'gcc test.c -o test') but single short +options taking a value can dispense with the space (e.g. 'head -n4 +test.c' or <code>gcc -I/usr/include/lua/5.1 ...</code>)</p> + +<p>As far as possible, Lapp will convert parameters into their equivalent Lua types, +i.e. convert numbers and convert filenames into file objects. If any conversion +fails, or a required parameter is missing, an error will be issued and the usage +text will be written out. So there are two necessary tasks, supplying the flag +and option names and associating them with a type.</p> + +<p>For any non-trivial script, even for personal consumption, it's necessary to +supply usage text. The novelty of Lapp is that it starts from that point and +defines a loose format for usage strings which can specify the names and types of +the parameters.</p> + +<p>An example will make this clearer:</p> + + +<pre> +<span class="comment">-- scale.lua +</span> lapp = <span class="global">require</span> <span class="string">'pl.lapp'</span> + <span class="keyword">local</span> args = lapp <span class="string">[[ + Does some calculations + -o,--offset (default 0.0) Offset to add to scaled number + -s,--scale (number) Scaling factor + <number> (number) Number to be scaled + ]]</span> + + <span class="global">print</span>(args.offset + args.scale * args.number) +</pre> + +<p>Here is a command-line session using this script:</p> + + +<pre> +$ lua scale.lua +scale.lua:missing required parameter: scale + +Does some calculations + -o,<span class="comment">--offset (default 0.0) Offset to add to scaled number +</span> -s,<span class="comment">--scale (number) Scaling factor +</span> <number> (number ) Number to be scaled + +$ lua scale.lua -s <span class="number">2.2</span> <span class="number">10</span> +<span class="number">22</span> + +$ lua scale.lua -s <span class="number">2.2</span> x10 +scale.lua:unable to convert to number: x10 + +....(usage as before) +</pre> + +<p>There are two kinds of lines in Lapp usage strings which are meaningful; option +and parameter lines. An option line gives the short option, optionally followed +by the corresponding long option. A type specifier in parentheses may follow. +Similarly, a parameter line starts with '<NAME>', followed by a type +specifier.</p> + +<p>Type specifiers usually start with a type name: one of 'boolean', 'string','number','file-in' or +'file-out'. You may leave this out, but then <em>must</em> say 'default' followed by a value. +If a flag or parameter has a default, it is not <em>required</em> and is set to the default. The actual +type is deduced from this value (number, string, file or boolean) if not provided directly. +'Deduce' is a fancy word for 'guess' and it can be wrong, e.g '(default 1)' +will always be a number. You can say '(string default 1)' to override the guess. +There are file values for the predefined console streams: stdin, stdout, stderr.</p> + +<p>The boolean type is the default for flags. Not providing the type specifier is equivalent to +'(boolean default false)`. If the flag is meant to be 'turned off' then either the full +'(boolean default true)` or the shortcut '(default true)' will work.</p> + +<p>An alternative to <code>default</code> is <code>optional</code>:</p> + + +<pre> +<span class="keyword">local</span> lapp = <span class="global">require</span> <span class="string">'pl.lapp'</span> +<span class="keyword">local</span> args = lapp <span class="string">[[ + --cmd (optional string) Command to run. +]]</span> + +<span class="keyword">if</span> args.cmd <span class="keyword">then</span> + <span class="global">os</span>.execute(args.cmd) +<span class="keyword">end</span> +</pre> + +<p>Here we're implying that <code>cmd</code> need not be specified (just as with <code>default</code>) but if not +present, then <code>args.cmd</code> is <code>nil</code>, which will always test false.</p> + +<p>The rest of the line is ignored and can be used for explanatory text.</p> + +<p>This script shows the relation between the specified parameter names and the +fields in the output table.</p> + + +<pre> +<span class="comment">-- simple.lua +</span><span class="keyword">local</span> args = <span class="global">require</span> (<span class="string">'pl.lapp'</span>) <span class="string">[[ +Various flags and option types + -p A simple optional flag, defaults to false + -q,--quiet A simple flag with long name + -o (string) A required option with argument + -s (default 'save') Optional string with default 'save' (single quotes ignored) + -n (default 1) Optional numerical flag with default 1 + -b (string default 1) Optional string flag with default '1' (type explicit) + <input> (default stdin) Optional input file parameter, reads from stdin +]]</span> + +<span class="keyword">for</span> k,v <span class="keyword">in</span> <span class="global">pairs</span>(args) <span class="keyword">do</span> + <span class="global">print</span>(k,v) +<span class="keyword">end</span> +</pre> + +<p>I've just dumped out all values of the args table; note that args.quiet has +become true, because it's specified; args.p defaults to false. If there is a long +name for an option, that will be used in preference as a field name. A type or +default specifier is not necessary for simple flags, since the default type is +boolean.</p> + + +<pre> +$ simple -o test -q simple.lua +p <span class="keyword">false</span> +input file (<span class="number">781</span>C1BD8) +quiet <span class="keyword">true</span> +o test +input_name simple.lua +D:\dev\lua\lapp>simple -o test simple.lua one two three +<span class="number">1</span> one +<span class="number">2</span> two +<span class="number">3</span> three +p <span class="keyword">false</span> +quiet <span class="keyword">false</span> +input file (<span class="number">781</span>C1BD8) +o test +input_name simple.lua +</pre> + +<p>The parameter input has been set to an open read-only file object - we know it +must be a read-only file since that is the type of the default value. The field +input_name is automatically generated, since it's often useful to have access to +the original filename.</p> + +<p>Notice that any extra parameters supplied will be put in the result table with +integer indices, i.e. args[i] where i goes from 1 to #args.</p> + +<p>Files don't really have to be closed explicitly for short scripts with a quick +well-defined mission, since the result of garbage-collecting file objects is to +close them.</p> + +<h4>Enforcing a Range and Enumerations</h4> + +<p>The type specifier can also be of the form '(' MIN '..' MAX ')' or a set of strings +separated by '|'.</p> + + +<pre> +<span class="keyword">local</span> lapp = <span class="global">require</span> <span class="string">'pl.lapp'</span> +<span class="keyword">local</span> args = lapp <span class="string">[[ + Setting ranges + <x> (1..10) A number from 1 to 10 + <y> (-5..1e6) Bigger range + <z> (slow|medium|fast) +]]</span> + +<span class="global">print</span>(args.x,args.y) +</pre> + +<p>Here the meaning of ranges is that the value is greater or equal to MIN and less or equal +to MAX. +An 'enum' is a <em>string</em> that can only have values from a specified set.</p> + +<h4>Custom Types</h4> + +<p>There is no builti-in way to force a parameter to be a whole number, but +you may define a custom type that does this:</p> + + +<pre> +lapp = <span class="global">require</span> (<span class="string">'pl.lapp'</span>) + +lapp.add_type(<span class="string">'integer'</span>,<span class="string">'number'</span>, + <span class="keyword">function</span>(x) + lapp.<span class="global">assert</span>(<span class="global">math</span>.ceil(x) == x, <span class="string">'not an integer!'</span>) + <span class="keyword">end</span> +) + +<span class="keyword">local</span> args = lapp <span class="string">[[ + <ival> (integer) Process PID +]]</span> + +<span class="global">print</span>(args.ival) +</pre> + +<p><a href="../libraries/pl.lapp.html#add_type">lapp.add_type</a> takes three parameters, a type name, a converter and a constraint +function. The constraint function is expected to throw an assertion if some +condition is not true; we use <a href="../libraries/pl.lapp.html#assert">lapp.assert</a> because it fails in the standard way +for a command-line script. The converter argument can either be a type name known +to Lapp, or a function which takes a string and generates a value.</p> + +<p>Here's a useful custom type that allows dates to be input as <a href="../classes/pl.Date.html#">pl.Date</a> values:</p> + + +<pre> +<span class="keyword">local</span> df = Date.Format() + +lapp.add_type(<span class="string">'date'</span>, + <span class="keyword">function</span>(s) + <span class="keyword">local</span> d,e = df:parse(s) + lapp.<span class="global">assert</span>(d,e) + <span class="keyword">return</span> d + <span class="keyword">end</span> +) +</pre> + +<h4>'varargs' Parameter Arrays</h4> + + +<pre> +lapp = <span class="global">require</span> <span class="string">'pl.lapp'</span> +<span class="keyword">local</span> args = lapp <span class="string">[[ +Summing numbers + <numbers...> (number) A list of numbers to be summed +]]</span> + +<span class="keyword">local</span> sum = <span class="number">0</span> +<span class="keyword">for</span> i,x <span class="keyword">in</span> <span class="global">ipairs</span>(args.numbers) <span class="keyword">do</span> + sum = sum + x +<span class="keyword">end</span> +<span class="global">print</span> (<span class="string">'sum is '</span>..sum) +</pre> + +<p>The parameter number has a trailing '...', which indicates that this parameter is +a 'varargs' parameter. It must be the last parameter, and args.number will be an +array.</p> + +<p>Consider this implementation of the head utility from Mac OS X:</p> + + +<pre> +<span class="comment">-- implements a BSD-style head +</span><span class="comment">-- (see http://www.manpagez.com/man/1/head/osx-10.3.php) +</span> +lapp = <span class="global">require</span> (<span class="string">'pl.lapp'</span>) + +<span class="keyword">local</span> args = lapp <span class="string">[[ +Print the first few lines of specified files + -n (default 10) Number of lines to print + <files...> (default stdin) Files to print +]]</span> + +<span class="comment">-- by default, lapp converts file arguments to an actual Lua file object. +</span><span class="comment">-- But the actual filename is always available as <file>_name. +</span><span class="comment">-- In this case, 'files' is a varargs array, so that 'files_name' is +</span><span class="comment">-- also an array. +</span><span class="keyword">local</span> nline = args.n +<span class="keyword">local</span> nfile = #args.files +<span class="keyword">for</span> i = <span class="number">1</span>,nfile <span class="keyword">do</span> + <span class="keyword">local</span> file = args.files[i] + <span class="keyword">if</span> nfile > <span class="number">1</span> <span class="keyword">then</span> + <span class="global">print</span>(<span class="string">'==> '</span>..args.files_name[i]..<span class="string">' <=='</span>) + <span class="keyword">end</span> + <span class="keyword">local</span> n = <span class="number">0</span> + <span class="keyword">for</span> line <span class="keyword">in</span> file:lines() <span class="keyword">do</span> + <span class="global">print</span>(line) + n = n + <span class="number">1</span> + <span class="keyword">if</span> n == nline <span class="keyword">then</span> <span class="keyword">break</span> <span class="keyword">end</span> + <span class="keyword">end</span> +<span class="keyword">end</span> +</pre> + +<p>Note how we have access to all the filenames, because the auto-generated field +<code>files_name</code> is also an array!</p> + +<p>(This is probably not a very considerate script, since Lapp will open all the +files provided, and only close them at the end of the script. See the <code>xhead.lua</code> +example for another implementation.)</p> + +<p>Flags and options may also be declared as vararg arrays, and can occur anywhere. +If there is both a short and long form, then the trailing "..." must happen after the long form, +for example "-x,--network... (string)...",</p> + +<p>Bear in mind that short options can be combined (like 'tar -xzf'), so it's +perfectly legal to have '-vvv'. But normally the value of args.v is just a simple +<code>true</code> value.</p> + + +<pre> +<span class="keyword">local</span> args = <span class="global">require</span> (<span class="string">'pl.lapp'</span>) <span class="string">[[ + -v... Verbosity level; can be -v, -vv or -vvv +]]</span> +vlevel = <span class="keyword">not</span> args.v[<span class="number">1</span>] <span class="keyword">and</span> <span class="number">0</span> <span class="keyword">or</span> #args.v +<span class="global">print</span>(vlevel) +</pre> + +<p>The vlevel assigment is a bit of Lua voodoo, so consider the cases:</p> + + +<pre> +* No -v flag, v is just { <span class="keyword">false</span> } +* One -v flags, v is { <span class="keyword">true</span> } +* Two -v flags, v is { <span class="keyword">true</span>, <span class="keyword">true</span> } +* Three -v flags, v is { <span class="keyword">true</span>, <span class="keyword">true</span>, <span class="keyword">true</span> } +</pre> + +<h4>Defining a Parameter Callback</h4> + +<p>If a script implements <code>lapp.callback</code>, then Lapp will call it after each +argument is parsed. The callback is passed the parameter name, the raw unparsed +value, and the result table. It is called immediately after assignment of the +value, so the corresponding field is available.</p> + + +<pre> +lapp = <span class="global">require</span> (<span class="string">'pl.lapp'</span>) + +<span class="keyword">function</span> lapp.callback(parm,arg,args) + <span class="global">print</span>(<span class="string">'+'</span>,parm,arg) +<span class="keyword">end</span> + +<span class="keyword">local</span> args = lapp <span class="string">[[ +Testing parameter handling + -p Plain flag (defaults to false) + -q,--quiet Plain flag with GNU-style optional long name + -o (string) Required string option + -n (number) Required number option + -s (default 1.0) Option that takes a number, but will default + <start> (number) Required number argument + <input> (default stdin) A parameter which is an input file + <output> (default stdout) One that is an output file +]]</span> +<span class="global">print</span> <span class="string">'args'</span> +<span class="keyword">for</span> k,v <span class="keyword">in</span> <span class="global">pairs</span>(args) <span class="keyword">do</span> + <span class="global">print</span>(k,v) +<span class="keyword">end</span> +</pre> + +<p>This produces the following output:</p> + + +<pre> +$ args -o name -n <span class="number">2</span> <span class="number">10</span> args.lua ++ o name ++ n <span class="number">2</span> ++ start <span class="number">10</span> ++ input args.lua +args +p <span class="keyword">false</span> +s <span class="number">1</span> +input_name args.lua +quiet <span class="keyword">false</span> +output file (<span class="number">781</span>C1B98) +start <span class="number">10</span> +input file (<span class="number">781</span>C1BD8) +o name +n <span class="number">2</span> +</pre> + +<p>Callbacks are needed when you want to take action immediately on parsing an +argument.</p> + +<h4>Slack Mode</h4> + +<p>If you'd like to use a multi-letter 'short' parameter you need to set +the <code>lapp.slack</code> variable to <code>true</code>.</p> + +<p>In the following example we also see how default <code>false</code> and default <code>true</code> flags can be used +and how to overwrite the default <code>-h</code> help flag (<code>--help</code> still works fine) - this applies +to non-slack mode as well.</p> + + +<pre> +<span class="comment">-- Parsing the command line ---------------------------------------------------- +</span><span class="comment">-- test.lua +</span><span class="keyword">local</span> lapp = <span class="global">require</span> <span class="string">'pl.lapp'</span> +<span class="keyword">local</span> pretty = <span class="global">require</span> <span class="string">'pl.pretty'</span> +lapp.slack = <span class="keyword">true</span> +<span class="keyword">local</span> args = lapp <span class="string">[[ +Does some calculations + -v, --video (string) Specify input video + -w, --width (default 256) Width of the video + -h, --height (default 144) Height of the video + -t, --time (default 10) Seconds of video to process + -sk,--seek (default 0) Seek number of seconds + -f1,--flag1 A false flag + -f2,--flag2 A false flag + -f3,--flag3 (default true) A true flag + -f4,--flag4 (default true) A true flag +]]</span> + +pretty.dump(args) +</pre> + +<p>And here we can see the output of <code>test.lua</code>:</p> + + +<pre> +$> lua test.lua -v abc <span class="comment">--time 40 -h 20 -sk 15 --flag1 -f3 +</span><span class="comment">----> +</span>{ + width = <span class="number">256</span>, + flag1 = <span class="keyword">true</span>, + flag3 = <span class="keyword">false</span>, + seek = <span class="number">15</span>, + flag2 = <span class="keyword">false</span>, + video = abc, + time = <span class="number">40</span>, + height = <span class="number">20</span>, + flag4 = <span class="keyword">true</span> +} +</pre> + +<p><a name="Simple_Test_Framework"></a></p> +<h3>Simple Test Framework</h3> + +<p><a href="../libraries/pl.test.html#">pl.test</a> was originally developed for the sole purpose of testing Penlight itself, +but you may find it useful for your own applications. (<a href="http://lua-users.org/wiki/UnitTesting">There are many other options</a>.)</p> + +<p>Most of the goodness is in <a href="../libraries/pl.test.html#asserteq">test.asserteq</a>. It uses <a href="../libraries/pl.tablex.html#deepcompare">tablex.deepcompare</a> on its two arguments, +and by default quits the test application with a non-zero exit code, and an informative +message printed to stderr:</p> + + +<pre> +<span class="keyword">local</span> test = <span class="global">require</span> <span class="string">'pl.test'</span> + +test.asserteq({<span class="number">10</span>,<span class="number">20</span>,<span class="number">30</span>},{<span class="number">10</span>,<span class="number">20</span>,<span class="number">30.1</span>}) + +<span class="comment">--~ test-test.lua:3: assertion failed +</span><span class="comment">--~ got: { +</span><span class="comment">--~ [1] = 10, +</span><span class="comment">--~ [2] = 20, +</span><span class="comment">--~ [3] = 30 +</span><span class="comment">--~ } +</span><span class="comment">--~ needed: { +</span><span class="comment">--~ [1] = 10, +</span><span class="comment">--~ [2] = 20, +</span><span class="comment">--~ [3] = 30.1 +</span><span class="comment">--~ } +</span><span class="comment">--~ these values were not equal</span> +</pre> + +<p>This covers most cases but it's also useful to compare strings using <a href="https://www.lua.org/manual/5.1/manual.html#pdf-string.match">string.match</a></p> + + +<pre> +<span class="comment">-- must start with bonzo the dog +</span>test.assertmatch (<span class="string">'bonzo the dog is here'</span>,<span class="string">'^bonzo the dog'</span>) +<span class="comment">-- must end with an integer +</span>test.assertmatch (<span class="string">'hello 42'</span>,<span class="string">'%d+$'</span>) +</pre> + +<p>Since Lua errors are usually strings, this matching strategy is used to test 'exceptions':</p> + + +<pre> +test.assertraise(<span class="keyword">function</span>() + <span class="keyword">local</span> t = <span class="keyword">nil</span> + <span class="global">print</span>(t.bonzo) +<span class="keyword">end</span>,<span class="string">'nil value'</span>) +</pre> + +<p>(Some care is needed to match the essential part of the thrown error if you care +for portability, since in Lua 5.2 +the exact error is "attempt to index local 't' (a nil value)" and in Lua 5.3 the error +is "attempt to index a nil value (local 't')")</p> + +<p>There is an extra optional argument to these test functions, which is helpful when writing +test helper functions. There you want to highlight the failed line, not the actual call +to <code>asserteq</code> or <code>assertmatch</code> - line 33 here is the call to <code>is_iden</code></p> + + +<pre> +<span class="keyword">function</span> is_iden(str) + test.assertmatch(str,<span class="string">'^[%a_][%w_]*$'</span>,<span class="number">1</span>) +<span class="keyword">end</span> + +is_iden <span class="string">'alpha_dog'</span> +is_iden <span class="string">'$dollars'</span> + +<span class="comment">--~ test-test.lua:33: assertion failed +</span><span class="comment">--~ got: "$dollars" +</span><span class="comment">--~ needed: "^[%a_][%w_]*$" +</span><span class="comment">--~ these strings did not match</span> +</pre> + +<p>Useful Lua functions often return multiple values, and <a href="../libraries/pl.test.html#tuple">test.tuple</a> is a convenient way to +capture these values, whether they contain nils or not.</p> + + +<pre> +T = test.tuple + +<span class="comment">--- common error pattern +</span><span class="keyword">function</span> failing() + <span class="keyword">return</span> <span class="keyword">nil</span>,<span class="string">'failed'</span> +<span class="keyword">end</span> + +test.asserteq(T(failing()),T(<span class="keyword">nil</span>,<span class="string">'failed'</span>)) +</pre> + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs/manual/09-discussion.md.html b/Data/Libraries/Penlight/docs/manual/09-discussion.md.html new file mode 100644 index 0000000..4e7dd69 --- /dev/null +++ b/Data/Libraries/Penlight/docs/manual/09-discussion.md.html @@ -0,0 +1,233 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Penlight Documentation</title> + <link rel="stylesheet" href="../ldoc_fixed.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>Penlight</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> + <li><a href="../index.html">Documentation</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Modularity_and_Granularity">Modularity and Granularity </a></li> +<li><a href="#Defining_what_is_Callable">Defining what is Callable </a></li> +</ul> + + +<h2>Manual</h2> +<ul class="nowrap"> + <li><a href="../manual/01-introduction.md.html">Introduction</a></li> + <li><a href="../manual/02-arrays.md.html">Tables and Arrays</a></li> + <li><a href="../manual/03-strings.md.html">Strings. Higher-level operations on strings.</a></li> + <li><a href="../manual/04-paths.md.html">Paths and Directories</a></li> + <li><a href="../manual/05-dates.md.html">Date and Time</a></li> + <li><a href="../manual/06-data.md.html">Data</a></li> + <li><a href="../manual/07-functional.md.html">Functional Programming</a></li> + <li><a href="../manual/08-additional.md.html">Additional Libraries</a></li> + <li><strong>Technical Choices</strong></li> +</ul> +<h2>Libraries</h2> +<ul class="nowrap"> + <li><a href="../libraries/pl.html">pl</a></li> + <li><a href="../libraries/pl.app.html">pl.app</a></li> + <li><a href="../libraries/pl.array2d.html">pl.array2d</a></li> + <li><a href="../libraries/pl.class.html">pl.class</a></li> + <li><a href="../libraries/pl.compat.html">pl.compat</a></li> + <li><a href="../libraries/pl.comprehension.html">pl.comprehension</a></li> + <li><a href="../libraries/pl.config.html">pl.config</a></li> + <li><a href="../libraries/pl.data.html">pl.data</a></li> + <li><a href="../libraries/pl.dir.html">pl.dir</a></li> + <li><a href="../libraries/pl.file.html">pl.file</a></li> + <li><a href="../libraries/pl.func.html">pl.func</a></li> + <li><a href="../libraries/pl.import_into.html">pl.import_into</a></li> + <li><a href="../libraries/pl.input.html">pl.input</a></li> + <li><a href="../libraries/pl.lapp.html">pl.lapp</a></li> + <li><a href="../libraries/pl.lexer.html">pl.lexer</a></li> + <li><a href="../libraries/pl.luabalanced.html">pl.luabalanced</a></li> + <li><a href="../libraries/pl.operator.html">pl.operator</a></li> + <li><a href="../libraries/pl.path.html">pl.path</a></li> + <li><a href="../libraries/pl.permute.html">pl.permute</a></li> + <li><a href="../libraries/pl.pretty.html">pl.pretty</a></li> + <li><a href="../libraries/pl.seq.html">pl.seq</a></li> + <li><a href="../libraries/pl.sip.html">pl.sip</a></li> + <li><a href="../libraries/pl.strict.html">pl.strict</a></li> + <li><a href="../libraries/pl.stringio.html">pl.stringio</a></li> + <li><a href="../libraries/pl.stringx.html">pl.stringx</a></li> + <li><a href="../libraries/pl.tablex.html">pl.tablex</a></li> + <li><a href="../libraries/pl.template.html">pl.template</a></li> + <li><a href="../libraries/pl.test.html">pl.test</a></li> + <li><a href="../libraries/pl.text.html">pl.text</a></li> + <li><a href="../libraries/pl.types.html">pl.types</a></li> + <li><a href="../libraries/pl.url.html">pl.url</a></li> + <li><a href="../libraries/pl.utils.html">pl.utils</a></li> + <li><a href="../libraries/pl.xml.html">pl.xml</a></li> +</ul> +<h2>Classes</h2> +<ul class="nowrap"> + <li><a href="../classes/pl.Date.html">pl.Date</a></li> + <li><a href="../classes/pl.List.html">pl.List</a></li> + <li><a href="../classes/pl.Map.html">pl.Map</a></li> + <li><a href="../classes/pl.MultiMap.html">pl.MultiMap</a></li> + <li><a href="../classes/pl.OrderedMap.html">pl.OrderedMap</a></li> + <li><a href="../classes/pl.Set.html">pl.Set</a></li> +</ul> +<h2>Examples</h2> +<ul class="nowrap"> + <li><a href="../examples/seesubst.lua.html">seesubst.lua</a></li> + <li><a href="../examples/sipscan.lua.html">sipscan.lua</a></li> + <li><a href="../examples/symbols.lua.html">symbols.lua</a></li> + <li><a href="../examples/test-cmp.lua.html">test-cmp.lua</a></li> + <li><a href="../examples/test-data.lua.html">test-data.lua</a></li> + <li><a href="../examples/test-listcallbacks.lua.html">test-listcallbacks.lua</a></li> + <li><a href="../examples/test-pretty.lua.html">test-pretty.lua</a></li> + <li><a href="../examples/test-symbols.lua.html">test-symbols.lua</a></li> + <li><a href="../examples/testclone.lua.html">testclone.lua</a></li> + <li><a href="../examples/testconfig.lua.html">testconfig.lua</a></li> + <li><a href="../examples/testglobal.lua.html">testglobal.lua</a></li> + <li><a href="../examples/testinputfields.lua.html">testinputfields.lua</a></li> + <li><a href="../examples/testinputfields2.lua.html">testinputfields2.lua</a></li> + <li><a href="../examples/testxml.lua.html">testxml.lua</a></li> + <li><a href="../examples/which.lua.html">which.lua</a></li> +</ul> + +</div> + +<div id="content"> + + +<h2>Technical Choices</h2> + +<p><a name="Modularity_and_Granularity"></a></p> +<h3>Modularity and Granularity</h3> + +<p>In an ideal world, a program should only load the libraries it needs. Penlight is +intended to work in situations where an extra 100Kb of bytecode could be a +problem. It is straightforward but tedious to load exactly what you need:</p> + + +<pre> +<span class="keyword">local</span> data = <span class="global">require</span> <span class="string">'pl.data'</span> +<span class="keyword">local</span> List = <span class="global">require</span> <span class="string">'pl.List'</span> +<span class="keyword">local</span> array2d = <span class="global">require</span> <span class="string">'pl.array2d'</span> +<span class="keyword">local</span> seq = <span class="global">require</span> <span class="string">'pl.seq'</span> +<span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">'pl.utils'</span> +</pre> + +<p>This is the style that I follow in Penlight itself, so that modules don't mess +with the global environment; also, <code>stringx.import()</code> is not used because it will +update the global <a href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> table.</p> + +<p>But <code>require 'pl'</code> is more convenient in scripts; the question is how to ensure +that one doesn't load the whole kitchen sink as the price of convenience. The +strategy is to only load modules when they are referenced. In 'init.lua' (which +is loaded by <code>require 'pl'</code>) a metatable is attached to the global table with an +<code>__index</code> metamethod. Any unknown name is looked up in the list of modules, and +if found, we require it and make that module globally available. So when +<a href="../libraries/pl.tablex.html#deepcompare">tablex.deepcompare</a> is encountered, looking up <a href="../libraries/pl.tablex.html#">tablex</a> causes 'pl.tablex' to be +required. .</p> + +<p>Modifying the behaviour of the global table has consequences. For instance, there +is the famous module <a href="../libraries/pl.strict.html#">strict</a> which comes with Lua itself (perhaps the only +standard Lua module written in Lua itself) which also does this modification so +that global variiables must be defined before use. So the implementation in +'init.lua' allows for a 'not found' hook, which 'pl.strict.lua' uses. Other +libraries may install their own metatables for <code>_G</code>, but Penlight will now +forward any unknown name to the <code>__index</code> defined by the original metatable.</p> + +<p>But the strategy is worth the effort: the old 'kitchen sink' 'init.lua' would +pull in about 260K of bytecode, whereas now typical programs use about 100K less, +and short scripts even better - for instance, if they were only needing +functionality in <a href="../libraries/pl.utils.html#">utils</a>.</p> + +<p>There are some functions which mark their output table with a special metatable, +when it seems particularly appropriate. For instance, <a href="../libraries/pl.tablex.html#makeset">tablex.makeset</a> creates a +<a href="../classes/pl.Set.html#">Set</a>, and <a href="../libraries/pl.seq.html#copy">seq.copy</a> creates a <a href="../classes/pl.List.html#">List</a>. But this does not automatically result in +the loading of <a href="../classes/pl.Set.html#">pl.Set</a> and <a href="../classes/pl.List.html#">pl.List</a>; only if you try to access any of these +methods. In 'utils.lua', there is an exported table called <code>stdmt</code>:</p> + + +<pre> +stdmt = { List = {}, Map = {}, Set = {}, MultiMap = {} } +</pre> + +<p>If you go through 'init.lua', then these plain little 'identity' tables get an +<code>__index</code> metamethod which forces the loading of the full functionality. Here is +the code from 'list.lua' which starts the ball rolling for lists:</p> + + +<pre> +List = utils.stdmt.List +List.__index = List +List._name = <span class="string">"List"</span> +List._class = List +</pre> + +<p>The 'load-on-demand' strategy helps to modularize the library. Especially for +more casual use, <code>require 'pl'</code> is a good compromise between convenience and +modularity.</p> + +<p>In this current version, I have generally reduced the amount of trickery +involved. Previously, <a href="../classes/pl.Map.html#">Map</a> was defined in <a href="../libraries/pl.class.html#">pl.class</a>; now it is sensibly defined +in <a href="../classes/pl.Map.html#">pl.Map</a>; <a href="../libraries/pl.class.html#">pl.class</a> only contains the basic class mechanism (and returns that +function.) For consistency, <a href="../classes/pl.List.html#">List</a> is returned directly by <code>require 'pl.List'</code> +(note the uppercase 'L'), Also, the amount of module dependencies in the +non-core libraries like <a href="../libraries/pl.config.html#">pl.config</a> have been reduced.</p> + +<p><a name="Defining_what_is_Callable"></a></p> +<h3>Defining what is Callable</h3> + +<p>'utils.lua' exports <code>function_arg</code> which is used extensively throughout Penlight. +It defines what is meant by 'callable'. Obviously true functions are immediately +passed back. But what about strings? The first option is that it represents an +operator in 'operator.lua', so that '<' is just an alias for <a href="../libraries/pl.operator.html#lt">operator.lt</a>.</p> + +<p>We then check whether there is a <em>function factory</em> defined for the metatable of +the value.</p> + +<p>(It is true that strings can be made callable, but in practice this turns out to +be a cute but dubious idea, since <em>all</em> strings share the same metatable. A +common programming error is to pass the wrong kind of object to a function, and +it's better to get a nice clean 'attempting to call a string' message rather than +some obscure trace from the bowels of your library.)</p> + +<p>The other module that registers a function factory is <a href="../libraries/pl.func.html#">pl.func</a>. Placeholder +expressions cannot be directly calleable, and so need to be instantiated and +cached in as efficient way as possible.</p> + +<p>(An inconsistency is that <code>utils.is_callable</code> does not do this thorough check.)</p> + + + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/docs_topics/01-introduction.md b/Data/Libraries/Penlight/docs_topics/01-introduction.md new file mode 100644 index 0000000..a8bf26a --- /dev/null +++ b/Data/Libraries/Penlight/docs_topics/01-introduction.md @@ -0,0 +1,621 @@ +## Introduction + +### Purpose + +It is often said of Lua that it does not include batteries. That is because the +goal of Lua is to produce a lean expressive language that will be used on all +sorts of machines, (some of which don't even have hierarchical filesystems). The +Lua language is the equivalent of an operating system kernel; the creators of Lua +do not see it as their responsibility to create a full software ecosystem around +the language. That is the role of the community. + +A principle of software design is to recognize common patterns and reuse them. If +you find yourself writing things like `io.write(string.format('the answer is %d +',42))` more than a number of times then it becomes useful just to define a +function `printf`. This is good, not just because repeated code is harder to +maintain, but because such code is easier to read, once people understand your +libraries. + +Penlight captures many such code patterns, so that the intent of your code +becomes clearer. For instance, a Lua idiom to copy a table is `{unpack(t)}`, but +this will only work for 'small' tables (for a given value of 'small') so it is +not very robust. Also, the intent is not clear. So `tablex.deepcopy` is provided, +which will also copy nested tables and and associated metatables, so it can be +used to clone complex objects. + +The default error handling policy follows that of the Lua standard libraries: if +a argument is the wrong type, then an error will be thrown, but otherwise we +return `nil,message` if there is a problem. There are some exceptions; functions +like `input.fields` default to shutting down the program immediately with a +useful message. This is more appropriate behaviour for a _script_ than providing +a stack trace. (However, this default can be changed.) The lexer functions always +throw errors, to simplify coding, and so should be wrapped in `pcall`. + +If you are used to Python conventions, please note that all indices consistently +start at 1. + +The Lua function `table.foreach` has been deprecated in favour of the `for in` +statement, but such an operation becomes particularly useful with the +higher-order function support in Penlight. Note that `tablex.foreach` reverses +the order, so that the function is passed the value and then the key. Although +perverse, this matches the intended use better. + +The only important external dependence of Penlight is +[LuaFileSystem](http://keplerproject.github.com/luafilesystem/manual.html) +(`lfs`), and if you want `dir.copyfile` to work cleanly on Windows, you will need +either [alien](http://alien.luaforge.net/) or be using +[LuaJIT](http://luajit.org) as well. (The fallback is to call the equivalent +shell commands.) + +### To Inject or not to Inject? + +It was realized a long time ago that large programs needed a way to keep names +distinct by putting them into tables (Lua), namespaces (C++) or modules +(Python). It is obviously impossible to run a company where everyone is called +'Bruce', except in Monty Python skits. These 'namespace clashes' are more of a +problem in a simple language like Lua than in C++, because C++ does more +complicated lookup over 'injected namespaces'. However, in a small group of +friends, 'Bruce' is usually unique, so in particular situations it's useful to +drop the formality and not use last names. It depends entirely on what kind of +program you are writing, whether it is a ten line script or a ten thousand line +program. + +So the Penlight library provides the formal way and the informal way, without +imposing any preference. You can do it formally like: + + local utils = require 'pl.utils' + utils.printf("%s\n","hello, world!") + +or informally like: + + require 'pl' + utils.printf("%s\n","That feels better") + +`require 'pl'` makes all the separate Penlight modules available, without needing +to require them each individually. + +Generally, the formal way is better when writing modules, since then there are no +global side-effects and the dependencies of your module are made explicit. + +Andrew Starks has contributed another way, which balances nicely between the +formal need to keep the global table uncluttered and the informal need for +convenience. `require'pl.import_into'` returns a function, which accepts a table +for injecting Penlight into, or if no table is given, it passes back a new one. + + local pl = require'pl.import_into'() + +The table `pl` is a 'lazy table' which loads modules as needed, so we can then +use `pl.utils.printf` and so forth, without an explicit `require' or harming any +globals. + +If you are using `_ENV` with Lua 5.2 to define modules, then here is a way to +make Penlight available within a module: + + local _ENV,M = require 'pl.import_into' () + + function answer () + -- all the Penlight modules are available! + return pretty.write(utils.split '10 20 30', '') + end + + return M + +The default is to put Penlight into `\_ENV`, which has the unintended effect of +making it available from the module (much as `module(...,package.seeall)` does). +To satisfy both convenience and safety, you may pass `true` to this function, and +then the _module_ `M` is not the same as `\_ENV`, but only contains the exported +functions. + +Otherwise, Penlight will _not_ bring in functions into the global table, or +clobber standard tables like 'io'. require('pl') will bring tables like +'utils','tablex',etc into the global table _if they are used_. This +'load-on-demand' strategy ensures that the whole kitchen sink is not loaded up +front, so this method is as efficient as explicitly loading required modules. + +You have an option to bring the `pl.stringx` methods into the standard string +table. All strings have a metatable that allows for automatic lookup in `string`, +so we can say `s:upper()`. Importing `stringx` allows for its functions to also +be called as methods: `s:strip()`,etc: + + require 'pl' + stringx.import() + +or, more explicitly: + + require('pl.stringx').import() + +A more delicate operation is importing tables into the local environment. This is +convenient when the context makes the meaning of a name very clear: + + > require 'pl' + > utils.import(math) + > = sin(1.2) + 0.93203908596723 + +`utils.import` can also be passed a module name as a string, which is first +required and then imported. If used in a module, `import` will bring the symbols +into the module context. + +Keeping the global scope simple is very necessary with dynamic languages. Using +global variables in a big program is always asking for trouble, especially since +you do not have the spell-checking provided by a compiler. The `pl.strict` +module enforces a simple rule: globals must be 'declared'. This means that they +must be assigned before use; assigning to `nil` is sufficient. + + > require 'pl.strict' + > print(x) + stdin:1: variable 'x' is not declared + > x = nil + > print(x) + nil + +The `strict` module provided by Penlight is compatible with the 'load-on-demand' +scheme used by `require 'pl`. + +`strict` also disallows assignment to global variables, except in the main +program. Generally, modules have no business messing with global scope; if you +must do it, then use a call to `rawset`. Similarly, if you have to check for the +existence of a global, use `rawget`. + +If you wish to enforce strictness globally, then just add `require 'pl.strict'` +at the end of `pl/init.lua`, otherwise call it from your main program. + +As from 1.1.0, this module provides a `strict.module` function which creates (or +modifies) modules so that accessing an unknown function or field causes an error. + +For example, + + -- mymod.lua + local strict = require 'pl.strict' + local M = strict.module (...) + + function M.answer () + return 42 + end + + return M + +If you were to accidently type `mymod.Answer()`, then you would get a runtime +error: "variable 'Answer' is not declared in 'mymod'". + +This can be applied to existing modules. You may desire to have the same level +of checking for the Lua standard libraries: + + strict.make_all_strict(_G) + +Thereafter a typo such as `math.cosine` will give you an explicit error, rather +than merely returning a `nil` that will cause problems later. + +### What are function arguments in Penlight? + +Many functions in Penlight themselves take function arguments, like `map` which +applies a function to a list, element by element. You can use existing +functions, like `math.max`, anonymous functions (like `function(x,y) return x > y +end` ), or operations by name (e.g '*' or '..'). The module `pl.operator` exports +all the standard Lua operations, like the Python module of the same name. +Penlight allows these to be referred to by name, so `operator.gt` can be more +concisely expressed as '>'. + +Note that the `map` functions pass any extra arguments to the function, so we can +have `ls:filter('>',0)`, which is a shortcut for +`ls:filter(function(x) return x > 0 end)`. + +Finally, `pl.func` supports _placeholder expressions_ in the Boost lambda style, +so that an anonymous function to multiply the two arguments can be expressed as +`\_1*\_2`. + +To use them directly, note that _all_ function arguments in Penlight go through +`utils.function_arg`. `pl.func` registers itself with this function, so that you +can directly use placeholder expressions with standard methods: + + > _1 = func._1 + > = List{10,20,30}:map(_1+1) + {11,21,31} + +Another option for short anonymous functions is provided by +`utils.string_lambda`; this is invoked automatically: + + > = List{10,20,30}:map '|x| x + 1' + {11,21,31} + +### Pros and Cons of Loopless Programming + +The standard loops-and-ifs 'imperative' style of programming is dominant, and +often seems to be the 'natural' way of telling a machine what to do. It is in +fact very much how the machine does things, but we need to take a step back and +find ways of expressing solutions in a higher-level way. For instance, applying +a function to all elements of a list is a common operation: + + local res = {} + for i = 1,#ls do + res[i] = fun(ls[i]) + end + +This can be efficiently and succintly expressed as `ls:map(fun)`. Not only is +there less typing but the intention of the code is clearer. If readers of your +code spend too much time trying to guess your intention by analyzing your loops, +then you have failed to express yourself clearly. Similarly, `ls:filter('>',0)` +will give you all the values in a list greater than zero. (Of course, if you +don't feel like using `List`, or have non-list-like tables, then `pl.tablex` +offers the same facilities. In fact, the `List` methods are implemented using +`tablex` functions.) + +A common observation is that loopless programming is less efficient, particularly +in the way it uses memory. `ls1:map2('*',ls2):reduce '+'` will give you the dot +product of two lists, but an unnecessary temporary list is created. But +efficiency is relative to the actual situation, it may turn out to be _fast +enough_, or may not appear in any crucial inner loops, etc. + +Writing loops is 'error-prone and tedious', as Stroustrup says. But any +half-decent editor can be taught to do much of that typing for you. The question +should actually be: is it tedious to _read_ loops? As with natural language, +programmers tend to read chunks at a time. A for-loop causes no surprise, and +probably little brain activity. One argument for loopless programming is the +loops that you _do_ write stand out more, and signal 'something different +happening here'. It should not be an all-or-nothing thing, since most programs +require a mixture of idioms that suit the problem. Some languages (like APL) do +nearly everything with map and reduce operations on arrays, and so solutions can +sometimes seem forced. Wisdom is knowing when a particular idiom makes a +particular problem easy to _solve_ and the solution easy to _explain_ afterwards. + +### Generally useful functions. + +The function `printf` discussed earlier is included in `pl.utils` because it +makes properly formatted output easier. (There is an equivalent `fprintf` which +also takes a file object parameter, just like the C function.) + +Splitting a string using a delimiter is a fairly common operation, hence `split`. + +Utility functions like `is_type` help with identifying what +kind of animal you are dealing with. +The Lua `type` function handles the basic types, but can't distinguish between +different kinds of objects, which are all tables. So `is_type` handles both +cases, like `is_type(s,"string")` and `is_type(ls,List)`. + +A common pattern when working with Lua varargs is capturing all the arguments in +a table: + + function t(...) + local args = {...} + ... + end + +But this will bite you someday when `nil` is one of the arguments, since this +will put a 'hole' in your table. In particular, `#ls` will only give you the size +upto the `nil` value. Hence the need for `table.pack` - this is a new Lua 5.2 +function which Penlight defines also for Lua 5.1. + + function t(...) + local args,n = table.pack(...) + for i = 1,n do + ... + end + end + +The 'memoize' pattern occurs when you have a function which is expensive to call, +but will always return the same value subsequently. `utils.memoize` is given a +function, and returns another function. This calls the function the first time, +saves the value for that argument, and thereafter for that argument returns the +saved value. This is a more flexible alternative to building a table of values +upfront, since in general you won't know what values are needed. + + sum = utils.memoize(function(n) + local sum = 0 + for i = 1,n do sum = sum + i end + return sum + end) + ... + s = sum(1e8) --takes time! + ... + s = sum(1e8) --returned saved value! + +Penlight is fully compatible with Lua 5.1, 5.2 and LuaJIT 2. To ensure this, +`utils` also defines the global Lua 5.2 +[load](http://www.lua.org/work/doc/manual.html#pdf-load) function as `utils.load` + + * the input (either a string or a function) + * the source name used in debug information + * the mode is a string that can have either or both of 'b' or 't', depending on +whether the source is a binary chunk or text code (default is 'bt') + * the environment for the compiled chunk + +Using `utils.load` should reduce the need to call the deprecated function `setfenv`, +and make your Lua 5.1 code 5.2-friendly. + +The `utils` module exports `getfenv` and `setfenv` for +Lua 5.2 as well, based on code by Sergey Rozhenko. Note that these functions can fail +for functions which don't access any globals. + +### Application Support + +`app.parse_args` is a simple command-line argument parser. If called without any +arguments, it tries to use the global `arg` array. It returns the _flags_ +(options begining with '-') as a table of name/value pairs, and the _arguments_ +as an array. It knows about long GNU-style flag names, e.g. `--value`, and +groups of short flags are understood, so that `-ab` is short for `-a -b`. The +flags result would then look like `{value=true,a=true,b=true}`. + +Flags may take values. The command-line `--value=open -n10` would result in +`{value='open',n='10'}`; generally you can use '=' or ':' to separate the flag +from its value, except in the special case where a short flag is followed by an +integer. Or you may specify upfront that some flags have associated values, and +then the values will follow the flag. + + > require 'pl' + > flags,args = app.parse_args({'-o','fred','-n10','fred.txt'},{o=true}) + > pretty.dump(flags) + {o='fred',n='10'} + +`parse_args` is not intelligent or psychic; it will not convert any flag values +or arguments for you, or raise errors. For that, have a look at +@{08-additional.md.Command_line_Programs_with_Lapp|Lapp}. + +An application which consists of several files usually cannot use `require` to +load files in the same directory as the main script. `app.require_here()` +ensures that the Lua module path is modified so that files found locally are +found first. In the `examples` directory, `test-symbols.lua` uses this function +to ensure that it can find `symbols.lua` even if it is not run from this directory. + +`app.appfile` will create a filename that your application can use to store its +private data, based on the script name. For example, `app.appfile "test.txt"` +from a script called `testapp.lua` produces the following file on my Windows +machine: + + @plain + C:\Documents and Settings\SJDonova\.testapp\test.txt + +and the equivalent on my Linux machine: + + @plain + /home/sdonovan/.testapp/test.txt + +If `.testapp` does not exist, it will be created. + +Penlight makes it convenient to save application data in Lua format. You can use +`pretty.dump(t,file)` to write a Lua table in a human-readable form to a file, +and `pretty.read(file.read(file))` to generate the table again, using the +`pretty` module. + + +### Simplifying Object-Oriented Programming in Lua + +Lua is similar to JavaScript in that the concept of class is not directly +supported by the language. In fact, Lua has a very general mechanism for +extending the behaviour of tables which makes it straightforward to implement +classes. A table's behaviour is controlled by its metatable. If that metatable +has a `\_\_index` function or table, this will handle looking up anything which is +not found in the original table. A class is just a table with an `__index` key +pointing to itself. Creating an object involves making a table and setting its +metatable to the class; then when handling `obj.fun`, Lua first looks up `fun` in +the table `obj`, and if not found it looks it up in the class. `obj:fun(a)` is +just short for `obj.fun(obj,a)`. So with the metatable mechanism and this bit of +syntactic sugar, it is straightforward to implement classic object orientation. + + -- animal.lua + + class = require 'pl.class' + + class.Animal() + + function Animal:_init(name) + self.name = name + end + + function Animal:__tostring() + return self.name..': '..self:speak() + end + + class.Dog(Animal) + + function Dog:speak() + return 'bark' + end + + class.Cat(Animal) + + function Cat:_init(name,breed) + self:super(name) -- must init base! + self.breed = breed + end + + function Cat:speak() + return 'meow' + end + + class.Lion(Cat) + + function Lion:speak() + return 'roar' + end + + fido = Dog('Fido') + felix = Cat('Felix','Tabby') + leo = Lion('Leo','African') + + $ lua -i animal.lua + > = fido,felix,leo + Fido: bark Felix: meow Leo: roar + > = leo:is_a(Animal) + true + > = leo:is_a(Dog) + false + > = leo:is_a(Cat) + true + +All Animal does is define `\_\_tostring`, which Lua will use whenever a string +representation is needed of the object. In turn, this relies on `speak`, which is +not defined. So it's what C++ people would call an abstract base class; the +specific derived classes like Dog define `speak`. Please note that _if_ derived +classes have their own constructors, they must explicitly call the base +constructor for their base class; this is conveniently available as the `super` +method. + +Note that (as always) there are multiple ways to implement OOP in Lua; this method +uses the classic 'a class is the __index of its objects' but does 'fat inheritance'; +methods from the base class are copied into the new class. The advantage of this is +that you are not penalized for long inheritance chains, for the price of larger classes, +but generally objects outnumber classes! (If not, something odd is going on with your design.) + +All such objects will have a `is_a` method, which looks up the inheritance chain +to find a match. Another form is `class_of`, which can be safely called on all +objects, so instead of `leo:is_a(Animal)` one can say `Animal:class_of(leo)`. + +There are two ways to define a class, either `class.Name()` or `Name = class()`; +both work identically, except that the first form will always put the class in +the current environment (whether global or module); the second form provides more +flexibility about where to store the class. The first form does _name_ the class +by setting the `_name` field, which can be useful in identifying the objects of +this type later. This session illustrates the usefulness of having named classes, +if no `__tostring` method is explicitly defined. + + > class.Fred() + > a = Fred() + > = a + Fred: 00459330 + > Alice = class() + > b = Alice() + > = b + table: 00459AE8 + > Alice._name = 'Alice' + > = b + Alice: 00459AE8 + +So `Alice = class(); Alice._name = 'Alice'` is exactly the same as `class.Alice()`. + +This useful notation is borrowed from Hugo Etchegoyen's +[classlib](http://lua-users.org/wiki/MultipleInheritanceClasses) which further +extends this concept to allow for multiple inheritance. Notice that the +more convenient form puts the class name in the _current environment_! That is, +you may use it safely within modules using the old-fashioned `module()` +or the new `_ENV` mechanism. + +There is always more than one way of doing things in Lua; some may prefer this +style for creating classes: + + local class = require 'pl.class' + + class.Named { + _init = function(self,name) + self.name = name + end; + + __tostring = function(self) + return 'boo '..self.name + end; + } + + b = Named 'dog' + print(b) + --> boo dog + +Note that you have to explicitly declare `self` and end each function definition +with a semi-colon or comma, since this is a Lua table. To inherit from a base class, +set the special field `_base` to the class in this table. + +Penlight provides a number of useful classes; there is `List`, which is a Lua +clone of the standard Python list object, and `Set` which represents sets. There +are three kinds of _map_ defined: `Map`, `MultiMap` (where a key may have +multiple values) and `OrderedMap` (where the order of insertion is remembered.). +There is nothing special about these classes and you may inherit from them. + +A powerful thing about dynamic languages is that you can redefine existing classes +and functions, which is often called 'monkey patching' It's entertaining and convenient, +but ultimately anti-social; you may modify `List` but then any other modules using +this _shared_ resource can no longer be sure about its behaviour. (This is why you +must say `stringx.import()` explicitly if you want the extended string methods - it +would be a bad default.) Lua is particularly open to modification but the +community is not as tolerant of monkey-patching as the Ruby community, say. You may +wish to add some new methods to `List`? Cool, but that's what subclassing is for. + + class.Strings(List) + + function Strings:my_method() + ... + end + +It's definitely more useful to define exactly how your objects behave +in _unknown_ conditions. All classes have a `catch` method you can use to set +a handler for unknown lookups; the function you pass looks exactly like the +`__index` metamethod. + + Strings:catch(function(self,name) + return function() error("no such method "..name,2) end + end) + +In this case we're just customizing the error message, but +creative things can be done. Consider this code from `test-vector.lua`: + + Strings:catch(List.default_map_with(string)) + + ls = Strings{'one','two','three'} + asserteq(ls:upper(),{'ONE','TWO','THREE'}) + asserteq(ls:sub(1,2),{'on','tw','th'}) + +So we've converted a unknown method invocation into a map using the function of +that name found in `string`. So for a `Vector` (which is a specialization of `List` +for numbers) it makes sense to make `math` the default map so that `v:sin()` makes +sense. + +Note that `map` operations return a object of the same type - this is often called +_covariance_. So `ls:upper()` itself returns a `Strings` object. + +This is not _always_ what you want, but objects can always be cast to the desired type. +(`cast` doesn't create a new object, but returns the object passed.) + + local sizes = ls:map '#' + asserteq(sizes, {3,3,5}) + asserteq(utils.type(sizes),'Strings') + asserteq(sizes:is_a(Strings),true) + sizes = Vector:cast(sizes) + asserteq(utils.type(sizes),'Vector') + asserteq(sizes+1,{4,4,6}) + +About `utils.type`: it can only return a string for a class type if that class does +in fact have a `_name` field. + + +_Properties_ are a useful object-oriented pattern. We wish to control access to a +field, but don't wish to force the user of the class to say `obj:get_field()` +etc. This excerpt from `tests/test-class.lua` shows how it is done: + + + local MyProps = class(class.properties) + local setted_a, got_b + + function MyProps:_init () + self._a = 1 + self._b = 2 + end + + function MyProps:set_a (v) + setted_a = true + self._a = v + end + + function MyProps:get_b () + got_b = true + return self._b + end + + local mp = MyProps() + + mp.a = 10 + + asserteq(mp.a,10) + asserteq(mp.b,2) + asserteq(setted_a and got_b, true) + +The convention is that the internal field name is prefixed with an underscore; +when reading `mp.a`, first a check for an explicit _getter_ `get_a` and then only +look for `_a`. Simularly, writing `mp.a` causes the _setter_ `set_a` to be used. + +This is cool behaviour, but like much Lua metaprogramming, it is not free. Method +lookup on such objects goes through `\_\_index` as before, but now `\_\_index` is a +function which has to explicitly look up methods in the class, before doing any +property indexing, which is not going to be as fast as field lookup. If however, +your accessors actually do non-trivial things, then the extra overhead could be +worth it. + +This is not really intended for _access control_ because external code can write +to `mp._a` directly. It is possible to have this kind of control in Lua, but it +again comes with run-time costs. diff --git a/Data/Libraries/Penlight/docs_topics/02-arrays.md b/Data/Libraries/Penlight/docs_topics/02-arrays.md new file mode 100644 index 0000000..9ee292f --- /dev/null +++ b/Data/Libraries/Penlight/docs_topics/02-arrays.md @@ -0,0 +1,649 @@ +## Tables and Arrays + +<a id="list"/> + +### Python-style Lists + +One of the elegant things about Lua is that tables do the job of both lists and +dicts (as called in Python) or vectors and maps, (as called in C++), and they do +it efficiently. However, if we are dealing with 'tables with numerical indices' +we may as well call them lists and look for operations which particularly make +sense for lists. The Penlight `List` class was originally written by Nick Trout +for Lua 5.0, and translated to 5.1 and extended by myself. It seemed that +borrowing from Python was a good idea, and this eventually grew into Penlight. + +Here is an example showing `List` in action; it redefines `__tostring`, so that +it can print itself out more sensibly: + + > List = require 'pl.List' --> automatic with require 'pl' <--- + > l = List() + > l:append(10) + > l:append(20) + > = l + {10,20} + > l:extend {30,40} + {10,20,30,40} + > l:insert(1,5) + {5,10,20,30,40} + > = l:pop() + 40 + > = l + {5,10,20,30} + > = l:index(30) + 4 + > = l:contains(30) + true + > = l:reverse() ---> note: doesn't make a copy! + {30,20,10,5} + +Although methods like `sort` and `reverse` operate in-place and change the list, +they do return the original list. This makes it possible to do _method chaining_, +like `ls = ls:append(10):append(20):reverse():append(1)`. But (and this is an +important but) no extra copy is made, so `ls` does not change identity. `List` +objects (like tables) are _mutable_, unlike strings. If you want a copy of a +list, then `List(ls)` will do the job, i.e. it acts like a copy constructor. +However, if passed any other table, `List` will just set the metatable of the +table and _not_ make a copy. + +A particular feature of Python lists is _slicing_. This is fully supported in +this version of `List`, except we use 1-based indexing. So `List.slice` works +rather like `string.sub`: + + > l = List {10,20,30,40} + > = l:slice(1,1) ---> note: creates a new list! + {10} + > = l:slice(2,2) + {20} + > = l:slice(2,3) + {20,30} + > = l:slice(2,-2) + {20,30} + > = l:slice_assign(2,2,{21,22,23}) + {10,21,22,23,30,40} + > = l:chop(1,1) + {21,22,23,30,40} + +Functions like `slice_assign` and `chop` modify the list; the first is equivalent +to Python`l[i1:i2] = seq` and the second to `del l[i1:i2]`. + +List objects are ultimately just Lua 'list-like' tables, but they have extra +operations defined on them, such as equality and concatention. For regular +tables, equality is only true if the two tables are _identical objects_, whereas +two lists are equal if they have the same contents, i.e. that `l1[i]==l2[i]` for +all elements. + + > l1 = List {1,2,3} + > l2 = List {1,2,3} + > = l1 == l2 + true + > = l1..l2 + {1,2,3,1,2,3} + +The `List` constructor can be passed a function. If so, it's assumed that this is +an iterator function that can be repeatedly called to generate a sequence. One +such function is `io.lines`; the following short, intense little script counts +the number of lines in standard input: + + -- linecount.lua + require 'pl' + ls = List(io.lines()) + print(#ls) + +`List.iterate` captures what `List` considers a sequence. In particular, it can +also iterate over all 'characters' in a string: + + > for ch in List.iterate 'help' do io.write(ch,' ') end + h e l p > + +Since the function `iterate` is used internally by the `List` constructor, +strings can be made into lists of character strings very easily. + +There are a number of operations that go beyond the standard Python methods. For +instance, you can _partition_ a list into a table of sublists using a function. +In the simplest form, you use a predicate (a function returning a boolean value) +to partition the list into two lists, one of elements matching and another of +elements not matching. But you can use any function; if we use `type` then the +keys will be the standard Lua type names. + + > ls = List{1,2,3,4} + > ops = require 'pl.operator' + > ls:partition(function(x) return x > 2 end) + {false={1,2},true={3,4}} + > ls = List{'one',math.sin,List{1},10,20,List{1,2}} + > ls:partition(type) + {function={function: 00369110},string={one},number={10,20},table={{1},{1,2}}} + +This is one `List` method which returns a table which is not a `List`. Bear in +mind that you can always call a `List` method on a plain table argument, so +`List.partition(t,type)` works as expected. But these functions will only operate +on the array part of the table. + +The 'nominal' type of the returned table is `pl.Multimap`, which describes a mapping +between keys and multiple values. This does not mean that `pl.Multimap` is automatically +loaded whenever you use `partition` (or `List` for that matter); this is one of the +standard metatables which are only filled out when the appropriate module is loaded. +This allows tables to be tagged appropriately without causing excessive coupling. + +Stacks occur everywhere in computing. `List` supports stack-like operations; +there is already `pop` (remove and return last value) and `append` acts like +`push` (add a value to the end). `push` is provided as an alias for `append`, and +the other stack operation (size) is simply the size operator `#`. Queues can +also be implemented; you use `pop` to take values out of the queue, and `put` to +insert a value at the begining. + +You may derive classes from `List`, and since the list-returning methods +are covariant, the result of `slice` etc will return lists of the derived type, +not `List`. For instance, consider the specialization of a `List` type that contains +numbers in `tests/test-list.lua`: + + n1 = NA{10,20,30} + n2 = NA{1,2,3} + ns = n1 + 2*n2 + asserteq(ns,{12,24,36}) + min,max = ns:slice(1,2):minmax() + asserteq(T(min,max),T(12,24)) + asserteq(n1:normalize():sum(),1,1e-8) + + +### Map and Set classes + +The `Map` class exposes what Python would call a 'dict' interface, and accesses +the hash part of the table. The name 'Map' is used to emphasize the interface, +not the implementation; it is an object which maps keys onto values; `m['alice']` +or the equivalent `m.alice` is the access operation. This class also provides +explicit `set` and `get` methods, which are trivial for regular maps but get +interesting when `Map` is subclassed. The other operation is `update`, which +extends a map by copying the keys and values from another table, perhaps +overwriting existing keys: + + > Map = require 'pl.Map' + > m = Map{one=1,two=2} + > m:update {three=3,four=4,two=20} + > = m == M{one=1,two=20,three=3,four=4} + true + +The method `values` returns a list of the values, and `keys` returns a list of +the keys; there is no guarantee of order. `getvalues` is given a list of keys and +returns a list of values associated with these keys: + + > m = Map{one=1,two=2,three=3} + > = m:getvalues {'one','three'} + {1,3} + > = m:getvalues(m:keys()) == m:values() + true + +When querying the value of a `Map`, it is best to use the `get` method: + + > print(m:get 'one', m:get 'two') + 1 2 + +The reason is that `m[key]` can be ambiguous; due to the current implementation, +`m["get"]` will always succeed, because if a value is not present in the map, it +will be looked up in the `Map` metatable, which contains a method `get`. There is +currently no simple solution to this annoying restriction. + +There are some useful classes which inherit from `Map`. An `OrderedMap` behaves +like a `Map` but keeps its keys in order if you use its `set` method to add keys +and values. Like all the 'container' classes in Penlight, it defines an `iter` +method for iterating over its values; this will return the keys and values in the +order of insertion; the `keys` and `values` methods likewise. + +A `MultiMap` allows multiple values to be associated with a given key. So `set` +(as before) takes a key and a value, but calling it with the same key and a +different value does not overwrite but adds a new value. `get` (or using `[]`) +will return a list of values. + +A `Set` can be seen as a special kind of `Map`, where all the values are `true`, +the keys are the values, and the order is not important. So in this case +`Set.values` is defined to return a list of the keys. Sets can display +themselves, and the basic operations like `union` (`+`) and `intersection` (`*`) +are defined. + + > Set = require 'pl.Set' + > = Set{'one','two'} == Set{'two','one'} + true + > fruit = Set{'apple','banana','orange'} + > = fruit['banana'] + true + > = fruit['hazelnut'] + nil + > = fruit:values() + {apple,orange,banana} + > colours = Set{'red','orange','green','blue'} + > = fruit,colours + [apple,orange,banana] [blue,green,orange,red] + > = fruit+colours + [blue,green,apple,red,orange,banana] + > = fruit*colours + [orange] + +There are also the functions `Set.difference` and `Set.symmetric_difference`. The +first answers the question 'what fruits are not colours?' and the second 'what +are fruits and colours but not both?' + + > = fruit - colours + [apple,banana] + > = fruit ^ colours + [blue,green,apple,red,banana] + +Adding elements to a set is simply `fruit['peach'] = true` and removing is +`fruit['apple'] = nil` . To make this simplicity work properly, the `Set` class has no +methods - either you use the operator forms or explicitly use `Set.intersect` +etc. In this way we avoid the ambiguity that plagues `Map`. + + +(See `pl.Map` and `pl.Set`) + +### Useful Operations on Tables + +@lookup pl.tablex + +Some notes on terminology: Lua tables are usually _list-like_ (like an array) or +_map-like_ (like an associative array or dict); they can of course have a +list-like and a map-like part. Some of the table operations only make sense for +list-like tables, and some only for map-like tables. (The usual Lua terminology +is the array part and the hash part of the table, which reflects the actual +implementation used; it is more accurate to say that a Lua table is an +associative map which happens to be particularly efficient at acting like an +array.) + +The functions provided in `table` provide all the basic manipulations on Lua +tables, but as we saw with the `List` class, it is useful to build higher-level +operations on top of those functions. For instance, to copy a table involves this +kind of loop: + + local res = {} + for k,v in pairs(T) do + res[k] = v + end + return res + +The `tablex` module provides this as `copy`, which does a _shallow_ copy of a +table. There is also `deepcopy` which goes further than a simple loop in two +ways; first, it also gives the copy the same metatable as the original (so it can +copy objects like `List` above) and any nested tables will also be copied, to +arbitrary depth. There is also `icopy` which operates on list-like tables, where +you can set optionally set the start index of the source and destination as well. +It ensures that any left-over elements will be deleted: + + asserteq(icopy({1,2,3,4,5,6},{20,30}),{20,30}) -- start at 1 + asserteq(icopy({1,2,3,4,5,6},{20,30},2),{1,20,30}) -- start at 2 + asserteq(icopy({1,2,3,4,5,6},{20,30},2,2),{1,30}) -- start at 2, copy from 2 + +(This code from the `tablex` test module shows the use of `pl.test.asserteq`) + +Whereas, `move` overwrites but does not delete the rest of the destination: + + asserteq(move({1,2,3,4,5,6},{20,30}),{20,30,3,4,5,6}) + asserteq(move({1,2,3,4,5,6},{20,30},2),{1,20,30,4,5,6}) + asserteq(move({1,2,3,4,5,6},{20,30},2,2),{1,30,3,4,5,6}) + +(The difference is somewhat like that between C's `strcpy` and `memmove`.) + +To summarize, use `copy` or `deepcopy` to make a copy of an arbitrary table. To +copy into a map-like table, use `update`; to copy into a list-like table use +`icopy`, and `move` if you are updating a range in the destination. + +To complete this set of operations, there is `insertvalues` which works like +`table.insert` except that one provides a table of values to be inserted, and +`removevalues` which removes a range of values. + + asserteq(insertvalues({1,2,3,4},2,{20,30}),{1,20,30,2,3,4}) + asserteq(insertvalues({1,2},{3,4}),{1,2,3,4}) + +Another example: + + > T = require 'pl.tablex' + > t = {10,20,30,40} + > = T.removevalues(t,2,3) + {10,40} + > = T.insertvalues(t,2,{20,30}) + {10,20,30,40} + + +In a similar spirit to `deepcopy`, `deepcompare` will take two tables and return +true only if they have exactly the same values and structure. + + > t1 = {1,{2,3},4} + > t2 = deepcopy(t1) + > = t1 == t2 + false + > = deepcompare(t1,t2) + true + +`find` will return the index of a given value in a list-like table. Note that +like `string.find` you can specify an index to start searching, so that all +instances can be found. There is an optional fourth argument, which makes the +search start at the end and go backwards, so we could define `rfind` like so: + + function rfind(t,val,istart) + return tablex.find(t,val,istart,true) + end + +`find` does a linear search, so it can slow down code that depends on it. If +efficiency is required for large tables, consider using an _index map_. +`index_map` will return a table where the keys are the original values of the +list, and the associated values are the indices. (It is almost exactly the +representation needed for a _set_.) + + > t = {'one','two','three'} + > = tablex.find(t,'two') + 2 + > = tablex.find(t,'four') + nil + > il = tablex.index_map(t) + > = il['two'] + 2 + > = il.two + 2 + +A version of `index_map` called `makeset` is also provided, where the values are +just `true`. This is useful because two such sets can be compared for equality +using `deepcompare`: + + > = deepcompare(makeset {1,2,3},makeset {2,1,3}) + true + +Consider the problem of determining the new employees that have joined in a +period. Assume we have two files of employee names: + + (last-month.txt) + smith,john + brady,maureen + mongale,thabo + + (this-month.txt) + smith,john + smit,johan + brady,maureen + mogale,thabo + van der Merwe,Piet + +To find out differences, just make the employee lists into sets, like so: + + require 'pl' + + function read_employees(file) + local ls = List(io.lines(file)) -- a list of employees + return tablex.makeset(ls) + end + + last = read_employees 'last-month.txt' + this = read_employees 'this-month.txt' + + -- who is in this but not in last? + diff = tablex.difference(this,last) + + -- in a set, the keys are the values... + for e in pairs(diff) do print(e) end + + -- *output* + -- van der Merwe,Piet + -- smit,johan + +The `difference` operation is easy to write and read: + + for e in pairs(this) do + if not last[e] then + print(e) + end + end + +Using `difference` here is not that it is a tricky thing to code, it is that you +are stating your intentions clearly to other readers of your code. (And naturally +to your future self, in six months time.) + +`find_if` will search a table using a function. The optional third argument is a +value which will be passed as a second argument to the function. `pl.operator` +provides the Lua operators conveniently wrapped as functions, so the basic +comparison functions are available: + + > ops = require 'pl.operator' + > = tablex.find_if({10,20,30,40},ops.gt,20) + 3 true + +Note that `find_if` will also return the _actual value_ returned by the function, +which of course is usually just `true` for a boolean function, but any value +which is not `nil` and not `false` can be usefully passed back. + +`deepcompare` does a thorough recursive comparison, but otherwise using the +default equality operator. `compare` allows you to specify exactly what function +to use when comparing two list-like tables, and `compare_no_order` is true if +they contain exactly the same elements. Do note that the latter does not need an +explicit comparison function - in this case the implementation is actually to +compare the two sets, as above: + + > = compare_no_order({1,2,3},{2,1,3}) + true + > = compare_no_order({1,2,3},{2,1,3},'==') + true + +(Note the special string '==' above; instead of saying `ops.gt` or `ops.eq` we +can use the strings '>' or '==' respectively.) + +`sort` and `sortv` return iterators that will iterate through the +sorted elements of a table. `sort` iterates by sorted key order, and +`sortv` iterates by sorted value order. For example, given a table +with names and ages, it is trivial to iterate over the elements: + + > t = {john=27,jane=31,mary=24} + > for name,age in tablex.sort(t) do print(name,age) end + jane 31 + john 27 + mary 24 + > for name,age in tablex.sortv(t) do print(name,age) end + mary 24 + john 27 + jane 31 + +There are several ways to merge tables in PL. If they are list-like, then see the +operations defined by `pl.List`, like concatenation. If they are map-like, then +`merge` provides two basic operations. If the third arg is false, then the result +only contains the keys that are in common between the two tables, and if true, +then the result contains all the keys of both tables. These are in fact +generalized set union and intersection operations: + + > S1 = {john=27,jane=31,mary=24} + > S2 = {jane=31,jones=50} + > = tablex.merge(S1, S2, false) + {jane=31} + > = tablex.merge(S1, S2, true) + {mary=24,jane=31,john=27,jones=50} + +When working with tables, you will often find yourself writing loops like in the +first example. Loops are second nature to programmers, but they are often not the +most elegant and self-describing way of expressing an operation. Consider the +`map` function, which creates a new table by applying a function to each element +of the original: + + > = map(math.sin, {1,2,3,4}) + { 0.84, 0.91, 0.14, -0.76} + > = map(function(x) return x*x end, {1,2,3,4}) + {1,4,9,16} + +`map` saves you from writing a loop, and the resulting code is often clearer, as +well as being shorter. This is not to say that 'loops are bad' (although you will +hear that from some extremists), just that it's good to capture standard +patterns. Then the loops you do write will stand out and acquire more significance. + +`pairmap` is interesting, because the function works with both the key and the +value. + + > t = {fred=10,bonzo=20,alice=4} + > = pairmap(function(k,v) return v end, t) + {4,10,20} + > = pairmap(function(k,v) return k end, t) + {'alice','fred','bonzo'} + +(These are common enough operations that the first is defined as `values` and the +second as `keys`.) If the function returns two values, then the _second_ value is +considered to be the new key: + + > = pairmap(t,function(k,v) return v+10, k:upper() end) + {BONZO=30,FRED=20,ALICE=14} + +`map2` applies a function to two tables: + + > map2(ops.add,{1,2},{10,20}) + {11,22} + > map2('*',{1,2},{10,20}) + {10,40} + +The various map operations generate tables; `reduce` applies a function of two +arguments over a table and returns the result as a scalar: + + > reduce ('+', {1,2,3}) + 6 + > reduce ('..', {'one','two','three'}) + 'onetwothree' + +Finally, `zip` sews different tables together: + + > = zip({1,2,3},{10,20,30}) + {{1,10},{2,20},{3,30}} + +Browsing through the documentation, you will find that `tablex` and `List` share +methods. For instance, `tablex.imap` and `List.map` are basically the same +function; they both operate over the array-part of the table and generate another +table. This can also be expressed as a _list comprehension_ `C 'f(x) for x' (t)` +which makes the operation more explicit. So why are there different ways to do +the same thing? The main reason is that not all tables are Lists: the expression +`ls:map('#')` will return a _list_ of the lengths of any elements of `ls`. A list +is a thin wrapper around a table, provided by the metatable `List`. Sometimes you +may wish to work with ordinary Lua tables; the `List` interface is not a +compulsory way to use Penlight table operations. + +### Operations on two-dimensional tables + +@lookup pl.array2d + +Two-dimensional tables are of course easy to represent in Lua, for instance +`{{1,2},{3,4}}` where we store rows as subtables and index like so `A[col][row]`. +This is the common representation used by matrix libraries like +[LuaMatrix](http://lua-users.org/wiki/LuaMatrix). `pl.array2d` does not provide +matrix operations, since that is the job for a specialized library, but rather +provides generalizations of the higher-level operations provided by `pl.tablex` +for one-dimensional arrays. + +`iter` is a useful generalization of `ipairs`. (The extra parameter determines +whether you want the indices as well.) + + > a = {{1,2},{3,4}} + > for i,j,v in array2d.iter(a,true) do print(i,j,v) end + 1 1 1 + 1 2 2 + 2 1 3 + 2 2 4 + +Note that you can always convert an arbitrary 2D array into a 'list of lists' +with `List(tablex.map(List,a))` + +`map` will apply a function over all elements (notice that extra arguments can be +provided, so this operation is in effect `function(x) return x-1 end`) + + > array2d.map('-',a,1) + {{0,1},{2,3}} + +2D arrays are stored as an array of rows, but columns can be extracted: + + > array2d.column(a,1) + {1,3} + +There are three equivalents to `tablex.reduce`. You can either reduce along the +rows (which is the most efficient) or reduce along the columns. Either one will +give you a 1D array. And `reduce2` will apply two operations: the first one +reduces the rows, and the second reduces the result. + + > array2d.reduce_rows('+',a) + {3,7} + > array2d.reduce_cols('+',a) + {4,6} + > -- same as tablex.reduce('*',array.reduce_rows('+',a)) + > array2d.reduce2('*','+',a) + 21 ` + +`tablex.map2` applies an operation to two tables, giving another table. +`array2d.map2` does this for 2D arrays. Note that you have to provide the _rank_ +of the arrays involved, since it's hard to always correctly deduce this from the +data: + + > b = {{10,20},{30,40}} + > a = {{1,2},{3,4}} + > = array2d.map2('+',2,2,a,b) -- two 2D arrays + {{11,22},{33,44}} + > = array2d.map2('+',1,2,{10,100},a) -- 1D, 2D + {{11,102},{13,104}} + > = array2d.map2('*',2,1,a,{1,-1}) -- 2D, 1D + {{1,-2},{3,-4}} + +Of course, you are not limited to simple arithmetic. Say we have a 2D array of +strings, and wish to print it out with proper right justification. The first step +is to create all the string lengths by mapping `string.len` over the array, the +second is to reduce this along the columns using `math.max` to get maximum column +widths, and last, apply `stringx.rjust` with these widths. + + maxlens = reduce_cols(math.max,map('#',lines)) + lines = map2(stringx.rjust,2,1,lines,maxlens) + +There is `product` which returns the _Cartesian product_ of two 1D arrays. The +result is a 2D array formed from applying the function to all possible pairs from +the two arrays. + + > array2d.product('{}',{1,2},{'a','b'}) + {{{1,'b'},{2,'a'}},{{1,'a'},{2,'b'}}} + +There is a set of operations which work in-place on 2D arrays. You can +`swap_rows` and `swap_cols`; the first really is a simple one-liner, but the idea +here is to give the operation a name. `remove_row` and `remove_col` are +generalizations of `table.remove`. Likewise, `extract_rows` and `extract_cols` +are given arrays of indices and discard anything else. So, for instance, +`extract_cols(A,{2,4})` will leave just columns 2 and 4 in the array. + +`List.slice` is often useful on 1D arrays; `slice` does the same thing, but is +generally given a start (row,column) and a end (row,column). + + > A = {{1,2,3},{4,5,6},{7,8,9}} + > B = slice(A,1,1,2,2) + > write(B) + 1 2 + 4 5 + > B = slice(A,2,2) + > write(B,nil,'%4.1f') + 5.0 6.0 + 8.0 9.0 + +Here `write` is used to print out an array nicely; the second parameter is `nil`, +which is the default (stdout) but can be any file object and the third parameter +is an optional format (as used in `string.format`). + +`parse_range` will take a spreadsheet range like 'A1:B2' or 'R1C1:R2C2' and +return the range as four numbers, which can be passed to `slice`. The rule is +that `slice` will return an array of the appropriate shape depending on the +range; if a range represents a row or a column, the result is 1D, otherwise 2D. + +This applies to `iter` as well, which can also optionally be given a range: + + + > for i,j,v in iter(A,true,2,2) do print(i,j,v) end + 2 2 5 + 2 3 6 + 3 2 8 + 3 3 9 + +`new` will construct a new 2D array with the given dimensions. You provide an +initial value for the elements, which is interpreted as a function if it's +callable. With `L` being `utils.string_lambda` we then have the following way to +make an _identity matrix_: + + asserteq( + array.new(3,3,L'|i,j| i==j and 1 or 0'), + {{1,0,0},{0,1,0},{0,0,1}} + ) + +Please note that most functions in `array2d` are _covariant_, that is, they +return an array of the same type as they receive. In particular, any objects +created with `data.new` or `matrix.new` will remain data or matrix objects when +reshaped or sliced, etc. Data objects have the `array2d` functions available as +methods. + + diff --git a/Data/Libraries/Penlight/docs_topics/03-strings.md b/Data/Libraries/Penlight/docs_topics/03-strings.md new file mode 100644 index 0000000..3808175 --- /dev/null +++ b/Data/Libraries/Penlight/docs_topics/03-strings.md @@ -0,0 +1,228 @@ +## Strings. Higher-level operations on strings. + +### Extra String Methods + +@lookup pl.stringx + +These are convenient borrowings from Python, as described in 3.6.1 of the Python +reference, but note that indices in Lua always begin at one. `stringx` defines +functions like `isalpha` and `isdigit`, which return `true` if s is only composed +of letters or digits respectively. `startswith` and `endswith` are convenient +ways to find substrings. (`endswith` works as in Python 2.5, so that `f:endswith +{'.bat','.exe','.cmd'}` will be true for any filename which ends with these +extensions.) There are justify methods and whitespace trimming functions like +`strip`. + + > stringx.import() + > ('bonzo.dog'):endswith {'.dog','.cat'} + true + > ('bonzo.txt'):endswith {'.dog','.cat'} + false + > ('bonzo.cat'):endswith {'.dog','.cat'} + true + > (' stuff'):ljust(20,'+') + '++++++++++++++ stuff' + > (' stuff '):lstrip() + 'stuff ' + > (' stuff '):rstrip() + ' stuff' + > (' stuff '):strip() + 'stuff' + > for s in ('one\ntwo\nthree\n'):lines() do print(s) end + one + two + three + +Most of these can be fairly easily implemented using the Lua string library, +which is more general and powerful. But they are convenient operations to have +easily at hand. Note that can be injected into the `string` table if you use +`stringx.import`, but a simple alias like `local stringx = require 'pl.stringx'` +is preferrable. This is the recommended practice when writing modules for +consumption by other people, since it is bad manners to change the global state +of the rest of the system. Magic may be used for convenience, but there is always +a price. + + +### String Templates + +@lookup pl.text + +Another borrowing from Python, string templates allow you to substitute values +looked up in a table: + + local Template = require ('pl.text').Template + t = Template('${here} is the $answer') + print(t:substitute {here = 'Lua', answer = 'best'}) + ==> + Lua is the best + +'$ variables' can optionally have curly braces; this form is useful if you are +glueing text together to make variables, e.g `${prefix}_name_${postfix}`. The +`substitute` method will throw an error if a $ variable is not found in the +table, and the `safe_substitute` method will not. + +The Lua implementation has an extra method, `indent_substitute` which is very +useful for inserting blocks of text, because it adjusts indentation. Consider +this example: + + -- testtemplate.lua + local Template = require ('pl.text').Template + + t = Template [[ + for i = 1,#$t do + $body + end + ]] + + body = Template [[ + local row = $t[i] + for j = 1,#row do + fun(row[j]) + end + ]] + + print(t:indent_substitute {body=body,t='tbl'}) + +And the output is: + + for i = 1,#tbl do + local row = tbl[i] + for j = 1,#row do + fun(row[j]) + end + end + +`indent_substitute` can substitute templates, and in which case they themselves +will be substituted using the given table. So in this case, `$t` was substituted +twice. + +`pl.text` also has a number of useful functions like `dedent`, which strips all +the initial indentation from a multiline string. As in Python, this is useful for +preprocessing multiline strings if you like indenting them with your code. The +function `wrap` is passed a long string (a _paragraph_) and returns a list of +lines that fit into a desired line width. As an extension, there is also `indent` +for indenting multiline strings. + +New in Penlight with the 0.9 series is `text.format_operator`. Calling this +enables Python-style string formating using the modulo operator `%`: + + > text.format_operator() + > = '%s[%d]' % {'dog',1} + dog[1] + +So in its simplest form it saves the typing involved with `string.format`; it +will also expand `$` variables using named fields: + + > = '$animal[$num]' % {animal='dog',num=1} + dog[1] + +As with `stringx.import` you have to do this explicitly, since all strings share the same +metatable. But in your own scripts you can feel free to do this. + +### Another Style of Template + +A new module is `template`, which is a version of Rici Lake's [Lua +Preprocessor](http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor). This +allows you to mix Lua code with your templates in a straightforward way. There +are only two rules: + + - Lines begining with `#` are Lua + - Otherwise, anything inside `$()` is a Lua expression. + +So a template generating an HTML list would look like this: + + <ul> + # for i,val in ipairs(T) do + <li>$(i) = $(val:upper())</li> + # end + </ul> + +Assume the text is inside `tmpl`, then the template can be expanded using: + + local template = require 'pl.template' + local my_env = { + ipairs = ipairs, + T = {'one','two','three'} + } + res = template.substitute(tmpl, my_env) + +and we get + + <ul> + <li>1 = ONE</li> + <li>2 = TWO</li> + <li>3 = THREE</li> + </ul> + +There is a single function, `template.substitute` which is passed a template +string and an environment table. This table may contain some special fields, +like `\_parent` which can be set to a table representing a 'fallback' environment +in case a symbol was not found. `\_brackets` is usually '()' and `\_escape` is +usually '#' but it's sometimes necessary to redefine these if the defaults +interfere with the target language - for instance, `$(V)` has another meaning in +Make, and `#` means a preprocessor line in C/C++. + +Finally, if something goes wrong, passing `_debug` will cause the intermediate +Lua code to be dumped if there's a problem. + +Here is a C code generation example; something that could easily be extended to +be a minimal Lua extension skeleton generator. + + local subst = require 'pl.template'.substitute + + local templ = [[ + #include <lua.h> + #include <lauxlib.h> + #include <lualib.h> + + > for _,f in ipairs(mod) do + static int l_$(f.name) (lua_State *L) { + + } + > end + + static const luaL_reg $(mod.name)[] = { + > for _,f in ipairs(mod) do + {"$(f.name)",l_$(f.name)}, + > end + {NULL,NULL} + }; + + int luaopen_$(mod.name) { + luaL_register (L, "$(mod.name)", $(mod.name)); + return 1; + } + ]] + + print(subst(templ,{ + _escape = '>', + ipairs = ipairs, + mod = { + name = 'baggins'; + {name='frodo'}, + {name='bilbo'} + } + })) + + +### File-style I/O on Strings + +`pl.stringio` provides just three functions; `stringio.open` is passed a string, +and returns a file-like object for reading. It supports a `read` method, which +takes the same arguments as standard file objects: + + > f = stringio.open 'first line\n10 20 30\n' + > = f:read() + first line + > = f:read('*n','*n','*n') + 10 20 30 + +`lines` and `seek` are also supported. + +`stringio.lines` is a useful short-cut for iterating over all the lines in a +string. + +`stringio.create` creates a writeable file-like object. You then use `write` to +this stream, and finally extract the builded string using `value`. This 'string +builder' pattern is useful for efficiently creating large strings. + diff --git a/Data/Libraries/Penlight/docs_topics/04-paths.md b/Data/Libraries/Penlight/docs_topics/04-paths.md new file mode 100644 index 0000000..4367fe6 --- /dev/null +++ b/Data/Libraries/Penlight/docs_topics/04-paths.md @@ -0,0 +1,170 @@ +## Paths and Directories + +### Working with Paths + +Programs should not depend on quirks of your operating system. They will be +harder to read, and need to be ported for other systems. The worst of course is +hardcoding paths like 'c:\\' in programs, and wondering why Vista complains so +much. But even something like `dir..'\\'..file` is a problem, since Unix can't +understand backslashes in this way. `dir..'/'..file` is _usually_ portable, but +it's best to put this all into a simple function, `path.join`. If you +consistently use `path.join`, then it's much easier to write cross-platform code, +since it handles the directory separator for you. + +`pl.path` provides the same functionality as Python's `os.path` module (11.1). + + > p = 'c:\\bonzo\\DOG.txt' + > = path.normcase (p) ---> only makes sense on Windows + c:\bonzo\dog.txt + > = path.splitext (p) + c:\bonzo\DOG .txt + > = path.extension (p) + .txt + > = path.basename (p) + DOG.txt + > = path.exists(p) + false + > = path.join ('fred','alice.txt') + fred\alice.txt + > = path.exists 'pretty.lua' + true + > = path.getsize 'pretty.lua' + 2125 + > = path.isfile 'pretty.lua' + true + > = path.isdir 'pretty.lua' + false + + +It is very important for all programmers, not just on Unix, to only write to +where they are allowed to write. `path.expanduser` will expand '~' (tilde) into +the home directory. Depending on your OS, this will be a guaranteed place where +you can create files: + + > = path.expanduser '~/mydata.txt' + 'C:\Documents and Settings\SJDonova/mydata.txt' + + > = path.expanduser '~/mydata.txt' + /home/sdonovan/mydata.txt + +Under Windows, `os.tmpname` returns a path which leads to your drive root full of +temporary files. (And increasingly, you do not have access to this root folder.) +This is corrected by `path.tmpname`, which uses the environment variable TMP: + + > os.tmpname() -- not a good place to put temporary files! + '\s25g.' + > path.tmpname() + 'C:\DOCUME~1\SJDonova\LOCALS~1\Temp\s25g.1' + + +A useful extra function is `pl.path.package_path`, which will tell you the path +of a particular Lua module. So on my system, `package_path('pl.path')` returns +'C:\Program Files\Lua\5.1\lualibs\pl\path.lua', and `package_path('ifs')` returns +'C:\Program Files\Lua\5.1\clibs\lfs.dll'. It is implemented in terms of +`package.searchpath`, which is a new function in Lua 5.2 which has been +implemented for Lua 5.1 in Penlight. + +### File Operations + +`pl.file` is a new module that provides more sensible names for common file +operations. For instance, `file.read` and `file.write` are aliases for +`utils.readfile` and `utils.writefile`. + +Smaller files can be efficiently read and written in one operation. `file.read` +is passed a filename and returns the contents as a string, if successful; if not, +then it returns `nil` and the actual error message. There is an optional boolean +parameter if you want the file to be read in binary mode (this makes no +difference on Unix but remains important with Windows.) + +In previous versions of Penlight, `utils.readfile` would read standard input if +the file was not specified, but this can lead to nasty bugs; use `io.read '*a'` +to grab all of standard input. + +Similarly, `file.write` takes a filename and a string which will be written to +that file. + +For example, this little script converts a file into upper case: + + require 'pl' + assert(#arg == 2, 'supply two filenames') + text = assert(file.read(arg[1])) + assert(file.write(arg[2],text:upper())) + +Copying files is suprisingly tricky. `file.copy` and `file.move` attempt to use +the best implementation possible. On Windows, they link to the API functions +`CopyFile` and `MoveFile`, but only if the `alien` package is installed (this is +true for Lua for Windows.) Otherwise, the system copy command is used. This can +be ugly when writing Windows GUI applications, because of the dreaded flashing +black-box problem with launching processes. + +### Directory Operations + +`pl.dir` provides some useful functions for working with directories. `fnmatch` +will match a filename against a shell pattern, and `filter` will return any files +in the supplied list which match the given pattern, which correspond to the +functions in the Python `fnmatch` module. `getdirectories` will return all +directories contained in a directory, and `getfiles` will return all files in a +directory which match a shell pattern. These functions return the files as a +table, unlike `lfs.dir` which returns an iterator.) + +`dir.makepath` can create a full path, creating subdirectories as necessary; +`rmtree` is the Nuclear Option of file deleting functions, since it will +recursively clear out and delete all directories found begining at a path (there +is a similar function with this name in the Python `shutils` module.) + + > = dir.makepath 't\\temp\\bonzo' + > = path.isdir 't\\temp\\bonzo' + true + > = dir.rmtree 't' + +`dir.rmtree` depends on `dir.walk`, which is a powerful tool for scanning a whole +directory tree. Here is the implementation of `dir.rmtree`: + + --- remove a whole directory tree. + -- @param path A directory path + function dir.rmtree(fullpath) + for root,dirs,files in dir.walk(fullpath) do + for i,f in ipairs(files) do + os.remove(path.join(root,f)) + end + lfs.rmdir(root) + end + end + + +`dir.clonetree` clones directory trees. The first argument is a path that must +exist, and the second path is the path to be cloned. (Note that this path cannot +be _inside_ the first path, since this leads to madness.) By default, it will +then just recreate the directory structure. You can in addition provide a +function, which will be applied for all files found. + + -- make a copy of my libs folder + require 'pl' + p1 = [[d:\dev\lua\libs]] + p2 = [[D:\dev\lua\libs\..\tests]] + dir.clonetree(p1,p2,dir.copyfile) + +A more sophisticated version, which only copies files which have been modified: + + -- p1 and p2 as before, or from arg[1] and arg[2] + dir.clonetree(p1,p2,function(f1,f2) + local res + local t1,t2 = path.getmtime(f1),path.getmtime(f2) + -- f2 might not exist, so be careful about t2 + if not t2 or t1 > t2 then + res = dir.copyfile(f1,f2) + end + return res -- indicates successful operation + end) + +`dir.clonetree` uses `path.common_prefix`. With `p1` and `p2` defined above, the +common path is 'd:\dev\lua'. So 'd:\dev\lua\libs\testfunc.lua' is copied to +'d:\dev\lua\test\testfunc.lua', etc. + +If you need to find the common path of list of files, then `tablex.reduce` will +do the job: + + > p3 = [[d:\dev]] + > = tablex.reduce(path.common_prefix,{p1,p2,p3}) + 'd:\dev' + diff --git a/Data/Libraries/Penlight/docs_topics/05-dates.md b/Data/Libraries/Penlight/docs_topics/05-dates.md new file mode 100644 index 0000000..32c42f7 --- /dev/null +++ b/Data/Libraries/Penlight/docs_topics/05-dates.md @@ -0,0 +1,111 @@ +## Date and Time + +<a id="date"></a> + +NOTE: the Date module is deprecated + +### Creating and Displaying Dates + +The `Date` class provides a simplified way to work with [date and +time](http://www.lua.org/pil/22.1.html) in Lua; it leans heavily on the functions +`os.date` and `os.time`. + +A `Date` object can be constructed from a table, just like with `os.time`. +Methods are provided to get and set the various parts of the date. + + > d = Date {year = 2011, month = 3, day = 2 } + > = d + 2011-03-02 12:00:00 + > = d:month(),d:year(),d:day() + 3 2011 2 + > d:month(4) + > = d + 2011-04-02 12:00:00 + > d:add {day=1} + > = d + 2011-04-03 12:00:00 + +`add` takes a table containing one of the date table fields. + + > = d:weekday_name() + Sun + > = d:last_day() + 2011-04-30 12:00:00 + > = d:month_name(true) + April + +There is a default conversion to text for date objects, but `Date.Format` gives +you full control of the format for both parsing and displaying dates: + + > iso = Date.Format 'yyyy-mm-dd' + > d = iso:parse '2010-04-10' + > amer = Date.Format 'mm/dd/yyyy' + > = amer:tostring(d) + 04/10/2010 + +With the 0.9.7 relase, the `Date` constructor has become more flexible. You may +omit any of the 'year', 'month' or 'day' fields: + + > = Date { year = 2008 } + 2008-01-01 12:00:00 + > = Date { month = 3 } + 2011-03-01 12:00:00 + > = Date { day = 20 } + 2011-10-20 12:00:00 + > = Date { hour = 14, min = 30 } + 2011-10-13 14:30:00 + +If 'year' is omitted, then the current year is assumed, and likewise for 'month'. + +To set the time on such a partial date, you can use the fact that the 'setter' +methods return the date object and so you can 'chain' these methods. + + > d = Date { day = 03 } + > = d:hour(18):min(30) + 2011-10-03 18:30:00 + +Finally, `Date` also now accepts positional arguments: + + > = Date(2011,10,3) + 2011-10-03 12:00:00 + > = Date(2011,10,3,18,30,23) + 2011-10-03 18:30:23 + +`Date.format` has been extended. If you construct an instance without a pattern, +then it will try to match against a set of known formats. This is useful for +human-input dates since keeping to a strict format is not one of the strong +points of users. It assumes that there will be a date, and then a date. + + > df = Date.Format() + > = df:parse '5.30pm' + 2011-10-13 17:30:00 + > = df:parse '1730' + nil day out of range: 1730 is not between 1 and 31 + > = df:parse '17.30' + 2011-10-13 17:30:00 + > = df:parse 'mar' + 2011-03-01 12:00:00 + > = df:parse '3 March' + 2011-03-03 12:00:00 + > = df:parse '15 March' + 2011-03-15 12:00:00 + > = df:parse '15 March 2008' + 2008-03-15 12:00:00 + > = df:parse '15 March 2008 1.30pm' + 2008-03-15 13:30:00 + > = df:parse '2008-10-03 15:30:23' + 2008-10-03 15:30:23 + +ISO date format is of course a good idea if you need to deal with users from +different countries. Here is the default behaviour for 'short' dates: + + > = df:parse '24/02/12' + 2012-02-24 12:00:00 + +That's not what Americans expect! It's tricky to work out in a cross-platform way +exactly what the expected format is, so there is an explicit flag: + + > df:US_order(true) + > = df:parse '9/11/01' + 2001-11-09 12:00:00 + diff --git a/Data/Libraries/Penlight/docs_topics/06-data.md b/Data/Libraries/Penlight/docs_topics/06-data.md new file mode 100644 index 0000000..e067b6b --- /dev/null +++ b/Data/Libraries/Penlight/docs_topics/06-data.md @@ -0,0 +1,1262 @@ +## Data + +### Reading Data Files + +The first thing to consider is this: do you actually need to write a custom file +reader? And if the answer is yes, the next question is: can you write the reader +in as clear a way as possible? Correctness, Robustness, and Speed; pick the first +two and the third can be sorted out later, _if necessary_. + +A common sort of data file is the configuration file format commonly used on Unix +systems. This format is often called a _property_ file in the Java world. + + # Read timeout in seconds + read.timeout=10 + + # Write timeout in seconds + write.timeout=10 + +Here is a simple Lua implementation: + + -- property file parsing with Lua string patterns + props = [] + for line in io.lines() do + if line:find('#',1,true) ~= 1 and not line:find('^%s*$') then + local var,value = line:match('([^=]+)=(.*)') + props[var] = value + end + end + +Very compact, but it suffers from a similar disease in equivalent Perl programs; +it uses odd string patterns which are 'lexically noisy'. Noisy code like this +slows the casual reader down. (For an even more direct way of doing this, see the +next section, 'Reading Configuration Files') + +Another implementation, using the Penlight libraries: + + -- property file parsing with extended string functions + require 'pl' + stringx.import() + props = [] + for line in io.lines() do + if not line:startswith('#') and not line:isspace() then + local var,value = line:splitv('=') + props[var] = value + end + end + +This is more self-documenting; it is generally better to make the code express +the _intention_, rather than having to scatter comments everywhere - comments are +necessary, of course, but mostly to give the higher view of your intention that +cannot be expressed in code. It is slightly slower, true, but in practice the +speed of this script is determined by I/O, so further optimization is unnecessary. + +### Reading Unstructured Text Data + +Text data is sometimes unstructured, for example a file containing words. The +`pl.input` module has a number of functions which makes processing such files +easier. For example, a script to count the number of words in standard input +using `import.words`: + + -- countwords.lua + require 'pl' + local k = 1 + for w in input.words(io.stdin) do + k = k + 1 + end + print('count',k) + +Or this script to calculate the average of a set of numbers using `input.numbers`: + + -- average.lua + require 'pl' + local k = 1 + local sum = 0 + for n in input.numbers(io.stdin) do + sum = sum + n + k = k + 1 + end + print('average',sum/k) + +These scripts can be improved further by _eliminating loops_ In the last case, +there is a perfectly good function `seq.sum` which can already take a sequence of +numbers and calculate these numbers for us: + + -- average2.lua + require 'pl' + local total,n = seq.sum(input.numbers()) + print('average',total/n) + +A further simplification here is that if `numbers` or `words` are not passed an +argument, they will grab their input from standard input. The first script can +be rewritten: + + -- countwords2.lua + require 'pl' + print('count',seq.count(input.words())) + +A useful feature of a sequence generator like `numbers` is that it can read from +a string source. Here is a script to calculate the sums of the numbers on each +line in a file: + + -- sums.lua + for line in io.lines() do + print(seq.sum(input.numbers(line)) + end + +### Reading Columnar Data + +It is very common to find data in columnar form, either space or comma-separated, +perhaps with an initial set of column headers. Here is a typical example: + + EventID Magnitude LocationX LocationY LocationZ + 981124001 2.0 18988.4 10047.1 4149.7 + 981125001 0.8 19104.0 9970.4 5088.7 + 981127003 0.5 19012.5 9946.9 3831.2 + ... + +`input.fields` is designed to extract several columns, given some delimiter +(default to whitespace). Here is a script to calculate the average X location of +all the events: + + -- avg-x.lua + require 'pl' + io.read() -- skip the header line + local sum,count = seq.sum(input.fields {3}) + print(sum/count) + +`input.fields` is passed either a field count, or a list of column indices, +starting at one as usual. So in this case we're only interested in column 3. If +you pass it a field count, then you get every field up to that count: + + for id,mag,locX,locY,locZ in input.fields (5) do + .... + end + +`input.fields` by default tries to convert each field to a number. It will skip +lines which clearly don't match the pattern, but will abort the script if there +are any fields which cannot be converted to numbers. + +The second parameter is a delimiter, by default spaces. ' ' is understood to mean +'any number of spaces', i.e. '%s+'. Any Lua string pattern can be used. + +The third parameter is a _data source_, by default standard input (defined by +`input.create_getter`.) It assumes that the data source has a `read` method which +brings in the next line, i.e. it is a 'file-like' object. As a special case, a +string will be split into its lines: + + > for x,y in input.fields(2,' ','10 20\n30 40\n') do print(x,y) end + 10 20 + 30 40 + +Note the default behaviour for bad fields, which is to show the offending line +number: + + > for x,y in input.fields(2,' ','10 20\n30 40x\n') do print(x,y) end + 10 20 + line 2: cannot convert '40x' to number + +This behaviour of `input.fields` is appropriate for a script which you want to +fail immediately with an appropriate _user_ error message if conversion fails. +The fourth optional parameter is an options table: `{no_fail=true}` means that +conversion is attempted but if it fails it just returns the string, rather as AWK +would operate. You are then responsible for checking the type of the returned +field. `{no_convert=true}` switches off conversion altogether and all fields are +returned as strings. + +@lookup pl.data + +Sometimes it is useful to bring a whole dataset into memory, for operations such +as extracting columns. Penlight provides a flexible reader specifically for +reading this kind of data, using the `data` module. Given a file looking like this: + + x,y + 10,20 + 2,5 + 40,50 + +Then `data.read` will create a table like this, with each row represented by a +sublist: + + > t = data.read 'test.txt' + > pretty.dump(t) + {{10,20},{2,5},{40,50},fieldnames={'x','y'},delim=','} + +You can now analyze this returned table using the supplied methods. For instance, +the method `column_by_name` returns a table of all the values of that column. + + -- testdata.lua + require 'pl' + d = data.read('fev.txt') + for _,name in ipairs(d.fieldnames) do + local col = d:column_by_name(name) + if type(col[1]) == 'number' then + local total,n = seq.sum(col) + utils.printf("Average for %s is %f\n",name,total/n) + end + end + +`data.read` tries to be clever when given data; by default it expects a first +line of column names, unless any of them are numbers. It tries to deduce the +column delimiter by looking at the first line. Sometimes it guesses wrong; these +things can be specified explicitly. The second optional parameter is an options +table: can override `delim` (a string pattern), `fieldnames` (a list or +comma-separated string), specify `no_convert` (default is to convert), numfields +(indices of columns known to be numbers, as a list) and `thousands_dot` (when the +thousands separator in Excel CSV is '.') + +A very powerful feature is a way to execute SQL-like queries on such data: + + -- queries on tabular data + require 'pl' + local d = data.read('xyz.txt') + local q = d:select('x,y,z where x > 3 and z < 2 sort by y') + for x,y,z in q do + print(x,y,z) + end + +Please note that the format of queries is restricted to the following syntax: + + FIELDLIST [ 'where' CONDITION ] [ 'sort by' FIELD [asc|desc]] + +Any valid Lua code can appear in `CONDITION`; remember it is _not_ SQL and you +have to use `==` (this warning comes from experience.) + +For this to work, _field names must be Lua identifiers_. So `read` will massage +fieldnames so that all non-alphanumeric chars are replaced with underscores. +However, the `original_fieldnames` field always contains the original un-massaged +fieldnames. + +`read` can handle standard CSV files fine, although doesn't try to be a +full-blown CSV parser. With the `csv=true` option, it's possible to have +double-quoted fields, which may contain commas; then trailing commas become +significant as well. + +Spreadsheet programs are not always the best tool to +process such data, strange as this might seem to some people. This is a toy CSV +file; to appreciate the problem, imagine thousands of rows and dozens of columns +like this: + + Department Name,Employee ID,Project,Hours Booked + sales,1231,overhead,4 + sales,1255,overhead,3 + engineering,1501,development,5 + engineering,1501,maintenance,3 + engineering,1433,maintenance,10 + +The task is to reduce the dataset to a relevant set of rows and columns, perhaps +do some processing on row data, and write the result out to a new CSV file. The +`write_row` method uses the delimiter to write the row to a file; +`Data.select_row` is like `Data.select`, except it iterates over _rows_, not +fields; this is necessary if we are dealing with a lot of columns! + + names = {[1501]='don',[1433]='dilbert'} + keepcols = {'Employee_ID','Hours_Booked'} + t:write_row (outf,{'Employee','Hours_Booked'}) + q = t:select_row { + fields=keepcols, + where=function(row) return row[1]=='engineering' end + } + for row in q do + row[1] = names[row[1]] + t:write_row(outf,row) + end + +`Data.select_row` and `Data.select` can be passed a table specifying the query; a +list of field names, a function defining the condition and an optional parameter +`sort_by`. It isn't really necessary here, but if we had a more complicated row +condition (such as belonging to a specified set) then it is not generally +possible to express such a condition as a query string, without resorting to +hackery such as global variables. + +With 1.0.3, you can specify explicit conversion functions for selected columns. +For instance, this is a log file with a Unix date stamp: + + Time Message + 1266840760 +# EE7C0600006F0D00C00F06010302054000000308010A00002B00407B00 + 1266840760 closure data 0.000000 1972 1972 0 + 1266840760 ++ 1266840760 EE 1 + 1266840760 +# EE7C0600006F0D00C00F06010302054000000408020A00002B00407B00 + 1266840764 closure data 0.000000 1972 1972 0 + +We would like the first column as an actual date object, so the `convert` +field sets an explicit conversion for column 1. (Note that we have to explicitly +convert the string to a number first.) + + Date = require 'pl.Date' + + function date_convert (ds) + return Date(tonumber(ds)) + end + + d = data.read(f,{convert={[1]=date_convert},last_field_collect=true}) + +This gives us a two-column dataset, where the first column contains `Date` objects +and the second column contains the rest of the line. Queries can then easily +pick out events on a day of the week: + + q = d:select "Time,Message where Time:weekday_name()=='Sun'" + +Data does not have to come from files, nor does it necessarily come from the lab +or the accounts department. On Linux, `ps aux` gives you a full listing of all +processes running on your machine. It is straightforward to feed the output of +this command into `data.read` and perform useful queries on it. Notice that +non-identifier characters like '%' get converted into underscores: + + require 'pl' + f = io.popen 'ps aux' + s = data.read (f,{last_field_collect=true}) + f:close() + print(s.fieldnames) + print(s:column_by_name 'USER') + qs = 'COMMAND,_MEM where _MEM > 5 and USER=="steve"' + for name,mem in s:select(qs) do + print(mem,name) + end + +I've always been an admirer of the AWK programming language; with `filter` you +can get Lua programs which are just as compact: + + -- printxy.lua + require 'pl' + data.filter 'x,y where x > 3' + +It is common enough to have data files without headers of field names. +`data.read` makes a special exception for such files if all fields are numeric. +Since there are no column names to use in query expressions, you can use AWK-like +column indexes, e.g. '$1,$2 where $1 > 3'. I have a little executable script on +my system called `lf` which looks like this: + + #!/usr/bin/env lua + require 'pl.data'.filter(arg[1]) + +And it can be used generally as a filter command to extract columns from data. +(The column specifications may be expressions or even constants.) + + $ lf '$1,$5/10' < test.dat + +(As with AWK, please note the single-quotes used in this command; this prevents +the shell trying to expand the column indexes. If you are on Windows, then you +must quote the expression in double-quotes so +it is passed as one argument to your batch file.) + +As a tutorial resource, have a look at `test-data.lua` in the PL tests directory +for other examples of use, plus comments. + +The data returned by `read` or constructed by `Data.copy_select` from a query is +basically just an array of rows: `{{1,2},{3,4}}`. So you may use `read` to pull +in any array-like dataset, and process with any function that expects such a +implementation. In particular, the functions in `array2d` will work fine with +this data. In fact, these functions are available as methods; e.g. +`array2d.flatten` can be called directly like so to give us a one-dimensional list: + + v = data.read('dat.txt'):flatten() + +The data is also in exactly the right shape to be treated as matrices by +[LuaMatrix](http://lua-users.org/wiki/LuaMatrix): + + > matrix = require 'matrix' + > m = matrix(data.read 'mat.txt') + > = m + 1 0.2 0.3 + 0.2 1 0.1 + 0.1 0.2 1 + > = m^2 -- same as m*m + 1.07 0.46 0.62 + 0.41 1.06 0.26 + 0.24 0.42 1.05 + +`write` will write matrices back to files for you. + +Finally, for the curious, the global variable `_DEBUG` can be used to print out +the actual iterator function which a query generates and dynamically compiles. By +using code generation, we can get pretty much optimal performance out of +arbitrary queries. + + > lua -lpl -e "_DEBUG=true" -e "data.filter 'x,y where x > 4 sort by x'" < test.txt + return function (t) + local i = 0 + local v + local ls = {} + for i,v in ipairs(t) do + if v[1] > 4 then + ls[#ls+1] = v + end + end + table.sort(ls,function(v1,v2) + return v1[1] < v2[1] + end) + local n = #ls + return function() + i = i + 1 + v = ls[i] + if i > n then return end + return v[1],v[2] + end + end + + 10,20 + 40,50 + +### Reading Configuration Files + +The `config` module provides a simple way to convert several kinds of +configuration files into a Lua table. Consider the simple example: + + # test.config + # Read timeout in seconds + read.timeout=10 + + # Write timeout in seconds + write.timeout=5 + + #acceptable ports + ports = 1002,1003,1004 + +This can be easily brought in using `config.read` and the result shown using +`pretty.write`: + + -- readconfig.lua + local config = require 'pl.config' + local pretty= require 'pl.pretty' + + local t = config.read(arg[1]) + print(pretty.write(t)) + +and the output of `lua readconfig.lua test.config` is: + + { + ports = { + 1002, + 1003, + 1004 + }, + write_timeout = 5, + read_timeout = 10 + } + +That is, `config.read` will bring in all key/value pairs, ignore # comments, and +ensure that the key names are proper Lua identifiers by replacing non-identifier +characters with '_'. If the values are numbers, then they will be converted. (So +the value of `t.write_timeout` is the number 5). In addition, any values which +are separated by commas will be converted likewise into an array. + +Any line can be continued with a backslash. So this will all be considered one +line: + + names=one,two,three, \ + four,five,six,seven, \ + eight,nine,ten + + +Windows-style INI files are also supported. The section structure of INI files +translates naturally to nested tables in Lua: + + ; test.ini + [timeouts] + read=10 ; Read timeout in seconds + write=5 ; Write timeout in seconds + [portinfo] + ports = 1002,1003,1004 + + The output is: + + { + portinfo = { + ports = { + 1002, + 1003, + 1004 + } + }, + timeouts = { + write = 5, + read = 10 + } + } + +You can now refer to the write timeout as `t.timeouts.write`. + +As a final example of the flexibility of `config.read`, if passed this simple +comma-delimited file + + one,two,three + 10,20,30 + 40,50,60 + 1,2,3 + +it will produce the following table: + + { + { "one", "two", "three" }, + { 10, 20, 30 }, + { 40, 50, 60 }, + { 1, 2, 3 } + } + +`config.read` isn't designed to read all CSV files in general, but intended to +support some Unix configuration files not structured as key-value pairs, such as +'/etc/passwd'. + +This function is intended to be a Swiss Army Knife of configuration readers, but +it does have to make assumptions, and you may not like them. So there is an +optional extra parameter which allows some control, which is table that may have +the following fields: + + { + variablilize = true, + convert_numbers = tonumber, + trim_space = true, + list_delim = ',', + trim_quotes = true, + ignore_assign = false, + keysep = '=', + smart = false, + } + +`variablilize` is the option that converted `write.timeout` in the first example +to the valid Lua identifier `write_timeout`. If `convert_numbers` is true, then +an attempt is made to convert any string that starts like a number. You can +specify your own function (say one that will convert a string like '5224 kb' into +a number.) + +`trim_space` ensures that there is no starting or trailing whitespace with +values, and `list_delim` is the character that will be used to decide whether to +split a value up into a list (it may be a Lua string pattern such as '%s+'.) + +For instance, the password file in Unix is colon-delimited: + + t = config.read('/etc/passwd',{list_delim=':'}) + +This produces the following output on my system (only last two lines shown): + + { + ... + { + "user", + "x", + "1000", + "1000", + "user,,,", + "/home/user", + "/bin/bash" + }, + { + "sdonovan", + "x", + "1001", + "1001", + "steve donovan,28,,", + "/home/sdonovan", + "/bin/bash" + } + } + +You can get this into a more sensible format, where the usernames are the keys, +with this (the `tablex.pairmap` function must return value, key!) + + t = tablex.pairmap(function(k,v) return v,v[1] end,t) + +and you get: + + { ... + sdonovan = { + "sdonovan", + "x", + "1001", + "1001", + "steve donovan,28,,", + "/home/sdonovan", + "/bin/bash" + } + ... + } + +Many common Unix configuration files can be read by tweaking these parameters. +For `/etc/fstab`, the options `{list_delim='%s+',ignore_assign=true}` will +correctly separate the columns. It's common to find 'KEY VALUE' assignments in +files such as `/etc/ssh/ssh_config`; the options `{keysep=' '}` make +`config.read` return a table where each KEY has a value VALUE. + +Files in the Linux `procfs` usually use ':` as the field delimiter: + + > t = config.read('/proc/meminfo',{keysep=':'}) + > = t.MemFree + 220140 kB + +That result is a string, since `tonumber` doesn't like it, but defining the +`convert_numbers` option as `function(s) return tonumber((s:gsub(' kB$',''))) +end` will get the memory figures as actual numbers in the result. (The extra +parentheses are necessary so that `tonumber` only gets the first result from +`gsub`). From `tests/test-config.lua': + + testconfig([[ + MemTotal: 1024748 kB + MemFree: 220292 kB + ]], + { MemTotal = 1024748, MemFree = 220292 }, + { + keysep = ':', + convert_numbers = function(s) + s = s:gsub(' kB$','') + return tonumber(s) + end + } + ) + + +The `smart` option lets `config.read` make a reasonable guess for you; there +are examples in `tests/test-config.lua`, but basically these common file +formats (and those following the same pattern) can be processed directly in +smart mode: 'etc/fstab', '/proc/XXXX/status', 'ssh_config' and 'pdatedb.conf'. + +Please note that `config.read` can be passed a _file-like object_; if it's not a +string and supports the `read` method, then that will be used. For instance, to +read a configuration from a string, use `stringio.open`. + + +<a id="lexer"/> + +### Lexical Scanning + +Although Lua's string pattern matching is very powerful, there are times when +something more powerful is needed. `pl.lexer.scan` provides lexical scanners +which _tokenize_ a string, classifying tokens into numbers, strings, etc. + + > lua -lpl + Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio + > tok = lexer.scan 'alpha = sin(1.5)' + > = tok() + iden alpha + > = tok() + = = + > = tok() + iden sin + > = tok() + ( ( + > = tok() + number 1.5 + > = tok() + ) ) + > = tok() + (nil) + +The scanner is a function, which is repeatedly called and returns the _type_ and +_value_ of the token. Recognized basic types are 'iden','string','number', and +'space'. and everything else is represented by itself. Note that by default the +scanner will skip any 'space' tokens. + +'comment' and 'keyword' aren't applicable to the plain scanner, which is not +language-specific, but a scanner which understands Lua is available. It +recognizes the Lua keywords, and understands both short and long comments and +strings. + + > for t,v in lexer.lua 'for i=1,n do' do print(t,v) end + keyword for + iden i + = = + number 1 + , , + iden n + keyword do + +A lexical scanner is useful where you have highly-structured data which is not +nicely delimited by newlines. For example, here is a snippet of a in-house file +format which it was my task to maintain: + + points + (818344.1,-20389.7,-0.1),(818337.9,-20389.3,-0.1),(818332.5,-20387.8,-0.1) + ,(818327.4,-20388,-0.1),(818322,-20387.7,-0.1),(818316.3,-20388.6,-0.1) + ,(818309.7,-20389.4,-0.1),(818303.5,-20390.6,-0.1),(818295.8,-20388.3,-0.1) + ,(818290.5,-20386.9,-0.1),(818285.2,-20386.1,-0.1),(818279.3,-20383.6,-0.1) + ,(818274,-20381.2,-0.1),(818274,-20380.7,-0.1); + +Here is code to extract the points using `pl.lexer`: + + -- assume 's' contains the text above... + local lexer = require 'pl.lexer' + local expecting = lexer.expecting + local append = table.insert + + local tok = lexer.scan(s) + + local points = {} + local t,v = tok() -- should be 'iden','points' + + while t ~= ';' do + c = {} + expecting(tok,'(') + c.x = expecting(tok,'number') + expecting(tok,',') + c.y = expecting(tok,'number') + expecting(tok,',') + c.z = expecting(tok,'number') + expecting(tok,')') + t,v = tok() -- either ',' or ';' + append(points,c) + end + +The `expecting` function grabs the next token and if the type doesn't match, it +throws an error. (`pl.lexer`, unlike other PL libraries, raises errors if +something goes wrong, so you should wrap your code in `pcall` to catch the error +gracefully.) + +The scanners all have a second optional argument, which is a table which controls +whether you want to exclude spaces and/or comments. The default for `lexer.lua` +is `{space=true,comments=true}`. There is a third optional argument which +determines how string and number tokens are to be processsed. + +The ultimate highly-structured data is of course, program source. Here is a +snippet from 'text-lexer.lua': + + require 'pl' + + lines = [[ + for k,v in pairs(t) do + if type(k) == 'number' then + print(v) -- array-like case + else + print(k,v) + end + end + ]] + + ls = List() + for tp,val in lexer.lua(lines,{space=true,comments=true}) do + assert(tp ~= 'space' and tp ~= 'comment') + if tp == 'keyword' then ls:append(val) end + end + test.asserteq(ls,List{'for','in','do','if','then','else','end','end'}) + +Here is a useful little utility that identifies all common global variables found +in a lua module (ignoring those declared locally for the moment): + + -- testglobal.lua + require 'pl' + + local txt,err = utils.readfile(arg[1]) + if not txt then return print(err) end + + local globals = List() + for t,v in lexer.lua(txt) do + if t == 'iden' and _G[v] then + globals:append(v) + end + end + pretty.dump(seq.count_map(globals)) + +Rather then dumping the whole list, with its duplicates, we pass it through +`seq.count_map` which turns the list into a table where the keys are the values, +and the associated values are the number of times those values occur in the +sequence. Typical output looks like this: + + { + type = 2, + pairs = 2, + table = 2, + print = 3, + tostring = 2, + require = 1, + ipairs = 4 + } + +You could further pass this through `tablex.keys` to get a unique list of +symbols. This can be useful when writing 'strict' Lua modules, where all global +symbols must be defined as locals at the top of the file. + +For a more detailed use of `lexer.scan`, please look at `testxml.lua` in the +examples directory. + +### XML + +New in the 0.9.7 release is some support for XML. This is a large topic, and +Penlight does not provide a full XML stack, which is properly the task of a more +specialized library. + +#### Parsing and Pretty-Printing + +The semi-standard XML parser in the Lua universe is [lua-expat](http://matthewwild.co.uk/projects/luaexpat/). +In particular, +it has a function called `lxp.lom.parse` which will parse XML into the Lua Object +Model (LOM) format. However, it does not provide a way to convert this data back +into XML text. `xml.parse` will use this function, _if_ `lua-expat` is +available, and otherwise switches back to a pure Lua parser originally written by +Roberto Ierusalimschy. + +The resulting document object knows how to render itself as a string, which is +useful for debugging: + + > d = xml.parse "<nodes><node id='1'>alice</node></nodes>" + > = d + <nodes><node id='1'>alice</node></nodes> + > pretty.dump (d) + { + { + "alice", + attr = { + "id", + id = "1" + }, + tag = "node" + }, + attr = { + }, + tag = "nodes" + } + +Looking at the actual shape of the data reveals the structure of LOM: + + * every element has a `tag` field with its name + * plus a `attr` field which is a table containing the attributes as fields, and +also as an array. It is always present. + * the children of the element are the array part of the element, so `d[1]` is +the first child of `d`, etc. + +It could be argued that having attributes also as the array part of `attr` is not +essential (you cannot depend on attribute order in XML) but that's how +it goes with this standard. + +`lua-expat` is another _soft dependency_ of Penlight; generally, the fallback +parser is good enough for straightforward XML as is commonly found in +configuration files, etc. `doc.basic_parse` is not intended to be a proper +conforming parser (it's only sixty lines) but it handles simple kinds of +documents that do not have comments or DTD directives. It is intelligent enough +to ignore the `<?xml` directive and that is about it. + +You can get pretty-printing by explicitly calling `xml.tostring` and passing it +the initial indent and the per-element indent: + + > = xml.tostring(d,'',' ') + + <nodes> + <node id='1'>alice</node> + </nodes> + +There is a fourth argument which is the _attribute indent_: + + > a = xml.parse "<frodo name='baggins' age='50' type='hobbit'/>" + > = xml.tostring(a,'',' ',' ') + + <frodo + type='hobbit' + name='baggins' + age='50' + /> + +#### Parsing and Working with Configuration Files + +It's common to find configurations expressed with XML these days. It's +straightforward to 'walk' the [LOM](http://matthewwild.co.uk/projects/luaexpat/lom.html) +data and extract the data in the form you want: + + require 'pl' + + local config = [[ + <config> + <alpha>1.3</alpha> + <beta>10</beta> + <name>bozo</name> + </config> + ]] + local d,err = xml.parse(config) + + local t = {} + for item in d:childtags() do + t[item.tag] = item[1] + end + + pretty.dump(t) + ---> + { + beta = "10", + alpha = "1.3", + name = "bozo" + } + +The only gotcha is that here we must use the `Doc:childtags` method, which will +skip over any text elements. + +A more involved example is this excerpt from `serviceproviders.xml`, which is +usually found at `/usr/share/mobile-broadband-provider-info/serviceproviders.xml` +on Debian/Ubuntu Linux systems. + + d = xml.parse [[ + <serviceproviders format="2.0"> + ... + <country code="za"> + <provider> + <name>Cell-c</name> + <gsm> + <network-id mcc="655" mnc="07"/> + <apn value="internet"> + <username>Cellcis</username> + <dns>196.7.0.138</dns> + <dns>196.7.142.132</dns> + </apn> + </gsm> + </provider> + <provider> + <name>MTN</name> + <gsm> + <network-id mcc="655" mnc="10"/> + <apn value="internet"> + <dns>196.11.240.241</dns> + <dns>209.212.97.1</dns> + </apn> + </gsm> + </provider> + <provider> + <name>Vodacom</name> + <gsm> + <network-id mcc="655" mnc="01"/> + <apn value="internet"> + <dns>196.207.40.165</dns> + <dns>196.43.46.190</dns> + </apn> + <apn value="unrestricted"> + <name>Unrestricted</name> + <dns>196.207.32.69</dns> + <dns>196.43.45.190</dns> + </apn> + </gsm> + </provider> + <provider> + <name>Virgin Mobile</name> + <gsm> + <apn value="vdata"> + <dns>196.7.0.138</dns> + <dns>196.7.142.132</dns> + </apn> + </gsm> + </provider> + </country> + .... + </serviceproviders> + ]] + +Getting the names of the providers per-country is straightforward: + + local t = {} + for country in d:childtags() do + local providers = {} + t[country.attr.code] = providers + for provider in country:childtags() do + table.insert(providers,provider:child_with_name('name'):get_text()) + end + end + + pretty.dump(t) + --> + { + za = { + "Cell-c", + "MTN", + "Vodacom", + "Virgin Mobile" + } + .... + } + +#### Generating XML with 'xmlification' + +This feature is inspired by the `htmlify` function used by +[Orbit](http://keplerproject.github.com/orbit/) to simplify HTML generation, +except that no function environment magic is used; the `tags` function returns a +set of _constructors_ for elements of the given tag names. + + > nodes, node = xml.tags 'nodes, node' + > = node 'alice' + <node>alice</node> + > = nodes { node {id='1','alice'}} + <nodes><node id='1'>alice</node></nodes> + +The flexibility of Lua tables is very useful here, since both the attributes and +the children of an element can be encoded naturally. The argument to these tag +constructors is either a single value (like a string) or a table where the +attributes are the named keys and the children are the array values. + +#### Generating XML using Templates + +A template is a little XML document which contains dollar-variables. The `subst` +method on a document is fed an array of tables containing values for these +variables. Note how the parent tag name is specified: + + > templ = xml.parse "<node id='$id'>$name</node>" + > = templ:subst {tag='nodes', {id=1,name='alice'},{id=2,name='john'}} + <nodes><node id='1'>alice</node><node id='2'>john</node></nodes> + +Substitution is very related to _filtering_ documents. One of the annoying things +about XML is that it is a document markup language first, and a data language +second. Standard parsers will assume you really care about all those extra +text elements. Consider this fragment, which has been changed by a five-year old: + + T = [[ + <weather> + boops! + <current_conditions> + <condition data='$condition'/> + <temp_c data='$temp'/> + <bo>whoops!</bo> + </current_conditions> + </weather> + ]] + +Conformant parsers will give you text elements with the line feed after `<current_conditions>` +although it makes handling the data more irritating. + + local function parse (str) + return xml.parse(str,false,true) + end + +Second argument means 'string, not file' and third argument means use the built-in +Lua parser (instead of LuaExpat if available) which _by default_ is not interested in +keeping such strings. + +How to remove the string `boops!`? `clone` (also called `filter` when called as a +method) copies a LOM document. It can be passed a filter function, which is applied +to each string found. The powerful thing about this is that this function receives +structural information - the parent node, and whether this was a tag name, a text +element or a attribute name: + + d = parse (T) + c = d:filter(function(s,kind,parent) + print(stringx.strip(s),kind,parent and parent.tag or '?') + if kind == '*TEXT' and #parent > 1 then return nil end + return s + end) + ---> + weather *TAG ? + boops! *TEXT weather + current_conditions *TAG weather + condition *TAG current_conditions + $condition data condition + temp_c *TAG current_conditions + $temp data temp_c + bo *TAG current_conditions + whoops! *TEXT bo + +We can pull out 'boops' and not 'whoops' by discarding text elements which are not +the single child of an element. + + + +#### Extracting Data using Templates + +Matching goes in the opposite direction. We have a document, and would like to +extract values from it using a pattern. + +A common use of this is parsing the XML result of API queries. The +[(undocumented and subsequently discontinued) Google Weather +API](http://blog.programmableweb.com/2010/02/08/googles-secret-weather-api/) is a +good example. Grabbing the result of +`http://www.google.com/ig/api?weather=Johannesburg,ZA" we get something like +this, after pretty-printing: + + <xml_api_reply version='1'> + <weather module_id='0' tab_id='0' mobile_zipped='1' section='0' row='0' +mobile_row='0'> + <forecast_information> + <city data='Johannesburg, Gauteng'/> + <postal_code data='Johannesburg,ZA'/> + <latitude_e6 data=''/> + <longitude_e6 data=''/> + <forecast_date data='2010-10-02'/> + <current_date_time data='2010-10-02 18:30:00 +0000'/> + <unit_system data='US'/> + </forecast_information> + <current_conditions> + <condition data='Clear'/> + <temp_f data='75'/> + <temp_c data='24'/> + <humidity data='Humidity: 19%'/> + <icon data='/ig/images/weather/sunny.gif'/> + <wind_condition data='Wind: NW at 7 mph'/> + </current_conditions> + <forecast_conditions> + <day_of_week data='Sat'/> + <low data='60'/> + <high data='89'/> + <icon data='/ig/images/weather/sunny.gif'/> + <condition data='Clear'/> + </forecast_conditions> + .... + </weather> + </xml_api_reply> + +Assume that the above XML has been read into `google`. The idea is to write a +pattern looking like a template, and use it to extract some values of interest: + + t = [[ + <weather> + <current_conditions> + <condition data='$condition'/> + <temp_c data='$temp'/> + </current_conditions> + </weather> + ]] + + local res, ret = google:match(t) + pretty.dump(res) + +And the output is: + + { + condition = "Clear", + temp = "24" + } + +The `match` method can be passed a LOM document or some text, which will be +parsed first. + +But what if we need to extract values from repeated elements? Match templates may +contain 'array matches' which are enclosed in '{{..}}': + + <weather> + {{<forecast_conditions> + <day_of_week data='$day'/> + <low data='$low'/> + <high data='$high'/> + <condition data='$condition'/> + </forecast_conditions>}} + </weather> + +And the match result is: + + { + { + low = "60", + high = "89", + day = "Sat", + condition = "Clear", + }, + { + low = "53", + high = "86", + day = "Sun", + condition = "Clear", + }, + { + low = "57", + high = "87", + day = "Mon", + condition = "Clear", + }, + { + low = "60", + high = "84", + day = "Tue", + condition = "Clear", + } + } + +With this array of tables, you can use `tablex` or `List` +to reshape into the desired form, if you choose. Just as with reading a Unix password +file with `config`, you can make the array into a map of days to conditions using: + + `tablex.pairmap`('|k,v| v,v.day',conditions) + +(Here using the alternative string lambda option) + +However, xml matches can shape the structure of the output. By replacing the `day_of_week` +line of the template with `<day_of_week data='$_'/>` we get the same effect; `$_` is +a special symbol that means that this captured value (or simply _capture_) becomes the key. + +Note that `$NUMBER` means a numerical index, so +that `$1` is the first element of the resulting array, and so forth. You can mix +numbered and named captures, but it's strongly advised to make the numbered captures +form a proper array sequence (everything from `1` to `n` inclusive). `$0` has a +special meaning; if it is the only capture (`{[0]='foo'}`) then the table is +collapsed into 'foo'. + + <weather> + {{<forecast_conditions> + <day_of_week data='$_'/> + <low data='$1'/> + <high data='$2'/> + <condition data='$3'/> + </forecast_conditions>}} + </weather> + +Now the result is: + + { + Tue = { + "60", + "84", + "Clear" + }, + Sun = { + "53", + "86", + "Clear" + }, + Sat = { + "60", + "89", + "Clear" + }, + Mon = { + "57", + "87", + "Clear" + } + } + +Applying matches to this config file poses another problem, because the actual +tags matched are themselves meaningful. + + <config> + <alpha>1.3</alpha> + <beta>10</beta> + <name>bozo</name> + </config> + +So there are tag 'wildcards' which are element names ending with a hyphen. + + <config> + {{<key->$value</key->}} + </config> + +You will then get `{{alpha='1.3'},...}`. The most convenient format would be +returned by this (note that `_-` behaves just like `$_`): + + <config> + {{<_->$0</_->}} + </config> + +which would return `{alpha='1.3',beta='10',name='bozo'}`. + +We could play this game endlessly, and encode ways of converting captures, but +the scheme is complex enough, and it's easy to do the conversion later + + local numbers = {alpha=true,beta=true} + for k,v in pairs(res) do + if numbers[v] then res[k] = tonumber(v) end + end + + +#### HTML Parsing + +HTML is an unusually degenerate form of XML, and Dennis Schridde has contributed +a feature which makes parsing it easier. For instance, from the tests: + + doc = xml.parsehtml [[ + <BODY> + Hello dolly<br> + HTML is <b>slack</b><br> + </BODY> + ]] + + asserteq(xml.tostring(doc),[[ + <body> + Hello dolly<br/> + HTML is <b>slack</b><br/></body>]]) + +That is, all tags are converted to lowercase, and empty HTML elements like `br` +are properly closed; attributes do not need to be quoted. + +Also, DOCTYPE directives and comments are skipped. For truly badly formed HTML, +this is not the tool for you! + + + diff --git a/Data/Libraries/Penlight/docs_topics/07-functional.md b/Data/Libraries/Penlight/docs_topics/07-functional.md new file mode 100644 index 0000000..5921a3d --- /dev/null +++ b/Data/Libraries/Penlight/docs_topics/07-functional.md @@ -0,0 +1,547 @@ +## Functional Programming + +### Sequences + +@lookup pl.seq + +A Lua iterator (in its simplest form) is a function which can be repeatedly +called to return a set of one or more values. The `for in` statement understands +these iterators, and loops until the function returns `nil`. There are standard +sequence adapters for tables in Lua (`ipairs` and `pairs`), and `io.lines` +returns an iterator over all the lines in a file. In the Penlight libraries, such +iterators are also called _sequences_. A sequence of single values (say from +`io.lines`) is called _single-valued_, whereas the sequence defined by `pairs` is +_double-valued_. + +`pl.seq` provides a number of useful iterators, and some functions which operate +on sequences. At first sight this example looks like an attempt to write Python +in Lua, (with the sequence being inclusive): + + > for i in seq.range(1,4) do print(i) end + 1 + 2 + 3 + 4 + +But `range` is actually equivalent to Python's `xrange`, since it generates a +sequence, not a list. To get a list, use `seq.copy(seq.range(1,10))`, which +takes any single-value sequence and makes a table from the result. `seq.list` is +like `ipairs` except that it does not give you the index, just the value. + + > for x in seq.list {1,2,3} do print(x) end + 1 + 2 + 3 + +`enum` takes a sequence and turns it into a double-valued sequence consisting of +a sequence number and the value, so `enum(list(ls))` is actually equivalent to +`ipairs`. A more interesting example prints out a file with line numbers: + + for i,v in seq.enum(io.lines(fname)) do print(i..' '..v) end + +Sequences can be _combined_, either by 'zipping' them or by concatenating them. + + > for x,y in seq.zip(l1,l2) do print(x,y) end + 10 1 + 20 2 + 30 3 + > for x in seq.splice(l1,l2) do print(x) end + 10 + 20 + 30 + 1 + 2 + 3 + +`seq.printall` is useful for printing out single-valued sequences, and provides +some finer control over formating, such as a delimiter, the number of fields per +line, and a format string to use (@see string.format) + + > seq.printall(seq.random(10)) + 0.0012512588885159 0.56358531449324 0.19330423902097 .... + > seq.printall(seq.random(10), ',', 4, '%4.2f') + 0.17,0.86,0.71,0.51 + 0.30,0.01,0.09,0.36 + 0.15,0.17, + +`map` will apply a function to a sequence. + + > seq.printall(seq.map(string.upper, {'one','two'})) + ONE TWO + > seq.printall(seq.map('+', {10,20,30}, 1)) + 11 21 31 + +`filter` will filter a sequence using a boolean function (often called a +_predicate_). For instance, this code only prints lines in a file which are +composed of digits: + + for l in seq.filter(io.lines(file), stringx.isdigit) do print(l) end + +The following returns a table consisting of all the positive values in the +original table (equivalent to `tablex.filter(ls, '>', 0)`) + + ls = seq.copy(seq.filter(ls, '>', 0)) + +We're already encounted `seq.sum` when discussing `input.numbers`. This can also +be expressed with `seq.reduce`: + + > seq.reduce(function(x,y) return x + y end, seq.list{1,2,3,4}) + 10 + +`seq.reduce` applies a binary function in a recursive fashion, so that: + + reduce(op,{1,2,3}) => op(1,reduce(op,{2,3}) => op(1,op(2,3)) + +it's now possible to easily generate other cumulative operations; the standard +operations declared in `pl.operator` are useful here: + + > ops = require 'pl.operator' + > -- can also say '*' instead of ops.mul + > = seq.reduce(ops.mul,input.numbers '1 2 3 4') + 24 + +There are functions to extract statistics from a sequence of numbers: + + > l1 = List {10,20,30} + > l2 = List {1,2,3} + > = seq.minmax(l1) + 10 30 + > = seq.sum(l1) + 60 3 + +It is common to get sequences where values are repeated, say the words in a file. +`count_map` will take such a sequence and count the values, returning a table +where the _keys_ are the unique values, and the value associated with each key is +the number of times they occurred: + + > t = seq.count_map {'one','fred','two','one','two','two'} + > = t + {one=2,fred=1,two=3} + +This will also work on numerical sequences, but you cannot expect the result to +be a proper list, i.e. having no 'holes'. Instead, you always need to use `pairs` +to iterate over the result - note that there is a hole at index 5: + + > t = seq.count_map {1,2,4,2,2,3,4,2,6} + > for k,v in pairs(t) do print(k,v) end + 1 1 + 2 4 + 3 1 + 4 2 + 6 1 + +`unique` uses `count_map` to return a list of the unique values, that is, just +the keys of the resulting table. + +`last` turns a single-valued sequence into a double-valued sequence with the +current value and the last value: + + > for current,last in seq.last {10,20,30,40} do print (current,last) end + 20 10 + 30 20 + 40 30 + +This makes it easy to do things like identify repeated lines in a file, or +construct differences between values. `filter` can handle double-valued sequences +as well, so one could filter such a sequence to only return cases where the +current value is less than the last value by using `operator.lt` or just '<'. +This code then copies the resulting code into a table. + + > ls = {10,9,10,3} + > = seq.copy(seq.filter(seq.last(s),'<')) + {9,3} + + +### Sequence Wrappers + +The functions in `pl.seq` cover the common patterns when dealing with sequences, +but chaining these functions together can lead to ugly code. Consider the last +example of the previous section; `seq` is repeated three times and the resulting +expression has to be read right-to-left. The first issue can be helped by local +aliases, so that the expression becomes `copy(filter(last(s),'<'))` but the +second issue refers to the somewhat unnatural order of functional application. +We tend to prefer reading operations from left to right, which is one reason why +object-oriented notation has become popular. Sequence adapters allow this +expression to be written like so: + + seq(s):last():filter('<'):copy() + +With this notation, the operation becomes a chain of method calls running from +left to right. + +'Sequence' is not a basic Lua type, they are generally functions or callable +objects. The expression `seq(s)` wraps a sequence in a _sequence wrapper_, which +is an object which understands all the functions in `pl.seq` as methods. This +object then explicitly represents sequences. + +As a special case, the constructor (which is when you call the table `seq`) will +make a wrapper for a plain list-like table. Here we apply the length operator to +a sequence of strings, and print them out. + + > seq{'one','tw','t'} :map '#' :printall() + 3 2 1 + +As a convenience, there is a function `seq.lines` which behaves just like +`io.lines` except it wraps the result as an explicit sequence type. This takes +the first 10 lines from standard input, makes it uppercase, turns it into a +sequence with a count and the value, glues these together with the concatenation +operator, and finally prints out the sequence delimited by a newline. + + seq.lines():take(10):upper():enum():map('..'):printall '\n' + +Note the method `upper`, which is not a `seq` function. if an unknown method is +called, sequence wrappers apply that method to all the values in the sequence +(this is implicit use of `mapmethod`) + +It is straightforward to create custom sequences that can be used in this way. On +Unix, `/dev/random` gives you an _endless_ sequence of random bytes, so we use +`take` to limit the sequence, and then `map` to scale the result into the desired +range. The key step is to use `seq` to wrap the iterator function: + + -- random.lua + local seq = require 'pl.seq' + + function dev_random() + local f = io.open('/dev/random') + local byte = string.byte + return seq(function() + -- read two bytes into a string and convert into a 16-bit number + local s = f:read(2) + return byte(s,1) + 256*byte(s,2) + end) + end + + -- print 10 random numbers from 0 to 1 ! + dev_random():take(10):map('%',100):map('/',100):printall ',' + + +Another Linux one-liner depends on the `/proc` filesystem and makes a list of all +the currently running processes: + + pids = seq(lfs.dir '/proc'):filter(stringx.isdigit):map(tonumber):copy() + +This version of Penlight has an experimental feature which relies on the fact +that _all_ Lua types can have metatables, including functions. This makes +_implicit sequence wrapping_ possible: + + > seq.import() + > seq.random(5):printall(',',5,'%4.1f') + 0.0, 0.1, 0.4, 0.1, 0.2 + +This avoids the awkward `seq(seq.random(5))` construction. Or the iterator can +come from somewhere else completely: + + > ('one two three'):gfind('%a+'):printall(',') + one,two,three, + +After `seq.import`, it is no longer necessary to explicitly wrap sequence +functions. + +But there is a price to pay for this convenience. _Every_ function is affected, +so that any function can be used, appropriate or not: + + > math.sin:printall() + ..seq.lua:287: bad argument #1 to '(for generator)' (number expected, got nil) + > a = tostring + > = a:find(' ') + function: 0042C920 + +What function is returned? It's almost certain to be something that makes no +sense in the current context. So implicit sequences may make certain kinds of +programming mistakes harder to catch - they are best used for interactive +exploration and small scripts. + +<a id="comprehensions"/> + +### List Comprehensions + +List comprehensions are a compact way to create tables by specifying their +elements. In Python, you can say this: + + ls = [x for x in range(5)] # == [0,1,2,3,4] + +In Lua, using `pl.comprehension`: + + > C = require('pl.comprehension').new() + > = C ('x for x=1,10') () + {1,2,3,4,5,6,7,8,9,10} + +`C` is a function which compiles a list comprehension _string_ into a _function_. +In this case, the function has no arguments. The parentheses are redundant for a +function taking a string argument, so this works as well: + + > = C 'x^2 for x=1,4' () + {1,4,9,16} + > = C '{x,x^2} for x=1,4' () + {{1,1},{2,4},{3,9},{4,16}} + +Note that the expression can be _any_ function of the variable `x`! + +The basic syntax so far is `<expr> for <set>`, where `<set>` can be anything that +the Lua `for` statement understands. `<set>` can also just be the variable, in +which case the values will come from the _argument_ of the comprehension. Here +I'm emphasizing that a comprehension is a function which can take a list argument: + + > = C '2*x for x' {1,2,3} + {2,4,6} + > dbl = C '2*x for x' + > = dbl {10,20,30} + {20,40,60} + +Here is a somewhat more explicit way of saying the same thing; `_1` is a +_placeholder_ refering to the _first_ argument passed to the comprehension. + + > = C '2*x for _,x in pairs(_1)' {10,20,30} + {20,40,60} + > = C '_1(x) for x'(tostring,{1,2,3,4}) + {'1','2','3','4'} + +This extended syntax is useful when you wish to collect the result of some +iterator, such as `io.lines`. This comprehension creates a function which creates +a table of all the lines in a file: + + > f = io.open('array.lua') + > lines = C 'line for line in _1:lines()' (f) + > = #lines + 118 + +There are a number of functions that may be applied to the result of a +comprehension: + + > = C 'min(x for x)' {1,44,0} + 0 + > = C 'max(x for x)' {1,44,0} + 44 + > = C 'sum(x for x)' {1,44,0} + 45 + +(These are equivalent to a reduce operation on a list.) + +After the `for` part, there may be a condition, which filters the output. This +comprehension collects the even numbers from a list: + + > = C 'x for x if x % 2 == 0' {1,2,3,4,5} + {2,4} + +There may be a number of `for` parts: + + > = C '{x,y} for x = 1,2 for y = 1,2' () + {{1,1},{1,2},{2,1},{2,2}} + > = C '{x,y} for x for y' ({1,2},{10,20}) + {{1,10},{1,20},{2,10},{2,20}} + +These comprehensions are useful when dealing with functions of more than one +variable, and are not so easily achieved with the other Penlight functional forms. + +<a id="func"/> + +### Creating Functions from Functions + +@lookup pl.func + +Lua functions may be treated like any other value, although of course you cannot +multiply or add them. One operation that makes sense is _function composition_, +which chains function calls (so `(f * g)(x)` is `f(g(x))`.) + + > func = require 'pl.func' + > printf = func.compose(io.write,string.format) + > printf("hello %s\n",'world') + hello world + true + +Many functions require you to pass a function as an argument, say to apply to all +values of a sequence or as a callback. Often useful functions have the wrong +number of arguments. So there is a need to construct a function of one argument +from one of two arguments, _binding_ the extra argument to a given value. + +_partial application_ takes a function of n arguments and returns a function of n-1 +arguments where the first argument is bound to some value: + + > p2 = func.bind1(print,'start>') + > p2('hello',2) + start> hello 2 + > ops = require 'pl.operator' + > = tablex.filter({1,-2,10,-1,2},bind1(ops.gt,0)) + {-2,-1} + > tablex.filter({1,-2,10,-1,2},bind1(ops.le,0)) + {1,10,2} + +The last example unfortunately reads backwards, because `bind1` alway binds the +first argument! Also unfortunately, in my youth I confused 'currying' with +'partial application', so the old name for `bind1` is `curry` - this alias still exists. + +This is a specialized form of function argument binding. Here is another way +to say the `print` example: + + > p2 = func.bind(print,'start>',func._1,func._2) + > p2('hello',2) + start> hello 2 + +where `_1` and `_2` are _placeholder variables_, corresponding to the first and +second argument respectively. + +Having `func` all over the place is distracting, so it's useful to pull all of +`pl.func` into the local context. Here is the filter example, this time the right +way around: + + > utils.import 'pl.func' + > tablex.filter({1,-2,10,-1,2},bind(ops.gt, _1, 0)) + {1,10,2} + +`tablex.merge` does a general merge of two tables. This example shows the +usefulness of binding the last argument of a function. + + > S1 = {john=27, jane=31, mary=24} + > S2 = {jane=31, jones=50} + > intersection = bind(tablex.merge, _1, _2, false) + > union = bind(tablex.merge, _1, _2, true) + > = intersection(S1,S2) + {jane=31} + > = union(S1,S2) + {mary=24,jane=31,john=27,jones=50} + +When using `bind` with `print`, we got a function of precisely two arguments, +whereas we really want our function to use varargs like `print`. This is the role +of `_0`: + + > _DEBUG = true + > p = bind(print,'start>', _0) + return function (fn,_v1) + return function(...) return fn(_v1,...) end + end + + > p(1,2,3,4,5) + start> 1 2 3 4 5 + +I've turned on the global `_DEBUG` flag, so that the function generated is +printed out. It is actually a function which _generates_ the required function; +the first call _binds the value_ of `_v1` to 'start>'. + +### Placeholder Expressions + +A common pattern in Penlight is a function which applies another function to all +elements in a table or a sequence, such as `tablex.map` or `seq.filter`. Lua does +anonymous functions well, although they can be a bit tedious to type: + + > = tablex.map(function(x) return x*x end, {1,2,3,4}) + {1,4,9,16} + +`pl.func` allows you to define _placeholder expressions_, which can cut down on +the typing required, and also make your intent clearer. First, we bring contents +of `pl.func` into our context, and then supply an expression using placeholder +variables, such as `_1`,`_2`,etc. (C++ programmers will recognize this from the +Boost libraries.) + + > utils.import 'pl.func' + > = tablex.map(_1*_1, {1,2,3,4}) + {1,4,9,16} + +Functions of up to 5 arguments can be generated. + + > = tablex.map2(_1+_2,{1,2,3}, {10,20,30}) + {11,22,33} + +These expressions can use arbitrary functions, altho they must first be +registered with the functional library. `func.register` brings in a single +function, and `func.import` brings in a whole table of functions, such as `math`. + + > sin = register(math.sin) + > = tablex.map(sin(_1), {1,2,3,4}) + {0.8414709848079,0.90929742682568,0.14112000805987,-0.75680249530793} + > import 'math' + > = tablex.map(cos(2*_1),{1,2,3,4}) + {-0.41614683654714,-0.65364362086361,0.96017028665037,-0.14550003380861} + +A common operation is calling a method of a set of objects: + + > = tablex.map(_1:sub(1,1), {'one','four','x'}) + {'o','f','x'} + +There are some restrictions on what operators can be used in PEs. For instance, +because the `__len` metamethod cannot be overriden by plain Lua tables, we need +to define a special function to express `#_1': + + > = tablex.map(Len(_1), {'one','four','x'}) + {3,4,1} + +Likewise for comparison operators, which cannot be overloaded for _different_ +types, and thus also have to be expressed as a special function: + + > = tablex.filter(Gt(_1,0), {1,-1,2,4,-3}) + {1,2,4} + +It is useful to express the fact that a function returns multiple values. For +instance, `tablex.pairmap` expects a function that will be called with the key +and the value, and returns the new value and the key, in that order. + + > = pairmap(Args(_2,_1:upper()),{fred=1,alice=2}) + {ALICE=2,FRED=1} + +PEs cannot contain `nil` values, since PE function arguments are represented as +an array. Instead, a special value called `Nil` is provided. So say +`_1:f(Nil,1)` instead of `_1:f(nil,1)`. + +A placeholder expression cannot be automatically used as a Lua function. The +technical reason is that the call operator must be overloaded to construct +function calls like `_1(1)`. If you want to force a PE to return a function, use +`func.I`. + + > = tablex.map(_1(10),{I(2*_1),I(_1*_1),I(_1+2)}) + {20,100,12} + +Here we make a table of functions taking a single argument, and then call them +all with a value of 10. + +The essential idea with PEs is to 'quote' an expression so that it is not +immediately evaluated, but instead turned into a function that can be applied +later to some arguments. The basic mechanism is to wrap values and placeholders +so that the usual Lua operators have the effect of building up an _expression +tree_. (It turns out that you can do _symbolic algebra_ using PEs, see +`symbols.lua` in the examples directory, and its test runner `testsym.lua`, which +demonstrates symbolic differentiation.) + +The rule is that if any operator has a PE operand, the result will be quoted. +Sometimes we need to quote things explicitly. For instance, say we want to pass a +function to a filter that must return true if the element value is in a set. +`set[_1]` is the obvious expression, but it does not give the desired result, +since it evaluates directly, giving `nil`. Indexing works differently than a +binary operation like addition (set+_1 _is_ properly quoted) so there is a need +for an explicit quoting or wrapping operation. This is the job of the `_` +function; the PE in this case should be `_(set)[_1]`. This works for functions +as well, as a convenient alternative to registering functions: `_(math.sin)(_1)`. +This is equivalent to using the `lines' method: + + for line in I(_(f):read()) do print(line) end + +Now this will work for _any_ 'file-like' object which which has a `read` method +returning the next line. If you had a LuaSocket client which was being 'pushed' +by lines sent from a server, then `_(s):receive '*l'` would create an iterator +for accepting input. These forms can be convenient for adapting your data flow so +that it can be passed to the sequence functions in `pl.seq'. + +Placeholder expressions can be mixed with sequence wrapper expressions. +`lexer.lua` will give us a double-valued sequence of tokens, where the first +value is a type, and the second is a value. We filter out only the values where +the type is 'iden', extract the actual value using `map`, get the unique values +and finally copy to a list. + + > str = 'for i=1,10 do for j = 1,10 do print(i,j) end end' + > = seq(lexer.lua(str)):filter('==','iden'):map(_2):unique():copy() + {i,print,j} + +This is a particularly intense line (and I don't always suggest making everything +a one-liner!); the key is the behaviour of `map`, which will take both values of +the sequence, so `_2` returns the value part. (Since `filter` here takes extra +arguments, it only operates on the type values.) + +There are some performance considerations to using placeholder expressions. +Instantiating a PE requires constructing and compiling a function, which is not +such a fast operation. So to get best performance, factor out PEs from loops like +this; + + local fn = I(_1:f() + _2:g()) + for i = 1,n do + res[i] = tablex.map2(fn,first[i],second[i]) + end + + diff --git a/Data/Libraries/Penlight/docs_topics/08-additional.md b/Data/Libraries/Penlight/docs_topics/08-additional.md new file mode 100644 index 0000000..2c99497 --- /dev/null +++ b/Data/Libraries/Penlight/docs_topics/08-additional.md @@ -0,0 +1,600 @@ +## Additional Libraries + +Libraries in this section are no longer considered to be part of the Penlight +core, but still provide specialized functionality when needed. + +<a id="sip"/> + +### Simple Input Patterns + +Lua string pattern matching is very powerful, and usually you will not need a +traditional regular expression library. Even so, sometimes Lua code ends up +looking like Perl, which happens because string patterns are not always the +easiest things to read, especially for the casual reader. Here is a program +which needs to understand three distinct date formats: + + -- parsing dates using Lua string patterns + months={Jan=1,Feb=2,Mar=3,Apr=4,May=5,Jun=6, + Jul=7,Aug=8,Sep=9,Oct=10,Nov=11,Dec=12} + + function check_and_process(d,m,y) + d = tonumber(d) + m = tonumber(m) + y = tonumber(y) + .... + end + + for line in f:lines() do + -- ordinary (English) date format + local d,m,y = line:match('(%d+)/(%d+)/(%d+)') + if d then + check_and_process(d,m,y) + else -- ISO date?? + y,m,d = line:match('(%d+)%-(%d+)%-(%d+)') + if y then + check_and_process(d,m,y) + else -- <day> <month-name> <year>? + d,mm,y = line:match('%(d+)%s+(%a+)%s+(%d+)') + m = months[mm] + check_and_process(d,m,y) + end + end + end + +These aren't particularly difficult patterns, but already typical issues are +appearing, such as having to escape '-'. Also, `string.match` returns its +captures, so that we're forced to use a slightly awkward nested if-statement. + +Verification issues will further cloud the picture, since regular expression +people try to enforce constraints (like year cannot be more than four digits) +using regular expressions, on the usual grounds that you shouldn't stop using a +hammer when you are enjoying yourself. + +`pl.sip` provides a simple, intuitive way to detect patterns in strings and +extract relevant parts. + + > sip = require 'pl.sip' + > dump = require('pl.pretty').dump + > res = {} + > c = sip.compile 'ref=$S{file}:$d{line}' + > = c('ref=hello.c:10',res) + true + > dump(res) + { + line = 10, + file = "hello.c" + } + > = c('ref=long name, no line',res) + false + +`sip.compile` creates a pattern matcher function, which takes a string and a +table as arguments. If the string matches the pattern, then `true` is returned +and the table is populated according to the captures within the pattern. + +Here is another version of the date parser: + + -- using SIP patterns + function check(t) + check_and_process(t.day,t.month,t.year) + end + + shortdate = sip.compile('$d{day}/$d{month}/$d{year}') + longdate = sip.compile('$d{day} $v{mon} $d{year}') + isodate = sip.compile('$d{year}-$d{month}-$d{day}') + + for line in f:lines() do + local res = {} + if shortdate(str,res) then + check(res) + elseif isodate(str,res) then + check(res) + elseif longdate(str,res) then + res.month = months[res.mon] + check(res) + end + end + +SIP captures start with '$', then a one-character type, and then an +optional variable name in curly braces. + + Type Meaning + v identifier + i possibly signed integer + f floating-point number + r rest of line + q quoted string (quoted using either ' or ") + p a path name + ( anything inside balanced parentheses + [ anything inside balanced brackets + { anything inside balanced curly brackets + < anything inside balanced angle brackets + +If a type is not one of the above, then it's assumed to be one of the standard +Lua character classes, and will match one or more repetitions of that class. +Any spaces you leave in your pattern will match any number of spaces, including +zero, unless the spaces are between two identifier characters or patterns +matching them; in that case, at least one space will be matched. + +SIP captures (like `$v{mon}`) do not have to be named. You can use just `$v`, but +you have to be consistent; if a pattern contains unnamed captures, then all +captures must be unnamed. In this case, the result table is a simple list of +values. + +`sip.match` is a useful shortcut if you want to compile and match in one call, +without saving the compiled pattern. It caches the result, so it is not much +slower than explicitly using `sip.compile`. + + > sip.match('($q{first},$q{second})','("john","smith")',res) + true + > res + {second='smith',first='john'} + > res = {} + > sip.match('($q,$q)','("jan","smit")',res) -- unnamed captures + true + > res + {'jan','smit'} + > sip.match('($q,$q)','("jan", "smit")',res) + false ---> oops! Can't handle extra space! + > sip.match('( $q , $q )','("jan", "smit")',res) + true + +As a general rule, allow for whitespace in your patterns. + +Finally, putting a '$' at the end of a pattern means 'capture the rest of the +line, starting at the first non-space'. It is a shortcut for '$r{rest}', +or just '$r' if no named captures are used. + + > sip.match('( $q , $q ) $','("jan", "smit") and a string',res) + true + > res + {'jan','smit','and a string'} + > res = {} + > sip.match('( $q{first} , $q{last} ) $','("jan", "smit") and a string',res) + true + > res + {first='jan',rest='and a string',last='smit'} + + +<a id="lapp"/> + +### Command-line Programs with Lapp + +`pl.lapp` is a small and focused Lua module which aims to make standard +command-line parsing easier and intuitive. It implements the standard GNU style, +i.e. short flags with one letter start with '-', and there may be an additional +long flag which starts with '--'. Generally options which take an argument expect +to find it as the next parameter (e.g. 'gcc test.c -o test') but single short +options taking a value can dispense with the space (e.g. 'head -n4 +test.c' or `gcc -I/usr/include/lua/5.1 ...`) + +As far as possible, Lapp will convert parameters into their equivalent Lua types, +i.e. convert numbers and convert filenames into file objects. If any conversion +fails, or a required parameter is missing, an error will be issued and the usage +text will be written out. So there are two necessary tasks, supplying the flag +and option names and associating them with a type. + +For any non-trivial script, even for personal consumption, it's necessary to +supply usage text. The novelty of Lapp is that it starts from that point and +defines a loose format for usage strings which can specify the names and types of +the parameters. + +An example will make this clearer: + + -- scale.lua + lapp = require 'pl.lapp' + local args = lapp [[ + Does some calculations + -o,--offset (default 0.0) Offset to add to scaled number + -s,--scale (number) Scaling factor + <number> (number) Number to be scaled + ]] + + print(args.offset + args.scale * args.number) + +Here is a command-line session using this script: + + $ lua scale.lua + scale.lua:missing required parameter: scale + + Does some calculations + -o,--offset (default 0.0) Offset to add to scaled number + -s,--scale (number) Scaling factor + <number> (number ) Number to be scaled + + $ lua scale.lua -s 2.2 10 + 22 + + $ lua scale.lua -s 2.2 x10 + scale.lua:unable to convert to number: x10 + + ....(usage as before) + +There are two kinds of lines in Lapp usage strings which are meaningful; option +and parameter lines. An option line gives the short option, optionally followed +by the corresponding long option. A type specifier in parentheses may follow. +Similarly, a parameter line starts with '<NAME>', followed by a type +specifier. + +Type specifiers usually start with a type name: one of 'boolean', 'string','number','file-in' or +'file-out'. You may leave this out, but then _must_ say 'default' followed by a value. +If a flag or parameter has a default, it is not _required_ and is set to the default. The actual +type is deduced from this value (number, string, file or boolean) if not provided directly. +'Deduce' is a fancy word for 'guess' and it can be wrong, e.g '(default 1)' +will always be a number. You can say '(string default 1)' to override the guess. +There are file values for the predefined console streams: stdin, stdout, stderr. + +The boolean type is the default for flags. Not providing the type specifier is equivalent to +'(boolean default false)`. If the flag is meant to be 'turned off' then either the full +'(boolean default true)` or the shortcut '(default true)' will work. + +An alternative to `default` is `optional`: + + local lapp = require 'pl.lapp' + local args = lapp [[ + --cmd (optional string) Command to run. + ]] + + if args.cmd then + os.execute(args.cmd) + end + +Here we're implying that `cmd` need not be specified (just as with `default`) but if not +present, then `args.cmd` is `nil`, which will always test false. + +The rest of the line is ignored and can be used for explanatory text. + +This script shows the relation between the specified parameter names and the +fields in the output table. + + -- simple.lua + local args = require ('pl.lapp') [[ + Various flags and option types + -p A simple optional flag, defaults to false + -q,--quiet A simple flag with long name + -o (string) A required option with argument + -s (default 'save') Optional string with default 'save' (single quotes ignored) + -n (default 1) Optional numerical flag with default 1 + -b (string default 1) Optional string flag with default '1' (type explicit) + <input> (default stdin) Optional input file parameter, reads from stdin + ]] + + for k,v in pairs(args) do + print(k,v) + end + +I've just dumped out all values of the args table; note that args.quiet has +become true, because it's specified; args.p defaults to false. If there is a long +name for an option, that will be used in preference as a field name. A type or +default specifier is not necessary for simple flags, since the default type is +boolean. + + $ simple -o test -q simple.lua + p false + input file (781C1BD8) + quiet true + o test + input_name simple.lua + D:\dev\lua\lapp>simple -o test simple.lua one two three + 1 one + 2 two + 3 three + p false + quiet false + input file (781C1BD8) + o test + input_name simple.lua + +The parameter input has been set to an open read-only file object - we know it +must be a read-only file since that is the type of the default value. The field +input_name is automatically generated, since it's often useful to have access to +the original filename. + +Notice that any extra parameters supplied will be put in the result table with +integer indices, i.e. args[i] where i goes from 1 to #args. + +Files don't really have to be closed explicitly for short scripts with a quick +well-defined mission, since the result of garbage-collecting file objects is to +close them. + +#### Enforcing a Range and Enumerations + +The type specifier can also be of the form '(' MIN '..' MAX ')' or a set of strings +separated by '|'. + + local lapp = require 'pl.lapp' + local args = lapp [[ + Setting ranges + <x> (1..10) A number from 1 to 10 + <y> (-5..1e6) Bigger range + <z> (slow|medium|fast) + ]] + + print(args.x,args.y) + +Here the meaning of ranges is that the value is greater or equal to MIN and less or equal +to MAX. +An 'enum' is a _string_ that can only have values from a specified set. + +#### Custom Types + +There is no builti-in way to force a parameter to be a whole number, but +you may define a custom type that does this: + + lapp = require ('pl.lapp') + + lapp.add_type('integer','number', + function(x) + lapp.assert(math.ceil(x) == x, 'not an integer!') + end + ) + + local args = lapp [[ + <ival> (integer) Process PID + ]] + + print(args.ival) + +`lapp.add_type` takes three parameters, a type name, a converter and a constraint +function. The constraint function is expected to throw an assertion if some +condition is not true; we use `lapp.assert` because it fails in the standard way +for a command-line script. The converter argument can either be a type name known +to Lapp, or a function which takes a string and generates a value. + +Here's a useful custom type that allows dates to be input as @{pl.Date} values: + + local df = Date.Format() + + lapp.add_type('date', + function(s) + local d,e = df:parse(s) + lapp.assert(d,e) + return d + end + ) + +#### 'varargs' Parameter Arrays + + lapp = require 'pl.lapp' + local args = lapp [[ + Summing numbers + <numbers...> (number) A list of numbers to be summed + ]] + + local sum = 0 + for i,x in ipairs(args.numbers) do + sum = sum + x + end + print ('sum is '..sum) + +The parameter number has a trailing '...', which indicates that this parameter is +a 'varargs' parameter. It must be the last parameter, and args.number will be an +array. + +Consider this implementation of the head utility from Mac OS X: + + -- implements a BSD-style head + -- (see http://www.manpagez.com/man/1/head/osx-10.3.php) + + lapp = require ('pl.lapp') + + local args = lapp [[ + Print the first few lines of specified files + -n (default 10) Number of lines to print + <files...> (default stdin) Files to print + ]] + + -- by default, lapp converts file arguments to an actual Lua file object. + -- But the actual filename is always available as <file>_name. + -- In this case, 'files' is a varargs array, so that 'files_name' is + -- also an array. + local nline = args.n + local nfile = #args.files + for i = 1,nfile do + local file = args.files[i] + if nfile > 1 then + print('==> '..args.files_name[i]..' <==') + end + local n = 0 + for line in file:lines() do + print(line) + n = n + 1 + if n == nline then break end + end + end + +Note how we have access to all the filenames, because the auto-generated field +`files_name` is also an array! + +(This is probably not a very considerate script, since Lapp will open all the +files provided, and only close them at the end of the script. See the `xhead.lua` +example for another implementation.) + +Flags and options may also be declared as vararg arrays, and can occur anywhere. +If there is both a short and long form, then the trailing "..." must happen after the long form, +for example "-x,--network... (string)...", + +Bear in mind that short options can be combined (like 'tar -xzf'), so it's +perfectly legal to have '-vvv'. But normally the value of args.v is just a simple +`true` value. + + local args = require ('pl.lapp') [[ + -v... Verbosity level; can be -v, -vv or -vvv + ]] + vlevel = not args.v[1] and 0 or #args.v + print(vlevel) + +The vlevel assigment is a bit of Lua voodoo, so consider the cases: + + * No -v flag, v is just { false } + * One -v flags, v is { true } + * Two -v flags, v is { true, true } + * Three -v flags, v is { true, true, true } + +#### Defining a Parameter Callback + +If a script implements `lapp.callback`, then Lapp will call it after each +argument is parsed. The callback is passed the parameter name, the raw unparsed +value, and the result table. It is called immediately after assignment of the +value, so the corresponding field is available. + + lapp = require ('pl.lapp') + + function lapp.callback(parm,arg,args) + print('+',parm,arg) + end + + local args = lapp [[ + Testing parameter handling + -p Plain flag (defaults to false) + -q,--quiet Plain flag with GNU-style optional long name + -o (string) Required string option + -n (number) Required number option + -s (default 1.0) Option that takes a number, but will default + <start> (number) Required number argument + <input> (default stdin) A parameter which is an input file + <output> (default stdout) One that is an output file + ]] + print 'args' + for k,v in pairs(args) do + print(k,v) + end + +This produces the following output: + + $ args -o name -n 2 10 args.lua + + o name + + n 2 + + start 10 + + input args.lua + args + p false + s 1 + input_name args.lua + quiet false + output file (781C1B98) + start 10 + input file (781C1BD8) + o name + n 2 + +Callbacks are needed when you want to take action immediately on parsing an +argument. + +#### Slack Mode + +If you'd like to use a multi-letter 'short' parameter you need to set +the `lapp.slack` variable to `true`. + +In the following example we also see how default `false` and default `true` flags can be used +and how to overwrite the default `-h` help flag (`--help` still works fine) - this applies +to non-slack mode as well. + + -- Parsing the command line ---------------------------------------------------- + -- test.lua + local lapp = require 'pl.lapp' + local pretty = require 'pl.pretty' + lapp.slack = true + local args = lapp [[ + Does some calculations + -v, --video (string) Specify input video + -w, --width (default 256) Width of the video + -h, --height (default 144) Height of the video + -t, --time (default 10) Seconds of video to process + -sk,--seek (default 0) Seek number of seconds + -f1,--flag1 A false flag + -f2,--flag2 A false flag + -f3,--flag3 (default true) A true flag + -f4,--flag4 (default true) A true flag + ]] + + pretty.dump(args) + +And here we can see the output of `test.lua`: + + $> lua test.lua -v abc --time 40 -h 20 -sk 15 --flag1 -f3 + ----> + { + width = 256, + flag1 = true, + flag3 = false, + seek = 15, + flag2 = false, + video = abc, + time = 40, + height = 20, + flag4 = true + } + +### Simple Test Framework + +`pl.test` was originally developed for the sole purpose of testing Penlight itself, +but you may find it useful for your own applications. ([There are many other options](http://lua-users.org/wiki/UnitTesting).) + +Most of the goodness is in `test.asserteq`. It uses `tablex.deepcompare` on its two arguments, +and by default quits the test application with a non-zero exit code, and an informative +message printed to stderr: + + local test = require 'pl.test' + + test.asserteq({10,20,30},{10,20,30.1}) + + --~ test-test.lua:3: assertion failed + --~ got: { + --~ [1] = 10, + --~ [2] = 20, + --~ [3] = 30 + --~ } + --~ needed: { + --~ [1] = 10, + --~ [2] = 20, + --~ [3] = 30.1 + --~ } + --~ these values were not equal + +This covers most cases but it's also useful to compare strings using `string.match` + + -- must start with bonzo the dog + test.assertmatch ('bonzo the dog is here','^bonzo the dog') + -- must end with an integer + test.assertmatch ('hello 42','%d+$') + +Since Lua errors are usually strings, this matching strategy is used to test 'exceptions': + + test.assertraise(function() + local t = nil + print(t.bonzo) + end,'nil value') + +(Some care is needed to match the essential part of the thrown error if you care +for portability, since in Lua 5.2 +the exact error is "attempt to index local 't' (a nil value)" and in Lua 5.3 the error +is "attempt to index a nil value (local 't')") + +There is an extra optional argument to these test functions, which is helpful when writing +test helper functions. There you want to highlight the failed line, not the actual call +to `asserteq` or `assertmatch` - line 33 here is the call to `is_iden` + + function is_iden(str) + test.assertmatch(str,'^[%a_][%w_]*$',1) + end + + is_iden 'alpha_dog' + is_iden '$dollars' + + --~ test-test.lua:33: assertion failed + --~ got: "$dollars" + --~ needed: "^[%a_][%w_]*$" + --~ these strings did not match + +Useful Lua functions often return multiple values, and `test.tuple` is a convenient way to +capture these values, whether they contain nils or not. + + T = test.tuple + + --- common error pattern + function failing() + return nil,'failed' + end + + test.asserteq(T(failing()),T(nil,'failed')) + diff --git a/Data/Libraries/Penlight/docs_topics/09-discussion.md b/Data/Libraries/Penlight/docs_topics/09-discussion.md new file mode 100644 index 0000000..7942d95 --- /dev/null +++ b/Data/Libraries/Penlight/docs_topics/09-discussion.md @@ -0,0 +1,91 @@ +## Technical Choices + +### Modularity and Granularity + +In an ideal world, a program should only load the libraries it needs. Penlight is +intended to work in situations where an extra 100Kb of bytecode could be a +problem. It is straightforward but tedious to load exactly what you need: + + local data = require 'pl.data' + local List = require 'pl.List' + local array2d = require 'pl.array2d' + local seq = require 'pl.seq' + local utils = require 'pl.utils' + +This is the style that I follow in Penlight itself, so that modules don't mess +with the global environment; also, `stringx.import()` is not used because it will +update the global `string` table. + +But `require 'pl'` is more convenient in scripts; the question is how to ensure +that one doesn't load the whole kitchen sink as the price of convenience. The +strategy is to only load modules when they are referenced. In 'init.lua' (which +is loaded by `require 'pl'`) a metatable is attached to the global table with an +`__index` metamethod. Any unknown name is looked up in the list of modules, and +if found, we require it and make that module globally available. So when +`tablex.deepcompare` is encountered, looking up `tablex` causes 'pl.tablex' to be +required. . + +Modifying the behaviour of the global table has consequences. For instance, there +is the famous module `strict` which comes with Lua itself (perhaps the only +standard Lua module written in Lua itself) which also does this modification so +that global variiables must be defined before use. So the implementation in +'init.lua' allows for a 'not found' hook, which 'pl.strict.lua' uses. Other +libraries may install their own metatables for `_G`, but Penlight will now +forward any unknown name to the `__index` defined by the original metatable. + +But the strategy is worth the effort: the old 'kitchen sink' 'init.lua' would +pull in about 260K of bytecode, whereas now typical programs use about 100K less, +and short scripts even better - for instance, if they were only needing +functionality in `utils`. + +There are some functions which mark their output table with a special metatable, +when it seems particularly appropriate. For instance, `tablex.makeset` creates a +`Set`, and `seq.copy` creates a `List`. But this does not automatically result in +the loading of `pl.Set` and `pl.List`; only if you try to access any of these +methods. In 'utils.lua', there is an exported table called `stdmt`: + + stdmt = { List = {}, Map = {}, Set = {}, MultiMap = {} } + +If you go through 'init.lua', then these plain little 'identity' tables get an +`__index` metamethod which forces the loading of the full functionality. Here is +the code from 'list.lua' which starts the ball rolling for lists: + + List = utils.stdmt.List + List.__index = List + List._name = "List" + List._class = List + +The 'load-on-demand' strategy helps to modularize the library. Especially for +more casual use, `require 'pl'` is a good compromise between convenience and +modularity. + +In this current version, I have generally reduced the amount of trickery +involved. Previously, `Map` was defined in `pl.class`; now it is sensibly defined +in `pl.Map`; `pl.class` only contains the basic class mechanism (and returns that +function.) For consistency, `List` is returned directly by `require 'pl.List'` +(note the uppercase 'L'), Also, the amount of module dependencies in the +non-core libraries like `pl.config` have been reduced. + +### Defining what is Callable + +'utils.lua' exports `function_arg` which is used extensively throughout Penlight. +It defines what is meant by 'callable'. Obviously true functions are immediately +passed back. But what about strings? The first option is that it represents an +operator in 'operator.lua', so that '<' is just an alias for `operator.lt`. + +We then check whether there is a _function factory_ defined for the metatable of +the value. + +(It is true that strings can be made callable, but in practice this turns out to +be a cute but dubious idea, since _all_ strings share the same metatable. A +common programming error is to pass the wrong kind of object to a function, and +it's better to get a nice clean 'attempting to call a string' message rather than +some obscure trace from the bowels of your library.) + +The other module that registers a function factory is `pl.func`. Placeholder +expressions cannot be directly calleable, and so need to be instantiated and +cached in as efficient way as possible. + +(An inconsistency is that `utils.is_callable` does not do this thorough check.) + + diff --git a/Data/Libraries/Penlight/examples/seesubst.lua b/Data/Libraries/Penlight/examples/seesubst.lua new file mode 100644 index 0000000..a2d0f18 --- /dev/null +++ b/Data/Libraries/Penlight/examples/seesubst.lua @@ -0,0 +1,55 @@ +-- shows how replacing '@see module' in the Markdown documentation +-- can be done more elegantly using PL. +-- We either have something like 'pl.config' (a module reference) +-- or 'pl.seq.map' (a function reference); these cases must be distinguished +-- and a Markdown link generated pointing to the LuaDoc file. + +local sip = require 'pl.sip' +local stringx = require 'pl.stringx' + +local res = {} +local s = [[ +(@see pl.bonzo.dog) +remember about @see pl.bonzo + +]] + +local _gsub_patterns = {} + +local function gsub (s,pat,subst,start) + local fpat = _gsub_patterns[pat] + if not fpat then + -- use SIP to generate a proper string pattern. + -- the _whole thing_ is a capture, to get the whole match + -- and the unnamed capture. + fpat = '('..sip.create_pattern(pat)..')' + _gsub_patterns[pat] = fpat + end + return s:gsub(fpat,subst,start) +end + + +local mod = sip.compile '$v.$v' +local fun = sip.compile '$v.$v.$v' + +for line in stringx.lines(s) do + line = gsub(line,'@see $p',function(see,path) + if fun(path,res) or mod(path,res) then + local ret = ('[see %s](%s.%s.html'):format(path,res[1],res[2]) + if res[3] then + return ret..'#'..res[3]..')' + else + return ret..')' + end + end + end) + print(line) +end + + + + + + + + diff --git a/Data/Libraries/Penlight/examples/sipscan.lua b/Data/Libraries/Penlight/examples/sipscan.lua new file mode 100644 index 0000000..78ac75b --- /dev/null +++ b/Data/Libraries/Penlight/examples/sipscan.lua @@ -0,0 +1,34 @@ +-- another SIP example, shows how an awkward log file format +-- can be parsed. It also prints out the actual Lua string +-- pattern generated: +-- SYNC%s*%[([+%-%d]%d*)%]%s*([+%-%d]%d*)%s*([+%-%d]%d*) + +local sip = require 'pl.sip' +local stringx = require 'pl.stringx' + +local s = [[ +SYNC [1] 0 547 (14679 sec) +SYNC [2] 0 555 (14679 sec) +SYNC [3] 0 563 (14679 sec) +SYNC [4] 0 571 (14679 sec) +SYNC [5] -1 580 (14679 sec) +SYNC [6] 0 587 (14679 sec) +]] + + +local first = true +local expected +local res = {} +local pat = 'SYNC [$i{seq}] $i{diff} $i{val}' +print(sip.create_pattern(pat)) +local match = sip.compile(pat) +for line in stringx.lines(s) do + if match(line,res) then + if first then + expected = res.val + first = false + end + print(res.val,expected - res.val) + expected = expected + 8 + end +end diff --git a/Data/Libraries/Penlight/examples/symbols.lua b/Data/Libraries/Penlight/examples/symbols.lua new file mode 100644 index 0000000..e73c4ba --- /dev/null +++ b/Data/Libraries/Penlight/examples/symbols.lua @@ -0,0 +1,223 @@ +require 'pl' +utils.import 'pl.func' +local ops = require 'pl.operator' +local List = require 'pl.List' +local append,concat = table.insert,table.concat +local compare,find_if,compare_no_order,imap,reduce,count_map = tablex.compare,tablex.find_if,tablex.compare_no_order,tablex.imap,tablex.reduce,tablex.count_map +local unpack = table.unpack + +function bindval (self,val) + rawset(self,'value',val) +end + +local optable = ops.optable + +function sexpr (e) + if isPE(e) then + if e.op ~= 'X' then + local args = tablex.imap(sexpr,e) + return '('..e.op..' '..table.concat(args,' ')..')' + else + return e.repr + end + else + return tostring(e) + end +end + + +psexpr = compose(print,sexpr) + + + +function equals (e1,e2) + local p1,p2 = isPE(e1),isPE(e2) + if p1 ~= p2 then return false end -- different kinds of animals! + if p1 and p2 then -- both PEs + -- operators must be the same + if e1.op ~= e2.op then return false end + -- PHs are equal if their representations are equal + if e1.op == 'X' then return e1.repr == e2.repr + -- commutative operators + elseif e1.op == '+' or e1.op == '*' then + return compare_no_order(e1,e2,equals) + else + -- arguments must be the same + return compare(e1,e2,equals) + end + else -- fall back on simple equality for non PEs + return e1 == e2 + end +end + +-- run down an unbalanced operator chain (like a+b+c) and return the arguments {a,b,c} +function tcollect (op,e,ls) + if isPE(e) and e.op == op then + for i = 1,#e do + tcollect(op,e[i],ls) + end + else + ls:append(e) + return + end +end + +function rcollect (e) + local res = List() + tcollect(e.op,e,res) + return res +end + + +-- balance ensures that +/* chains are collected together, operates in-place. +-- thus (+(+ a b) c) or (+ a (+ b c)) becomes (+ a b c), order immaterial +function balance (e) + if isPE(e) and e.op ~= 'X' then + local op,args = e.op + if op == '+' or op == '*' then + args = rcollect(e) + else + args = imap(balance,e) + end + for i = 1,#args do + e[i] = args[i] + end + end + return e +end + +-- fold constants in an expression +function fold (e) + if isPE(e) then + if e.op == 'X' then + -- there could be _bound values_! + local val = rawget(e,'value') + return val and val or e + else + local op = e.op + local addmul = op == '*' or op == '+' + -- first fold all arguments + local args = imap(fold,e) + if not addmul and not find_if(args,isPE) then + -- no placeholders in these args, we can fold the expression. + local opfn = optable[op] + if opfn then + return opfn(unpack(args)) + else + return '?' + end + elseif addmul then + -- enforce a few rules for + and * + -- split the args into two classes, PE args and non-PE args. + local classes = List.partition(args,isPE) + local pe,npe = classes[true],classes[false] + if npe then -- there's at least one non PE argument + -- so fold them + if #npe == 1 then npe = npe[1] + else npe = npe:reduce(optable[op]) + end + -- if the result is a constant, return it + if not pe then return npe end + + -- either (* 1 x) => x or (* 1 x y ...) => (* x y ...) + if op == '*' then + if npe == 0 then return 0 + elseif npe == 1 then -- identity + if #pe == 1 then return pe[1] else npe = nil end + end + else -- special cases for + + if npe == 0 then -- identity + if #pe == 1 then return pe[1] else npe = nil end + end + end + end + -- build up the final arguments + local res = {} + if npe then append(res,npe) end + for val,count in pairs(count_map(pe,equals)) do + if count > 1 then + if op == '*' then val = val ^ count + else val = val * count + end + end + append(res,val) + end + if #res == 1 then return res[1] end + return PE{op=op,unpack(res)} + elseif op == '^' then + if args[2] == 1 then return args[1] end -- identity + if args[2] == 0 then return 1 end + end + return PE{op=op,unpack(args)} + end + else + return e + end +end + +function expand (e) + if isPE(e) and e.op == '*' and isPE(e[2]) and e[2].op == '+' then + local a,b = e[1],e[2] + return expand(b[1]*a) + expand(b[2]*a) + else + return e + end +end + +function isnumber (x) + return type(x) == 'number' +end + +-- does this PE contain a reference to x? +function references (e,x) + if isPE(e) then + if e.op == 'X' then return x.repr == e.repr + else + return find_if(e,references,x) + end + else + return false + end +end + +local function muli (args) + return PE{op='*',unpack(args)} +end + +local function addi (args) + return PE{op='+',unpack(args)} +end + +function diff (e,x) + if isPE(e) and references(e,x) then + local op = e.op + if op == 'X' then + return 1 + else + local a,b = e[1],e[2] + if op == '+' then -- differentiation is linear + local args = imap(diff,e,x) + return balance(addi(args)) + elseif op == '*' then -- product rule + local res,d,ee = {} + for i = 1,#e do + d = fold(diff(e[i],x)) + if d ~= 0 then + ee = {unpack(e)} + ee[i] = d + append(res,balance(muli(ee))) + end + end + if #res > 1 then return addi(res) + else return res[1] end + elseif op == '^' and isnumber(b) then -- power rule + return b*x^(b-1) + end + end + else + return 0 + end +end + + + diff --git a/Data/Libraries/Penlight/examples/test-cmp.lua b/Data/Libraries/Penlight/examples/test-cmp.lua new file mode 100644 index 0000000..cbab394 --- /dev/null +++ b/Data/Libraries/Penlight/examples/test-cmp.lua @@ -0,0 +1,3 @@ +local A = require 'pl.tablex' +print(A.compare_no_order({1,2,3},{2,1,3})) +print(A.compare_no_order({1,2,3},{2,1,3},'==')) diff --git a/Data/Libraries/Penlight/examples/test-listcallbacks.lua b/Data/Libraries/Penlight/examples/test-listcallbacks.lua new file mode 100644 index 0000000..a9a31c3 --- /dev/null +++ b/Data/Libraries/Penlight/examples/test-listcallbacks.lua @@ -0,0 +1,11 @@ +-- demonstrates how to use a list of callbacks +local List = require 'pl.List' +local utils = require 'pl.utils' +local actions = List() +local L = utils.string_lambda + +actions:append(function() print 'hello' end) +actions:append(L '|| print "yay"') + +-- '()' is a shortcut for operator.call or function(x) return x() end +actions:foreach '()' diff --git a/Data/Libraries/Penlight/examples/test-pretty.lua b/Data/Libraries/Penlight/examples/test-pretty.lua new file mode 100644 index 0000000..7b2b553 --- /dev/null +++ b/Data/Libraries/Penlight/examples/test-pretty.lua @@ -0,0 +1,13 @@ +local pretty = require 'pl.pretty' + +local tb = { + 'one','two','three',{1,2,3}, + alpha=1,beta=2,gamma=3,['&']=true,[0]=false, + _fred = {true,true}, + s = [[ +hello dolly +you're so fine +]] +} + +print(pretty.write(tb)) diff --git a/Data/Libraries/Penlight/examples/test-symbols.lua b/Data/Libraries/Penlight/examples/test-symbols.lua new file mode 100644 index 0000000..2b80aac --- /dev/null +++ b/Data/Libraries/Penlight/examples/test-symbols.lua @@ -0,0 +1,85 @@ +require 'pl' +-- force us to look in the script's directory when requiring... +app.require_here() +require 'symbols' + +local MT = getmetatable(_1) + +add = MT.__add +mul = MT.__mul +pow = MT.__pow + + +function testeq (e1,e2) + if not equals(e1,e2) then + print ('Not equal',repr(e1),repr(e2)) + end +end + +sin = register(math.sin,'sin') + +f = register(function(x,y,z) end) + +--[[ +testeq (_1,_1) +testeq (_1+_2,_1+_2) +testeq (_1 + 3*_2,_1 + 3*_2) +testeq (_2+_1,_1+_2) +testeq (sin(_1),sin(_1)) +testeq (1+f(10,20,'ok'),f(10,20,'ok')+1) +--]] + + +function testexpand (e) + print(repr(fold(expand(e)))) --fold +end + +--[[ +testexpand (a*(a+1)) + +testexpand ((x+2)*(b+1)) +]]-- + +function testfold (e) + print(repr(fold(e))) +end + +a,b,c,x,y = Var 'a,b,c,x,y' + +--~ testfold(_1 + _2) +--~ testfold(add(10,20)) +--~ testfold(add(mul(2,_1),mul(3,_2))) +--[[ +testfold(sin(a)) +e = a^(b+2) +testfold(e) +bindval(b,1) +testfold(e) +bindval(a,2) +testfold(e) + +bindval(a) +bindval(b) +]] + + + +function testdiff (e) + balance(e) + e = diff(e,x) + balance(e) + print('+ ',e) + e = fold(e) + print('- ',e) +end + + +testdiff(x^2+1) +testdiff(3*x^2) +testdiff(x^2 + 2*x^3) +testdiff(x^2 + 2*a*x^3 + x^4) +testdiff(2*a*x^3) +testdiff(x*x*x) + + + diff --git a/Data/Libraries/Penlight/examples/testclone.lua b/Data/Libraries/Penlight/examples/testclone.lua new file mode 100644 index 0000000..b0d948f --- /dev/null +++ b/Data/Libraries/Penlight/examples/testclone.lua @@ -0,0 +1,40 @@ +--cloning a directory tree. +local lfs = require 'lfs' +local path = require 'pl.path' +local dir = require 'pl.dir' + +local p1 = [[examples]] +local p2 = [[copy/of/examples]] + +if not path.isfile 'examples/testclone.lua' then + return print 'please run this in the penlight folder (below examples)' +end + +-- make a copy of the examples folder +dir.clonetree(p1,p2,dir.copyfile) + +assert(path.isdir 'copy') + +print '---' +local t = os.time() +print(lfs.touch('examples/testclone.lua',t,t+10)) + +-- this should only update this file +dir.clonetree(p1,p2, +function(f1,f2) + local t1 = path.getmtime(f1) + local t2 = path.getmtime(f2) + --print(f1,t1,f2,t2) + if t1 > t2 then + dir.copyfile(f1,f2) + print(f1,f2,t1,t2) + end + return true +end) + +-- and get rid of the whole copy directory, with subdirs +dir.rmtree 'copy' + +assert(not path.exists 'copy') + + diff --git a/Data/Libraries/Penlight/examples/testconfig.lua b/Data/Libraries/Penlight/examples/testconfig.lua new file mode 100644 index 0000000..4712747 --- /dev/null +++ b/Data/Libraries/Penlight/examples/testconfig.lua @@ -0,0 +1,51 @@ +local stringio = require 'pl.stringio' +local config = require 'pl.config' + +local function dump(t,indent) + if type(t) == 'table' then + io.write(indent,'{\n') + local newindent = indent..' ' + for k,v in pairs(t) do + io.write(newindent,k,'=') + dump(v,indent) + io.write('\n') + end + io.write(newindent,'},\n') + else + io.write(indent,t,'(',type(t),')') + end +end + + +local function testconfig(test) + local f = stringio.open(test) + local c = config.read(f) + f:close() + dump(c,' ') + print '-----' +end + +testconfig [[ + ; comment 2 (an ini file) +[section!] +bonzo.dog=20,30 +config_parm=here we go again +depth = 2 +[another] +felix="cat" +]] + +testconfig [[ +# this is a more Unix-y config file +fred = 1 +alice = 2 +home = /bonzo/dog/etc +]] + +testconfig [[ +# this is just a set of comma-separated values +1000,444,222 +44,555,224 +]] + + diff --git a/Data/Libraries/Penlight/examples/testglobal.lua b/Data/Libraries/Penlight/examples/testglobal.lua new file mode 100644 index 0000000..0baaaef --- /dev/null +++ b/Data/Libraries/Penlight/examples/testglobal.lua @@ -0,0 +1,28 @@ +-- very simple lexer program which looks at all identifiers in a Lua +-- file and checks whether they're in the global namespace. +-- At the end, we dump out the result of count_map, which will give us +-- unique identifiers with their usage count. +-- (an example of a program which itself needs to be careful about what +-- goes into the global namespace) + +local utils = require 'pl.utils' +local file = require 'pl.file' +local lexer = require 'pl.lexer' +local List = require 'pl.List' +local pretty = require 'pl.pretty' +local seq = require 'pl.seq' +local path = require 'pl.path' + +utils.on_error 'quit' + +local txt = file.read(arg[1] or path.normpath('examples/testglobal.lua')) +local globals = List() +for t,v in lexer.lua(txt) do + if t == 'iden' and rawget(_G,v) then + globals:append(v) + end +end + +pretty.dump(seq.count_map(globals)) + + diff --git a/Data/Libraries/Penlight/examples/testinputfields.lua b/Data/Libraries/Penlight/examples/testinputfields.lua new file mode 100644 index 0000000..9269488 --- /dev/null +++ b/Data/Libraries/Penlight/examples/testinputfields.lua @@ -0,0 +1,13 @@ +local input = require 'pl.input' +local sum = 0.0 +local count = 0 +local text = [[ + 981124001 2.0 18988.4 10047.1 4149.7 + 981125001 0.8 19104.0 9970.4 5088.7 + 981127003 0.5 19012.5 9946.9 3831.2 +]] +for id,magn,x in input.fields(3,' ',text) do + sum = sum + x + count = count + 1 +end +print('average x coord is ',sum/count) diff --git a/Data/Libraries/Penlight/examples/testinputfields2.lua b/Data/Libraries/Penlight/examples/testinputfields2.lua new file mode 100644 index 0000000..fc649eb --- /dev/null +++ b/Data/Libraries/Penlight/examples/testinputfields2.lua @@ -0,0 +1,9 @@ +local input = require 'pl.input' +local seq = require 'pl.seq' +local text = [[ + 981124001 2.0 18988.4 10047.1 4149.7 + 981125001 0.8 19104.0 9970.4 5088.7 + 981127003 0.5 19012.5 9946.9 3831.2 +]] +local sum,count = seq.sum(input.fields ({3},' ',text)) +print(sum/count) diff --git a/Data/Libraries/Penlight/examples/testxml.lua b/Data/Libraries/Penlight/examples/testxml.lua new file mode 100644 index 0000000..2528020 --- /dev/null +++ b/Data/Libraries/Penlight/examples/testxml.lua @@ -0,0 +1,83 @@ +-- an example showing 'pl.lexer' doing some serious work. +-- The resulting Lua table is in the same LOM format used by luaexpat. +-- This is (clearly) not a professional XML parser, so don't use it +-- on your homework! + +local lexer = require 'pl.lexer' +local pretty = require 'pl.pretty' + +local append = table.insert +local skipws,expecting = lexer.skipws,lexer.expecting + +local function parse_element (tok,tag) + local tbl,t,v,attrib + tbl = {} + tbl.tag = tag -- LOM 'tag' is the element tag + t,v = skipws(tok) + while v ~= '/' and v ~= '>' do + if t ~= 'iden' then error('expecting attribute identifier') end + attrib = v + expecting(tok,'=') + v = expecting(tok,'string') + -- LOM: 'attr' subtable contains attrib/value pairs and an ordered list of attribs + if not tbl.attr then tbl.attr = {} end + tbl.attr[attrib] = v + append(tbl.attr,attrib) + t,v = skipws(tok) + end + if v == '/' then + expecting(tok,'>') + return tbl + end + -- pick up element data + t,v = tok() + while true do + if t == '<' then + t,v = skipws(tok) + if t == '/' then -- element end tag + t,v = tok() + if t == '>' then return tbl end + if t == 'iden' and v == tag then + if tok() == '>' then return tbl end + end + error('expecting end tag '..tag) + else + append(tbl,parse_element(tok,v)) -- LOM: child elements added to table + t,v = skipws(tok) + end + else + append(tbl,v) -- LOM: text added to table + t,v = skipws(tok) + end + end +end + +local function parse_xml (tok) + local t = skipws(tok) + local v + while t == '<' do + t,v = tok() + if t == '?' or t == '!' then + -- skip meta stuff and commentary + repeat t = tok() until t == '>' + t = expecting(tok,'<') + else + return parse_element(tok,v) + end + end +end + +local s = [[ +<?xml version="1.0" encoding="UTF-8"?> +<sensor name="closure-meter-2" id="7D7D0600006F0D00" loc="100,100,0" device="closure-meter" init="true"> +<detector name="closure-meter" phenomenon="closure" units="mm" id="1" + vmin="0" vmax="5000" device="closure-meter" calib="0,0;5000,5000" + sampling_interval="25000" measurement_interval="600000" +/> +</sensor> +]] + +local tok = lexer.scan(s,nil,{space=false},{string=true}) +local res = parse_xml(tok) +print(pretty.write(res)) + diff --git a/Data/Libraries/Penlight/examples/which.lua b/Data/Libraries/Penlight/examples/which.lua new file mode 100644 index 0000000..0544cfc --- /dev/null +++ b/Data/Libraries/Penlight/examples/which.lua @@ -0,0 +1,30 @@ +-- a simple implementation of the which command. This looks for +-- the given file on the path. On windows, it will assume an extension +-- of .exe if no extension is given. +local List = require 'pl.List' +local path = require 'pl.path' +local app = require 'pl.app' + +local pathl = List.split(os.getenv 'PATH',path.dirsep) + +local function which (file) + local res = pathl:map(path.join,file) + res = res:filter(path.exists) + if res then return res[1] end +end + +local _,lua = app.lua() +local file = arg[1] or lua -- i.e. location of lua executable +local try + +if not file then return print 'must provide a filename' end + +if path.extension(file) == '' and path.is_windows then + try = which(file..'.exe') +else + try = which(file) +end + +if try then print(try) else print 'cannot find on path' end + + diff --git a/Data/Libraries/Penlight/ldoc.ltp b/Data/Libraries/Penlight/ldoc.ltp new file mode 100644 index 0000000..9f0a566 --- /dev/null +++ b/Data/Libraries/Penlight/ldoc.ltp @@ -0,0 +1,315 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=$(ldoc.doc_charset)"/> +<head> + <title>$(ldoc.title)</title> + <link rel="stylesheet" href="$(ldoc.css)" type="text/css" /> +# if ldoc.custom_css then -- add custom CSS file if configured. + <link rel="stylesheet" href="$(ldoc.custom_css)" type="text/css" /> +# end +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + +# local no_spaces = ldoc.no_spaces +# local use_li = ldoc.use_li +# local display_name = ldoc.display_name +# local iter = ldoc.modules.iter +# local function M(txt,item) return ldoc.markup(txt,item,ldoc.plain) end +# local nowrap = ldoc.wrap and '' or 'nowrap' + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>$(ldoc.project)</h1> + +<ul> + <li><a href="https://github.com/lunarmodules/Penlight">GitHub Project</a></li> +# if not ldoc.single and module then -- reference back to project index + <li><a href="../$(ldoc.output).html">Documentation</a></li> +# else + <li><a href="$(ldoc.output).html">Documentation</a></li> +# end +</ul> + +# --------- contents of module ------------- +# if module and not ldoc.no_summary and #module.items > 0 then +<h2>Contents</h2> +<ul> +# for kind,items in module.kinds() do +<li><a href="#$(no_spaces(kind))">$(kind)</a></li> +# end +</ul> +# end + + +# if ldoc.no_summary and module and not ldoc.one then -- bang out the functions on the side +# for kind, items in module.kinds() do +<h2>$(kind)</h2> +<ul class="nowrap"> +# for item in items() do + <li><a href="#$(item.name)">$(display_name(item))</a></li> +# end +</ul> +# end +# end +# -------- contents of project ---------- +# local this_mod = module and module.name +# for kind, mods, type in ldoc.kinds() do +# if ldoc.allowed_in_contents(type,module) then +<h2>$(kind)</h2> +<ul class="$(kind=='Topics' and '' or 'nowrap')"> +# for mod in mods() do local name = display_name(mod) +# if mod.name == this_mod then + <li><strong>$(name)</strong></li> +# else + <li><a href="$(ldoc.ref_to_module(mod))">$(name)</a></li> +# end +# end +# end +</ul> +# end + +</div> + +<div id="content"> + +# if ldoc.body then -- verbatim HTML as contents; 'non-code' entries + $(ldoc.body) +# elseif module then -- module documentation +<h1>$(ldoc.module_typename(module)) <code>$(module.name)</code></h1> +<p>$(M(module.summary,module))</p> +<p>$(M(module.description,module))</p> +# if module.tags.include then + $(M(ldoc.include_file(module.tags.include))) +# end +# if module.see then +# local li,il = use_li(module.see) + <h3>See also:</h3> + <ul> +# for see in iter(module.see) do + $(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il) +# end -- for + </ul> +# end -- if see +# if module.usage then +# local li,il = use_li(module.usage) + <h3>Usage:</h3> + <ul> +# for usage in iter(module.usage) do + $(li)<pre class="example">$(ldoc.escape(usage))</pre>$(il) +# end -- for + </ul> +# end -- if usage +# if module.info then + <h3>Info:</h3> + <ul> +# for tag, value in module.info:iter() do + <li><strong>$(tag)</strong>: $(M(value,module))</li> +# end + </ul> +# end -- if module.info + + +# if not ldoc.no_summary then +# -- bang out the tables of item types for this module (e.g Functions, Tables, etc) +# for kind,items in module.kinds() do +<h2><a href="#$(no_spaces(kind))">$(kind)</a></h2> +<table class="function_list"> +# for item in items() do + <tr> + <td class="name" $(nowrap)><a href="#$(item.name)">$(display_name(item))</a></td> + <td class="summary">$(M(item.summary,item))</td> + </tr> +# end -- for items +</table> +#end -- for kinds + +<br/> +<br/> + +#end -- if not no_summary + +# --- currently works for both Functions and Tables. The params field either contains +# --- function parameters or table fields. +# local show_return = not ldoc.no_return_or_parms +# local show_parms = show_return +# for kind, items in module.kinds() do +# local kitem = module.kinds:get_item(kind) +# local has_description = kitem and ldoc.descript(kitem) ~= "" + <h2 class="section-header $(has_description and 'has-description')"><a name="$(no_spaces(kind))"></a>$(kind)</h2> + $(M(module.kinds:get_section_description(kind),nil)) +# if kitem then +# if has_description then + <div class="section-description"> + $(M(ldoc.descript(kitem),kitem)) + </div> +# end +# if kitem.usage then + <h3>Usage:</h3> + <pre class="example">$(ldoc.prettify(kitem.usage[1]))</pre> +# end +# end + <dl class="function"> +# for item in items() do + <dt> + <a name = "$(item.name)"></a> + <strong>$(display_name(item))</strong> +# if ldoc.prettify_files and ldoc.is_file_prettified[item.module.file.filename] then + <a style="float:right;" href="$(ldoc.source_ref(item))">line $(item.lineno)</a> +# end + </dt> + <dd> + $(M(ldoc.descript(item),item)) + +# if ldoc.custom_tags then +# for custom in iter(ldoc.custom_tags) do +# local tag = item.tags[custom[1]] +# if tag and not custom.hidden then +# local li,il = use_li(tag) + <h3>$(custom.title or custom[1]):</h3> + <ul> +# for value in iter(tag) do + $(li)$(custom.format and custom.format(value) or M(value))$(il) +# end -- for +# end -- if tag + </ul> +# end -- iter tags +# end + +# if show_parms and item.params and #item.params > 0 then +# local subnames = module.kinds:type_of(item).subnames +# if subnames then + <h3>$(subnames):</h3> +# end + <ul> +# for parm in iter(item.params) do +# local param,sublist = item:subparam(parm) +# if sublist then + <li><span class="parameter">$(sublist)</span>$(M(item.params.map[sublist],item)) + <ul> +# end +# for p in iter(param) do +# local name,tp,def = item:display_name_of(p), ldoc.typename(item:type_of_param(p)), item:default_of_param(p) + <li><span class="parameter">$(name)</span> +# if tp ~= '' then + <span class="types">$(tp)</span> +# end + $(M(item.params.map[p],item)) +# if def == true then + (<em>optional</em>) +# elseif def then + (<em>default</em> $(def)) +# end +# if item:readonly(p) then + <em>readonly</em> +# end + </li> +# end +# if sublist then + </li></ul> +# end +# end -- for + </ul> +# end -- if params + +# if show_return and item.retgroups then local groups = item.retgroups + <h3>Returns:</h3> +# for i,group in ldoc.ipairs(groups) do local li,il = use_li(group) + <ol> +# for r in group:iter() do local type, ctypes = item:return_type(r); local rt = ldoc.typename(type) + $(li) +# if rt ~= '' then + <span class="types">$(rt)</span> +# end + $(M(r.text,item))$(il) +# if ctypes then + <ul> +# for c in ctypes:iter() do + <li><span class="parameter">$(c.name)</span> + <span class="types">$(ldoc.typename(c.type))</span> + $(M(c.comment,item))</li> +# end + </ul> +# end -- if ctypes +# end -- for r + </ol> +# if i < #groups then + <h3>Or</h3> +# end +# end -- for group +# end -- if returns + +# if show_return and item.raise then + <h3>Raises:</h3> + $(M(item.raise,item)) +# end + +# if item.see then +# local li,il = use_li(item.see) + <h3>See also:</h3> + <ul> +# for see in iter(item.see) do + $(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il) +# end -- for + </ul> +# end -- if see + +# if item.usage then +# local li,il = use_li(item.usage) + <h3>Usage:</h3> + <ul> +# for usage in iter(item.usage) do + $(li)<pre class="example">$(ldoc.prettify(usage))</pre>$(il) +# end -- for + </ul> +# end -- if usage + +</dd> +# end -- for items +</dl> +# end -- for kinds + +# else -- if module; project-level contents + +# if ldoc.description then + <h2>$(M(ldoc.description,nil))</h2> +# end +# if ldoc.full_description then + <p>$(M(ldoc.full_description,nil))</p> +# end + +# for kind, mods in ldoc.kinds() do +<h2>$(kind)</h2> +# kind = kind:lower() +<table class="module_list"> +# for m in mods() do + <tr> + <td class="name" $(nowrap)><a href="$(no_spaces(kind))/$(m.name).html">$(m.name)</a></td> + <td class="summary">$(M(ldoc.strip_header(m.summary),m))</td> + </tr> +# end -- for modules +</table> +# end -- for kinds +# end -- if module + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc $(ldoc.version)</a></i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/Data/Libraries/Penlight/lua/pl/Date.lua b/Data/Libraries/Penlight/lua/pl/Date.lua new file mode 100644 index 0000000..039dbaf --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/Date.lua @@ -0,0 +1,675 @@ +--- Date and Date Format classes. +-- See @{05-dates.md|the Guide}. +-- +-- NOTE: the date module is deprecated! see +-- https://github.com/lunarmodules/Penlight/issues/285 +-- +-- Dependencies: `pl.class`, `pl.stringx`, `pl.utils` +-- @classmod pl.Date +-- @pragma nostrip + +local class = require 'pl.class' +local os_time, os_date = os.time, os.date +local stringx = require 'pl.stringx' +local utils = require 'pl.utils' +local assert_arg,assert_string = utils.assert_arg,utils.assert_string + + +utils.raise_deprecation { + source = "Penlight " .. utils._VERSION, + message = "the 'Date' module is deprecated, see https://github.com/lunarmodules/Penlight/issues/285", + version_removed = "2.0.0", + version_deprecated = "1.9.2", +} + + +local Date = class() +Date.Format = class() + +--- Date constructor. +-- @param t this can be either +-- +-- * `nil` or empty - use current date and time +-- * number - seconds since epoch (as returned by `os.time`). Resulting time is UTC +-- * `Date` - make a copy of this date +-- * table - table containing year, month, etc as for `os.time`. You may leave out year, month or day, +-- in which case current values will be used. +-- * year (will be followed by month, day etc) +-- +-- @param ... true if Universal Coordinated Time, or two to five numbers: month,day,hour,min,sec +-- @function Date +function Date:_init(t,...) + local time + local nargs = select('#',...) + if nargs > 2 then + local extra = {...} + local year = t + t = { + year = year, + month = extra[1], + day = extra[2], + hour = extra[3], + min = extra[4], + sec = extra[5] + } + end + if nargs == 1 then + self.utc = select(1,...) == true + end + if t == nil or t == 'utc' then + time = os_time() + self.utc = t == 'utc' + elseif type(t) == 'number' then + time = t + if self.utc == nil then self.utc = true end + elseif type(t) == 'table' then + if getmetatable(t) == Date then -- copy ctor + time = t.time + self.utc = t.utc + else + if not (t.year and t.month) then + local lt = os_date('*t') + if not t.year and not t.month and not t.day then + t.year = lt.year + t.month = lt.month + t.day = lt.day + else + t.year = t.year or lt.year + t.month = t.month or (t.day and lt.month or 1) + t.day = t.day or 1 + end + end + t.day = t.day or 1 + time = os_time(t) + end + else + error("bad type for Date constructor: "..type(t),2) + end + self:set(time) +end + +--- set the current time of this Date object. +-- @int t seconds since epoch +function Date:set(t) + self.time = t + if self.utc then + self.tab = os_date('!*t',t) + else + self.tab = os_date('*t',t) + end +end + +--- get the time zone offset from UTC. +-- @int ts seconds ahead of UTC +function Date.tzone (ts) + if ts == nil then + ts = os_time() + elseif type(ts) == "table" then + if getmetatable(ts) == Date then + ts = ts.time + else + ts = Date(ts).time + end + end + local utc = os_date('!*t',ts) + local lcl = os_date('*t',ts) + lcl.isdst = false + return os.difftime(os_time(lcl), os_time(utc)) +end + +--- convert this date to UTC. +function Date:toUTC () + local ndate = Date(self) + if not self.utc then + ndate.utc = true + ndate:set(ndate.time) + end + return ndate +end + +--- convert this UTC date to local. +function Date:toLocal () + local ndate = Date(self) + if self.utc then + ndate.utc = false + ndate:set(ndate.time) +--~ ndate:add { sec = Date.tzone(self) } + end + return ndate +end + +--- set the year. +-- @int y Four-digit year +-- @class function +-- @name Date:year + +--- set the month. +-- @int m month +-- @class function +-- @name Date:month + +--- set the day. +-- @int d day +-- @class function +-- @name Date:day + +--- set the hour. +-- @int h hour +-- @class function +-- @name Date:hour + +--- set the minutes. +-- @int min minutes +-- @class function +-- @name Date:min + +--- set the seconds. +-- @int sec seconds +-- @class function +-- @name Date:sec + +--- set the day of year. +-- @class function +-- @int yday day of year +-- @name Date:yday + +--- get the year. +-- @int y Four-digit year +-- @class function +-- @name Date:year + +--- get the month. +-- @class function +-- @name Date:month + +--- get the day. +-- @class function +-- @name Date:day + +--- get the hour. +-- @class function +-- @name Date:hour + +--- get the minutes. +-- @class function +-- @name Date:min + +--- get the seconds. +-- @class function +-- @name Date:sec + +--- get the day of year. +-- @class function +-- @name Date:yday + + +for _,c in ipairs{'year','month','day','hour','min','sec','yday'} do + Date[c] = function(self,val) + if val then + assert_arg(1,val,"number") + self.tab[c] = val + self:set(os_time(self.tab)) + return self + else + return self.tab[c] + end + end +end + +--- name of day of week. +-- @bool full abbreviated if true, full otherwise. +-- @ret string name +function Date:weekday_name(full) + return os_date(full and '%A' or '%a',self.time) +end + +--- name of month. +-- @int full abbreviated if true, full otherwise. +-- @ret string name +function Date:month_name(full) + return os_date(full and '%B' or '%b',self.time) +end + +--- is this day on a weekend?. +function Date:is_weekend() + return self.tab.wday == 1 or self.tab.wday == 7 +end + +--- add to a date object. +-- @param t a table containing one of the following keys and a value: +-- one of `year`,`month`,`day`,`hour`,`min`,`sec` +-- @return this date +function Date:add(t) + local old_dst = self.tab.isdst + local key,val = next(t) + self.tab[key] = self.tab[key] + val + self:set(os_time(self.tab)) + if old_dst ~= self.tab.isdst then + self.tab.hour = self.tab.hour - (old_dst and 1 or -1) + self:set(os_time(self.tab)) + end + return self +end + +--- last day of the month. +-- @return int day +function Date:last_day() + local d = 28 + local m = self.tab.month + while self.tab.month == m do + d = d + 1 + self:add{day=1} + end + self:add{day=-1} + return self +end + +--- difference between two Date objects. +-- @tparam Date other Date object +-- @treturn Date.Interval object +function Date:diff(other) + local dt = self.time - other.time + if dt < 0 then error("date difference is negative!",2) end + return Date.Interval(dt) +end + +--- long numerical ISO data format version of this date. +function Date:__tostring() + local fmt = '%Y-%m-%dT%H:%M:%S' + if self.utc then + fmt = "!"..fmt + end + local t = os_date(fmt,self.time) + if self.utc then + return t .. 'Z' + else + local offs = self:tzone() + if offs == 0 then + return t .. 'Z' + end + local sign = offs > 0 and '+' or '-' + local h = math.ceil(offs/3600) + local m = (offs % 3600)/60 + if m == 0 then + return t .. ('%s%02d'):format(sign,h) + else + return t .. ('%s%02d:%02d'):format(sign,h,m) + end + end +end + +--- equality between Date objects. +function Date:__eq(other) + return self.time == other.time +end + +--- ordering between Date objects. +function Date:__lt(other) + return self.time < other.time +end + +--- difference between Date objects. +-- @function Date:__sub +Date.__sub = Date.diff + +--- add a date and an interval. +-- @param other either a `Date.Interval` object or a table such as +-- passed to `Date:add` +function Date:__add(other) + local nd = Date(self) + if Date.Interval:class_of(other) then + other = {sec=other.time} + end + nd:add(other) + return nd +end + +Date.Interval = class(Date) + +---- Date.Interval constructor +-- @int t an interval in seconds +-- @function Date.Interval +function Date.Interval:_init(t) + self:set(t) +end + +function Date.Interval:set(t) + self.time = t + self.tab = os_date('!*t',self.time) +end + +local function ess(n) + if n > 1 then return 's ' + else return ' ' + end +end + +--- If it's an interval then the format is '2 hours 29 sec' etc. +function Date.Interval:__tostring() + local t, res = self.tab, '' + local y,m,d = t.year - 1970, t.month - 1, t.day - 1 + if y > 0 then res = res .. y .. ' year'..ess(y) end + if m > 0 then res = res .. m .. ' month'..ess(m) end + if d > 0 then res = res .. d .. ' day'..ess(d) end + if y == 0 and m == 0 then + local h = t.hour + if h > 0 then res = res .. h .. ' hour'..ess(h) end + if t.min > 0 then res = res .. t.min .. ' min ' end + if t.sec > 0 then res = res .. t.sec .. ' sec ' end + end + if res == '' then res = 'zero' end + return res +end + +------------ Date.Format class: parsing and renderinig dates ------------ + +-- short field names, explicit os.date names, and a mask for allowed field repeats +local formats = { + d = {'day',{true,true}}, + y = {'year',{false,true,false,true}}, + m = {'month',{true,true}}, + H = {'hour',{true,true}}, + M = {'min',{true,true}}, + S = {'sec',{true,true}}, +} + +--- Date.Format constructor. +-- @string fmt. A string where the following fields are significant: +-- +-- * d day (either d or dd) +-- * y year (either yy or yyy) +-- * m month (either m or mm) +-- * H hour (either H or HH) +-- * M minute (either M or MM) +-- * S second (either S or SS) +-- +-- Alternatively, if fmt is nil then this returns a flexible date parser +-- that tries various date/time schemes in turn: +-- +-- * [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601), like `2010-05-10 12:35:23Z` or `2008-10-03T14:30+02` +-- * times like 15:30 or 8.05pm (assumed to be today's date) +-- * dates like 28/10/02 (European order!) or 5 Feb 2012 +-- * month name like march or Mar (case-insensitive, first 3 letters); here the +-- day will be 1 and the year this current year +-- +-- A date in format 3 can be optionally followed by a time in format 2. +-- Please see test-date.lua in the tests folder for more examples. +-- @usage df = Date.Format("yyyy-mm-dd HH:MM:SS") +-- @class function +-- @name Date.Format +function Date.Format:_init(fmt) + if not fmt then + self.fmt = '%Y-%m-%d %H:%M:%S' + self.outf = self.fmt + self.plain = true + return + end + local append = table.insert + local D,PLUS,OPENP,CLOSEP = '\001','\002','\003','\004' + local vars,used = {},{} + local patt,outf = {},{} + local i = 1 + while i < #fmt do + local ch = fmt:sub(i,i) + local df = formats[ch] + if df then + if used[ch] then error("field appeared twice: "..ch,4) end + used[ch] = true + -- this field may be repeated + local _,inext = fmt:find(ch..'+',i+1) + local cnt = not _ and 1 or inext-i+1 + if not df[2][cnt] then error("wrong number of fields: "..ch,4) end + -- single chars mean 'accept more than one digit' + local p = cnt==1 and (D..PLUS) or (D):rep(cnt) + append(patt,OPENP..p..CLOSEP) + append(vars,ch) + if ch == 'y' then + append(outf,cnt==2 and '%y' or '%Y') + else + append(outf,'%'..ch) + end + i = i + cnt + else + append(patt,ch) + append(outf,ch) + i = i + 1 + end + end + -- escape any magic characters + fmt = utils.escape(table.concat(patt)) + -- fmt = table.concat(patt):gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1') + -- replace markers with their magic equivalents + fmt = fmt:gsub(D,'%%d'):gsub(PLUS,'+'):gsub(OPENP,'('):gsub(CLOSEP,')') + self.fmt = fmt + self.outf = table.concat(outf) + self.vars = vars +end + +local parse_date + +--- parse a string into a Date object. +-- @string str a date string +-- @return date object +function Date.Format:parse(str) + assert_string(1,str) + if self.plain then + return parse_date(str,self.us) + end + local res = {str:match(self.fmt)} + if #res==0 then return nil, 'cannot parse '..str end + local tab = {} + for i,v in ipairs(self.vars) do + local name = formats[v][1] -- e.g. 'y' becomes 'year' + tab[name] = tonumber(res[i]) + end + -- os.date() requires these fields; if not present, we assume + -- that the time set is for the current day. + if not (tab.year and tab.month and tab.day) then + local today = Date() + tab.year = tab.year or today:year() + tab.month = tab.month or today:month() + tab.day = tab.day or today:day() + end + local Y = tab.year + if Y < 100 then -- classic Y2K pivot + tab.year = Y + (Y < 35 and 2000 or 1999) + elseif not Y then + tab.year = 1970 + end + return Date(tab) +end + +--- convert a Date object into a string. +-- @param d a date object, or a time value as returned by @{os.time} +-- @return string +function Date.Format:tostring(d) + local tm + local fmt = self.outf + if type(d) == 'number' then + tm = d + else + tm = d.time + if d.utc then + fmt = '!'..fmt + end + end + return os_date(fmt,tm) +end + +--- force US order in dates like 9/11/2001 +function Date.Format:US_order(yesno) + self.us = yesno +end + +--local months = {jan=1,feb=2,mar=3,apr=4,may=5,jun=6,jul=7,aug=8,sep=9,oct=10,nov=11,dec=12} +local months +local parse_date_unsafe +local function create_months() + local ld, day1 = parse_date_unsafe '2000-12-31', {day=1} + months = {} + for i = 1,12 do + ld = ld:last_day() + ld:add(day1) + local mon = ld:month_name():lower() + months [mon] = i + end +end + +--[[ +Allowed patterns: +- [day] [monthname] [year] [time] +- [day]/[month][/year] [time] + +]] + +local function looks_like_a_month(w) + return w:match '^%a+,*$' ~= nil +end +local is_number = stringx.isdigit +local function tonum(s,l1,l2,kind) + kind = kind or '' + local n = tonumber(s) + if not n then error(("%snot a number: '%s'"):format(kind,s)) end + if n < l1 or n > l2 then + error(("%s out of range: %s is not between %d and %d"):format(kind,s,l1,l2)) + end + return n +end + +local function parse_iso_end(p,ns,sec) + -- may be fractional part of seconds + local _,nfrac,secfrac = p:find('^%.%d+',ns+1) + if secfrac then + sec = sec .. secfrac + p = p:sub(nfrac+1) + else + p = p:sub(ns+1) + end + -- ISO 8601 dates may end in Z (for UTC) or [+-][isotime] + -- (we're working with the date as lower case, hence 'z') + if p:match 'z$' then -- we're UTC! + return sec, {h=0,m=0} + end + p = p:gsub(':','') -- turn 00:30 to 0030 + local _,_,sign,offs = p:find('^([%+%-])(%d+)') + if not sign then return sec, nil end -- not UTC + + if #offs == 2 then offs = offs .. '00' end -- 01 to 0100 + local tz = { h = tonumber(offs:sub(1,2)), m = tonumber(offs:sub(3,4)) } + if sign == '-' then tz.h = -tz.h; tz.m = -tz.m end + return sec, tz +end + +function parse_date_unsafe (s,US) + s = s:gsub('T',' ') -- ISO 8601 + local parts = stringx.split(s:lower()) + local i,p = 1,parts[1] + local function nextp() i = i + 1; p = parts[i] end + local year,min,hour,sec,apm + local tz + local _,nxt,day, month = p:find '^(%d+)/(%d+)' + if day then + -- swop for US case + if US then + day, month = month, day + end + _,_,year = p:find('^/(%d+)',nxt+1) + nextp() + else -- ISO + year,month,day = p:match('^(%d+)%-(%d+)%-(%d+)') + if year then + nextp() + end + end + if p and not year and is_number(p) then -- has to be date + if #p < 4 then + day = p + nextp() + else -- unless it looks like a 24-hour time + year = true + end + end + if p and looks_like_a_month(p) then -- date followed by month + p = p:sub(1,3) + if not months then + create_months() + end + local mon = months[p] + if mon then + month = mon + else error("not a month: " .. p) end + nextp() + end + if p and not year and is_number(p) then + year = p + nextp() + end + + if p then -- time is hh:mm[:ss], hhmm[ss] or H.M[am|pm] + _,nxt,hour,min = p:find '^(%d+):(%d+)' + local ns + if nxt then -- are there seconds? + _,ns,sec = p:find ('^:(%d+)',nxt+1) + --if ns then + sec,tz = parse_iso_end(p,ns or nxt,sec) + --end + else -- might be h.m + _,ns,hour,min = p:find '^(%d+)%.(%d+)' + if ns then + apm = p:match '[ap]m$' + else -- or hhmm[ss] + local hourmin + _,nxt,hourmin = p:find ('^(%d+)') + if nxt then + hour = hourmin:sub(1,2) + min = hourmin:sub(3,4) + sec = hourmin:sub(5,6) + if #sec == 0 then sec = nil end + sec,tz = parse_iso_end(p,nxt,sec) + end + end + end + end + local today + if year == true then year = nil end + if not (year and month and day) then + today = Date() + end + day = day and tonum(day,1,31,'day') or (month and 1 or today:day()) + month = month and tonum(month,1,12,'month') or today:month() + year = year and tonumber(year) or today:year() + if year < 100 then -- two-digit year pivot around year < 2035 + year = year + (year < 35 and 2000 or 1900) + end + hour = hour and tonum(hour,0,apm and 12 or 24,'hour') or 12 + if apm == 'pm' then + hour = hour + 12 + end + min = min and tonum(min,0,59) or 0 + sec = sec and tonum(sec,0,60) or 0 --60 used to indicate leap second + local res = Date {year = year, month = month, day = day, hour = hour, min = min, sec = sec} + if tz then -- ISO 8601 UTC time + local corrected = false + if tz.h ~= 0 then res:add {hour = -tz.h}; corrected = true end + if tz.m ~= 0 then res:add {min = -tz.m}; corrected = true end + res.utc = true + -- we're in UTC, so let's go local... + if corrected then + res = res:toLocal() + end-- we're UTC! + end + return res +end + +function parse_date (s) + local ok, d = pcall(parse_date_unsafe,s) + if not ok then -- error + d = d:gsub('.-:%d+: ','') + return nil, d + else + return d + end +end + +return Date + diff --git a/Data/Libraries/Penlight/lua/pl/List.lua b/Data/Libraries/Penlight/lua/pl/List.lua new file mode 100644 index 0000000..b66c251 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/List.lua @@ -0,0 +1,566 @@ +--- Python-style list class. +-- +-- **Please Note**: methods that change the list will return the list. +-- This is to allow for method chaining, but please note that `ls = ls:sort()` +-- does not mean that a new copy of the list is made. In-place (mutable) methods +-- are marked as returning 'the list' in this documentation. +-- +-- See the Guide for further @{02-arrays.md.Python_style_Lists|discussion} +-- +-- See <a href="http://www.python.org/doc/current/tut/tut.html">http://www.python.org/doc/current/tut/tut.html</a>, section 5.1 +-- +-- **Note**: The comments before some of the functions are from the Python docs +-- and contain Python code. +-- +-- Written for Lua version Nick Trout 4.0; Redone for Lua 5.1, Steve Donovan. +-- +-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class` +-- @classmod pl.List +-- @pragma nostrip + +local tinsert,tremove,concat,tsort = table.insert,table.remove,table.concat,table.sort +local setmetatable, getmetatable,type,tostring,string = setmetatable,getmetatable,type,tostring,string +local tablex = require 'pl.tablex' +local filter,imap,imap2,reduce,transform,tremovevalues = tablex.filter,tablex.imap,tablex.imap2,tablex.reduce,tablex.transform,tablex.removevalues +local tsub = tablex.sub +local utils = require 'pl.utils' +local class = require 'pl.class' + +local array_tostring,split,assert_arg,function_arg = utils.array_tostring,utils.split,utils.assert_arg,utils.function_arg +local normalize_slice = tablex._normalize_slice + +-- metatable for our list and map objects has already been defined.. +local Multimap = utils.stdmt.MultiMap +local List = utils.stdmt.List + +local iter + +class(nil,nil,List) + +-- we want the result to be _covariant_, i.e. t must have type of obj if possible +local function makelist (t,obj) + local klass = List + if obj then + klass = getmetatable(obj) + end + return setmetatable(t,klass) +end + +local function simple_table(t) + return type(t) == 'table' and not getmetatable(t) and #t > 0 +end + +function List._create (src) + if simple_table(src) then return src end +end + +function List:_init (src) + if self == src then return end -- existing table used as self! + if src then + for v in iter(src) do + tinsert(self,v) + end + end +end + +--- Create a new list. Can optionally pass a table; +-- passing another instance of List will cause a copy to be created; +-- this will return a plain table with an appropriate metatable. +-- we pass anything which isn't a simple table to iterate() to work out +-- an appropriate iterator +-- @see List.iterate +-- @param[opt] t An optional list-like table +-- @return a new List +-- @usage ls = List(); ls = List {1,2,3,4} +-- @function List.new + +List.new = List + +--- Make a copy of an existing list. +-- The difference from a plain 'copy constructor' is that this returns +-- the actual List subtype. +function List:clone() + local ls = makelist({},self) + ls:extend(self) + return ls +end + +--- Add an item to the end of the list. +-- @param i An item +-- @return the list +function List:append(i) + tinsert(self,i) + return self +end + +List.push = tinsert + +--- Extend the list by appending all the items in the given list. +-- equivalent to 'a[len(a):] = L'. +-- @tparam List L Another List +-- @return the list +function List:extend(L) + assert_arg(1,L,'table') + for i = 1,#L do tinsert(self,L[i]) end + return self +end + +--- Insert an item at a given position. i is the index of the +-- element before which to insert. +-- @int i index of element before whichh to insert +-- @param x A data item +-- @return the list +function List:insert(i, x) + assert_arg(1,i,'number') + tinsert(self,i,x) + return self +end + +--- Insert an item at the begining of the list. +-- @param x a data item +-- @return the list +function List:put (x) + return self:insert(1,x) +end + +--- Remove an element given its index. +-- (equivalent of Python's del s[i]) +-- @int i the index +-- @return the list +function List:remove (i) + assert_arg(1,i,'number') + tremove(self,i) + return self +end + +--- Remove the first item from the list whose value is given. +-- (This is called 'remove' in Python; renamed to avoid confusion +-- with table.remove) +-- Return nil if there is no such item. +-- @param x A data value +-- @return the list +function List:remove_value(x) + for i=1,#self do + if self[i]==x then tremove(self,i) return self end + end + return self + end + +--- Remove the item at the given position in the list, and return it. +-- If no index is specified, a:pop() returns the last item in the list. +-- The item is also removed from the list. +-- @int[opt] i An index +-- @return the item +function List:pop(i) + if not i then i = #self end + assert_arg(1,i,'number') + return tremove(self,i) +end + +List.get = List.pop + +--- Return the index in the list of the first item whose value is given. +-- Return nil if there is no such item. +-- @function List:index +-- @param x A data value +-- @int[opt=1] idx where to start search +-- @return the index, or nil if not found. + +local tfind = tablex.find +List.index = tfind + +--- Does this list contain the value? +-- @param x A data value +-- @return true or false +function List:contains(x) + return tfind(self,x) and true or false +end + +--- Return the number of times value appears in the list. +-- @param x A data value +-- @return number of times x appears +function List:count(x) + local cnt=0 + for i=1,#self do + if self[i]==x then cnt=cnt+1 end + end + return cnt +end + +--- Sort the items of the list, in place. +-- @func[opt='<'] cmp an optional comparison function +-- @return the list +function List:sort(cmp) + if cmp then cmp = function_arg(1,cmp) end + tsort(self,cmp) + return self +end + +--- Return a sorted copy of this list. +-- @func[opt='<'] cmp an optional comparison function +-- @return a new list +function List:sorted(cmp) + return List(self):sort(cmp) +end + +--- Reverse the elements of the list, in place. +-- @return the list +function List:reverse() + local t = self + local n = #t + for i = 1,n/2 do + t[i],t[n] = t[n],t[i] + n = n - 1 + end + return self +end + +--- Return the minimum and the maximum value of the list. +-- @return minimum value +-- @return maximum value +function List:minmax() + local vmin,vmax = 1e70,-1e70 + for i = 1,#self do + local v = self[i] + if v < vmin then vmin = v end + if v > vmax then vmax = v end + end + return vmin,vmax +end + +--- Emulate list slicing. like 'list[first:last]' in Python. +-- If first or last are negative then they are relative to the end of the list +-- eg. slice(-2) gives last 2 entries in a list, and +-- slice(-4,-2) gives from -4th to -2nd +-- @param first An index +-- @param last An index +-- @return a new List +function List:slice(first,last) + return tsub(self,first,last) +end + +--- Empty the list. +-- @return the list +function List:clear() + for i=1,#self do tremove(self) end + return self +end + +local eps = 1.0e-10 + +--- Emulate Python's range(x) function. +-- Include it in List table for tidiness +-- @int start A number +-- @int[opt] finish A number greater than start; if absent, +-- then start is 1 and finish is start +-- @int[opt=1] incr an increment (may be less than 1) +-- @return a List from start .. finish +-- @usage List.range(0,3) == List{0,1,2,3} +-- @usage List.range(4) = List{1,2,3,4} +-- @usage List.range(5,1,-1) == List{5,4,3,2,1} +function List.range(start,finish,incr) + if not finish then + finish = start + start = 1 + end + if incr then + assert_arg(3,incr,'number') + if math.ceil(incr) ~= incr then finish = finish + eps end + else + incr = 1 + end + assert_arg(1,start,'number') + assert_arg(2,finish,'number') + local t = List() + for i=start,finish,incr do tinsert(t,i) end + return t +end + +--- list:len() is the same as #list. +function List:len() + return #self +end + +-- Extended operations -- + +--- Remove a subrange of elements. +-- equivalent to 'del s[i1:i2]' in Python. +-- @int i1 start of range +-- @int i2 end of range +-- @return the list +function List:chop(i1,i2) + return tremovevalues(self,i1,i2) +end + +--- Insert a sublist into a list +-- equivalent to 's[idx:idx] = list' in Python +-- @int idx index +-- @tparam List list list to insert +-- @return the list +-- @usage l = List{10,20}; l:splice(2,{21,22}); assert(l == List{10,21,22,20}) +function List:splice(idx,list) + assert_arg(1,idx,'number') + idx = idx - 1 + local i = 1 + for v in iter(list) do + tinsert(self,i+idx,v) + i = i + 1 + end + return self +end + +--- General slice assignment s[i1:i2] = seq. +-- @int i1 start index +-- @int i2 end index +-- @tparam List seq a list +-- @return the list +function List:slice_assign(i1,i2,seq) + assert_arg(1,i1,'number') + assert_arg(1,i2,'number') + i1,i2 = normalize_slice(self,i1,i2) + if i2 >= i1 then self:chop(i1,i2) end + self:splice(i1,seq) + return self +end + +--- Concatenation operator. +-- @within metamethods +-- @tparam List L another List +-- @return a new list consisting of the list with the elements of the new list appended +function List:__concat(L) + assert_arg(1,L,'table') + local ls = self:clone() + ls:extend(L) + return ls +end + +--- Equality operator ==. True iff all elements of two lists are equal. +-- @within metamethods +-- @tparam List L another List +-- @return true or false +function List:__eq(L) + if #self ~= #L then return false end + for i = 1,#self do + if self[i] ~= L[i] then return false end + end + return true +end + +--- Join the elements of a list using a delimiter. +-- This method uses tostring on all elements. +-- @string[opt=''] delim a delimiter string, can be empty. +-- @return a string +function List:join (delim) + delim = delim or '' + assert_arg(1,delim,'string') + return concat(array_tostring(self),delim) +end + +--- Join a list of strings. <br> +-- Uses `table.concat` directly. +-- @function List:concat +-- @string[opt=''] delim a delimiter +-- @return a string +List.concat = concat + +local function tostring_q(val) + local s = tostring(val) + if type(val) == 'string' then + s = '"'..s..'"' + end + return s +end + +--- How our list should be rendered as a string. Uses join(). +-- @within metamethods +-- @see List:join +function List:__tostring() + return '{'..self:join(',',tostring_q)..'}' +end + +--- Call the function on each element of the list. +-- @func fun a function or callable object +-- @param ... optional values to pass to function +function List:foreach (fun,...) + fun = function_arg(1,fun) + for i = 1,#self do + fun(self[i],...) + end +end + +local function lookup_fun (obj,name) + local f = obj[name] + if not f then error(type(obj).." does not have method "..name,3) end + return f +end + +--- Call the named method on each element of the list. +-- @string name the method name +-- @param ... optional values to pass to function +function List:foreachm (name,...) + for i = 1,#self do + local obj = self[i] + local f = lookup_fun(obj,name) + f(obj,...) + end +end + +--- Create a list of all elements which match a function. +-- @func fun a boolean function +-- @param[opt] arg optional argument to be passed as second argument of the predicate +-- @return a new filtered list. +function List:filter (fun,arg) + return makelist(filter(self,fun,arg),self) +end + +--- Split a string using a delimiter. +-- @string s the string +-- @string[opt] delim the delimiter (default spaces) +-- @return a List of strings +-- @see pl.utils.split +function List.split (s,delim) + assert_arg(1,s,'string') + return makelist(split(s,delim)) +end + +--- Apply a function to all elements. +-- Any extra arguments will be passed to the function. +-- @func fun a function of at least one argument +-- @param ... arbitrary extra arguments. +-- @return a new list: {f(x) for x in self} +-- @usage List{'one','two'}:map(string.upper) == {'ONE','TWO'} +-- @see pl.tablex.imap +function List:map (fun,...) + return makelist(imap(fun,self,...),self) +end + +--- Apply a function to all elements, in-place. +-- Any extra arguments are passed to the function. +-- @func fun A function that takes at least one argument +-- @param ... arbitrary extra arguments. +-- @return the list. +function List:transform (fun,...) + transform(fun,self,...) + return self +end + +--- Apply a function to elements of two lists. +-- Any extra arguments will be passed to the function +-- @func fun a function of at least two arguments +-- @tparam List ls another list +-- @param ... arbitrary extra arguments. +-- @return a new list: {f(x,y) for x in self, for x in arg1} +-- @see pl.tablex.imap2 +function List:map2 (fun,ls,...) + return makelist(imap2(fun,self,ls,...),self) +end + +--- apply a named method to all elements. +-- Any extra arguments will be passed to the method. +-- @string name name of method +-- @param ... extra arguments +-- @return a new list of the results +-- @see pl.seq.mapmethod +function List:mapm (name,...) + local res = {} + for i = 1,#self do + local val = self[i] + local fn = lookup_fun(val,name) + res[i] = fn(val,...) + end + return makelist(res,self) +end + +local function composite_call (method,f) + return function(self,...) + return self[method](self,f,...) + end +end + +function List.default_map_with(T) + return function(self,name) + local m + if T then + local f = lookup_fun(T,name) + m = composite_call('map',f) + else + m = composite_call('mapn',name) + end + getmetatable(self)[name] = m -- and cache.. + return m + end +end + +List.default_map = List.default_map_with + +--- 'reduce' a list using a binary function. +-- @func fun a function of two arguments +-- @return result of the function +-- @see pl.tablex.reduce +function List:reduce (fun) + return reduce(fun,self) +end + +--- Partition a list using a classifier function. +-- The function may return nil, but this will be converted to the string key '<nil>'. +-- @func fun a function of at least one argument +-- @param ... will also be passed to the function +-- @treturn MultiMap a table where the keys are the returned values, and the values are Lists +-- of values where the function returned that key. +-- @see pl.MultiMap +function List:partition (fun,...) + fun = function_arg(1,fun) + local res = {} + for i = 1,#self do + local val = self[i] + local klass = fun(val,...) + if klass == nil then klass = '<nil>' end + if not res[klass] then res[klass] = List() end + res[klass]:append(val) + end + return setmetatable(res,Multimap) +end + +--- return an iterator over all values. +function List:iter () + return iter(self) +end + +--- Create an iterator over a seqence. +-- This captures the Python concept of 'sequence'. +-- For tables, iterates over all values with integer indices. +-- @param seq a sequence; a string (over characters), a table, a file object (over lines) or an iterator function +-- @usage for x in iterate {1,10,22,55} do io.write(x,',') end ==> 1,10,22,55 +-- @usage for ch in iterate 'help' do do io.write(ch,' ') end ==> h e l p +function List.iterate(seq) + if type(seq) == 'string' then + local idx = 0 + local n = #seq + local sub = string.sub + return function () + idx = idx + 1 + if idx > n then return nil + else + return sub(seq,idx,idx) + end + end + elseif type(seq) == 'table' then + local idx = 0 + local n = #seq + return function() + idx = idx + 1 + if idx > n then return nil + else + return seq[idx] + end + end + elseif type(seq) == 'function' then + return seq + elseif type(seq) == 'userdata' and io.type(seq) == 'file' then + return seq:lines() + end +end +iter = List.iterate + +return List + diff --git a/Data/Libraries/Penlight/lua/pl/Map.lua b/Data/Libraries/Penlight/lua/pl/Map.lua new file mode 100644 index 0000000..572b22a --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/Map.lua @@ -0,0 +1,120 @@ +--- A Map class. +-- +-- > Map = require 'pl.Map' +-- > m = Map{one=1,two=2} +-- > m:update {three=3,four=4,two=20} +-- > = m == M{one=1,two=20,three=3,four=4} +-- true +-- +-- Dependencies: `pl.utils`, `pl.class`, `pl.tablex`, `pl.pretty` +-- @classmod pl.Map + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local stdmt = utils.stdmt +local deepcompare = tablex.deepcompare + +local pretty_write = require 'pl.pretty' . write +local Map = stdmt.Map +local Set = stdmt.Set + +local class = require 'pl.class' + +-- the Map class --------------------- +class(nil,nil,Map) + +function Map:_init (t) + local mt = getmetatable(t) + if mt == Set or mt == Map then + self:update(t) + else + return t -- otherwise assumed to be a map-like table + end +end + + +local function makelist(t) + return setmetatable(t, require('pl.List')) +end + +--- list of keys. +Map.keys = tablex.keys + +--- list of values. +Map.values = tablex.values + +--- return an iterator over all key-value pairs. +function Map:iter () + return pairs(self) +end + +--- return a List of all key-value pairs, sorted by the keys. +function Map:items() + local ls = makelist(tablex.pairmap (function (k,v) return makelist {k,v} end, self)) + ls:sort(function(t1,t2) return t1[1] < t2[1] end) + return ls +end + +--- set a value in the map if it doesn't exist yet. +-- @param key the key +-- @param default value to set +-- @return the value stored in the map (existing value, or the new value) +function Map:setdefault(key, default) + local val = self[key] + if val ~= nil then + return val + end + self:set(key,default) + return default +end + +--- size of map. +-- note: this is a relatively expensive operation! +-- @class function +-- @name Map:len +Map.len = tablex.size + +--- put a value into the map. +-- This will remove the key if the value is `nil` +-- @param key the key +-- @param val the value +function Map:set (key,val) + self[key] = val +end + +--- get a value from the map. +-- @param key the key +-- @return the value, or nil if not found. +function Map:get (key) + return rawget(self,key) +end + +local index_by = tablex.index_by + +--- get a list of values indexed by a list of keys. +-- @param keys a list-like table of keys +-- @return a new list +function Map:getvalues (keys) + return makelist(index_by(self,keys)) +end + +--- update the map using key/value pairs from another table. +-- @tab table +-- @function Map:update +Map.update = tablex.update + +--- equality between maps. +-- @within metamethods +-- @tparam Map m another map. +function Map:__eq (m) + -- note we explicitly ask deepcompare _not_ to use __eq! + return deepcompare(self,m,true) +end + +--- string representation of a map. +-- @within metamethods +function Map:__tostring () + return pretty_write(self,'') +end + +return Map diff --git a/Data/Libraries/Penlight/lua/pl/MultiMap.lua b/Data/Libraries/Penlight/lua/pl/MultiMap.lua new file mode 100644 index 0000000..0abe84d --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/MultiMap.lua @@ -0,0 +1,54 @@ +--- MultiMap, a Map which has multiple values per key. +-- +-- Dependencies: `pl.utils`, `pl.class`, `pl.List`, `pl.Map` +-- @classmod pl.MultiMap + +local utils = require 'pl.utils' +local class = require 'pl.class' +local List = require 'pl.List' +local Map = require 'pl.Map' + +-- MultiMap is a standard MT +local MultiMap = utils.stdmt.MultiMap + +class(Map,nil,MultiMap) +MultiMap._name = 'MultiMap' + +function MultiMap:_init (t) + if not t then return end + self:update(t) +end + +--- update a MultiMap using a table. +-- @param t either a Multimap or a map-like table. +-- @return the map +function MultiMap:update (t) + utils.assert_arg(1,t,'table') + if Map:class_of(t) then + for k,v in pairs(t) do + self[k] = List() + self[k]:append(v) + end + else + for k,v in pairs(t) do + self[k] = List(v) + end + end +end + +--- add a new value to a key. Setting a nil value removes the key. +-- @param key the key +-- @param val the value +-- @return the map +function MultiMap:set (key,val) + if val == nil then + self[key] = nil + else + if not self[key] then + self[key] = List() + end + self[key]:append(val) + end +end + +return MultiMap diff --git a/Data/Libraries/Penlight/lua/pl/OrderedMap.lua b/Data/Libraries/Penlight/lua/pl/OrderedMap.lua new file mode 100644 index 0000000..379c44f --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/OrderedMap.lua @@ -0,0 +1,167 @@ +--- OrderedMap, a map which preserves ordering. +-- +-- Derived from `pl.Map`. +-- +-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`, `pl.List`, `pl.Map` +-- @classmod pl.OrderedMap + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local List = require 'pl.List' +local index_by,tsort,concat = tablex.index_by,table.sort,table.concat + +local class = require 'pl.class' +local Map = require 'pl.Map' + +local OrderedMap = class(Map) +OrderedMap._name = 'OrderedMap' + +local rawset = rawset + +--- construct an OrderedMap. +-- Will throw an error if the argument is bad. +-- @param t optional initialization table, same as for @{OrderedMap:update} +function OrderedMap:_init (t) + rawset(self,'_keys',List()) + if t then + local map,err = self:update(t) + if not map then error(err,2) end + end +end + +local assert_arg,raise = utils.assert_arg,utils.raise + +--- update an OrderedMap using a table. +-- If the table is itself an OrderedMap, then its entries will be appended. +-- if it s a table of the form `{{key1=val1},{key2=val2},...}` these will be appended. +-- +-- Otherwise, it is assumed to be a map-like table, and order of extra entries is arbitrary. +-- @tab t a table. +-- @return the map, or nil in case of error +-- @return the error message +function OrderedMap:update (t) + assert_arg(1,t,'table') + if OrderedMap:class_of(t) then + for k,v in t:iter() do + self:set(k,v) + end + elseif #t > 0 then -- an array must contain {key=val} tables + if type(t[1]) == 'table' then + for _,pair in ipairs(t) do + local key,value = next(pair) + if not key then return raise 'empty pair initialization table' end + self:set(key,value) + end + else + return raise 'cannot use an array to initialize an OrderedMap' + end + else + for k,v in pairs(t) do + self:set(k,v) + end + end + return self +end + +--- set the key's value. This key will be appended at the end of the map. +-- +-- If the value is nil, then the key is removed. +-- @param key the key +-- @param val the value +-- @return the map +function OrderedMap:set (key,val) + if rawget(self, key) == nil and val ~= nil then -- new key + self._keys:append(key) -- we keep in order + rawset(self,key,val) -- don't want to provoke __newindex! + else -- existing key-value pair + if val == nil then + self._keys:remove_value(key) + rawset(self,key,nil) + else + self[key] = val + end + end + return self +end + +OrderedMap.__newindex = OrderedMap.set + +--- insert a key/value pair before a given position. +-- Note: if the map already contains the key, then this effectively +-- moves the item to the new position by first removing at the old position. +-- Has no effect if the key does not exist and val is nil +-- @int pos a position starting at 1 +-- @param key the key +-- @param val the value; if nil use the old value +function OrderedMap:insert (pos,key,val) + local oldval = self[key] + val = val or oldval + if oldval then + self._keys:remove_value(key) + end + if val then + self._keys:insert(pos,key) + rawset(self,key,val) + end + return self +end + +--- return the keys in order. +-- (Not a copy!) +-- @return List +function OrderedMap:keys () + return self._keys +end + +--- return the values in order. +-- this is relatively expensive. +-- @return List +function OrderedMap:values () + return List(index_by(self,self._keys)) +end + +--- sort the keys. +-- @func cmp a comparison function as for @{table.sort} +-- @return the map +function OrderedMap:sort (cmp) + tsort(self._keys,cmp) + return self +end + +--- iterate over key-value pairs in order. +function OrderedMap:iter () + local i = 0 + local keys = self._keys + local idx + return function() + i = i + 1 + if i > #keys then return nil end + idx = keys[i] + return idx,self[idx] + end +end + +--- iterate over an ordered map (5.2). +-- @within metamethods +-- @function OrderedMap:__pairs +OrderedMap.__pairs = OrderedMap.iter + +--- string representation of an ordered map. +-- @within metamethods +function OrderedMap:__tostring () + local res = {} + for i,v in ipairs(self._keys) do + local val = self[v] + local vs = tostring(val) + if type(val) ~= 'number' then + vs = '"'..vs..'"' + end + res[i] = tostring(v)..'='..vs + end + return '{'..concat(res,',')..'}' +end + +return OrderedMap + + + diff --git a/Data/Libraries/Penlight/lua/pl/Set.lua b/Data/Libraries/Penlight/lua/pl/Set.lua new file mode 100644 index 0000000..ce428f0 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/Set.lua @@ -0,0 +1,222 @@ +--- A Set class. +-- +-- > Set = require 'pl.Set' +-- > = Set{'one','two'} == Set{'two','one'} +-- true +-- > fruit = Set{'apple','banana','orange'} +-- > = fruit['banana'] +-- true +-- > = fruit['hazelnut'] +-- nil +-- > colours = Set{'red','orange','green','blue'} +-- > = fruit,colours +-- [apple,orange,banana] [blue,green,orange,red] +-- > = fruit+colours +-- [blue,green,apple,red,orange,banana] +-- [orange] +-- > more_fruits = fruit + 'apricot' +-- > = fruit*colours +-- > = more_fruits, fruit +-- [banana,apricot,apple,orange] [banana,apple,orange] +-- +-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`, `pl.Map`, (`pl.List` if __tostring is used) +-- @classmod pl.Set + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local array_tostring, concat = utils.array_tostring, table.concat +local merge,difference = tablex.merge,tablex.difference +local Map = require 'pl.Map' +local class = require 'pl.class' +local stdmt = utils.stdmt +local Set = stdmt.Set + +-- the Set class -------------------- +class(Map,nil,Set) + +-- note: Set has _no_ methods! +Set.__index = nil + +local function makeset (t) + return setmetatable(t,Set) +end + +--- create a set. <br> +-- @param t may be a Set, Map or list-like table. +-- @class function +-- @name Set +function Set:_init (t) + t = t or {} + local mt = getmetatable(t) + if mt == Set or mt == Map then + for k in pairs(t) do self[k] = true end + else + for _,v in ipairs(t) do self[v] = true end + end +end + +--- string representation of a set. +-- @within metamethods +function Set:__tostring () + return '['..concat(array_tostring(Set.values(self)),',')..']' +end + +--- get a list of the values in a set. +-- @param self a Set +-- @function Set.values +-- @return a list +Set.values = Map.keys + +--- map a function over the values of a set. +-- @param self a Set +-- @param fn a function +-- @param ... extra arguments to pass to the function. +-- @return a new set +function Set.map (self,fn,...) + fn = utils.function_arg(1,fn) + local res = {} + for k in pairs(self) do + res[fn(k,...)] = true + end + return makeset(res) +end + +--- union of two sets (also +). +-- @param self a Set +-- @param set another set +-- @return a new set +function Set.union (self,set) + return merge(self,set,true) +end + +--- modifies '+' operator to allow addition of non-Set elements +--- Preserves +/- semantics - does not modify first argument. +local function setadd(self,other) + local mt = getmetatable(other) + if mt == Set or mt == Map then + return Set.union(self,other) + else + local new = Set(self) + new[other] = true + return new + end +end + +--- union of sets. +-- @within metamethods +-- @function Set.__add + +Set.__add = setadd + +--- intersection of two sets (also *). +-- @param self a Set +-- @param set another set +-- @return a new set +-- @usage +-- > s = Set{10,20,30} +-- > t = Set{20,30,40} +-- > = t +-- [20,30,40] +-- > = Set.intersection(s,t) +-- [30,20] +-- > = s*t +-- [30,20] + +function Set.intersection (self,set) + return merge(self,set,false) +end + +--- intersection of sets. +-- @within metamethods +-- @function Set.__mul +Set.__mul = Set.intersection + +--- new set with elements in the set that are not in the other (also -). +-- @param self a Set +-- @param set another set +-- @return a new set +function Set.difference (self,set) + return difference(self,set,false) +end + +--- modifies "-" operator to remove non-Set values from set. +--- Preserves +/- semantics - does not modify first argument. +local function setminus (self,other) + local mt = getmetatable(other) + if mt == Set or mt == Map then + return Set.difference(self,other) + else + local new = Set(self) + new[other] = nil + return new + end +end + +--- difference of sets. +-- @within metamethods +-- @function Set.__sub +Set.__sub = setminus + +-- a new set with elements in _either_ the set _or_ other but not both (also ^). +-- @param self a Set +-- @param set another set +-- @return a new set +function Set.symmetric_difference (self,set) + return difference(self,set,true) +end + +--- symmetric difference of sets. +-- @within metamethods +-- @function Set.__pow +Set.__pow = Set.symmetric_difference + +--- is the first set a subset of the second (also <)?. +-- @param self a Set +-- @param set another set +-- @return true or false +function Set.issubset (self,set) + for k in pairs(self) do + if not set[k] then return false end + end + return true +end + +--- first set subset of second? +-- @within metamethods +-- @function Set.__lt +Set.__lt = Set.issubset + +--- is the set empty?. +-- @param self a Set +-- @return true or false +function Set.isempty (self) + return next(self) == nil +end + +--- are the sets disjoint? (no elements in common). +-- Uses naive definition, i.e. that intersection is empty +-- @param s1 a Set +-- @param s2 another set +-- @return true or false +function Set.isdisjoint (s1,s2) + return Set.isempty(Set.intersection(s1,s2)) +end + +--- size of this set (also # for 5.2). +-- @param s a Set +-- @return size +-- @function Set.len +Set.len = tablex.size + +--- cardinality of set (5.2). +-- @within metamethods +-- @function Set.__len +Set.__len = Set.len + +--- equality between sets. +-- @within metamethods +function Set.__eq (s1,s2) + return Set.issubset(s1,s2) and Set.issubset(s2,s1) +end + +return Set diff --git a/Data/Libraries/Penlight/lua/pl/app.lua b/Data/Libraries/Penlight/lua/pl/app.lua new file mode 100644 index 0000000..a06305d --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/app.lua @@ -0,0 +1,297 @@ +--- Application support functions. +-- See @{01-introduction.md.Application_Support|the Guide} +-- +-- Dependencies: `pl.utils`, `pl.path` +-- @module pl.app + +local io,package,require = _G.io, _G.package, _G.require +local utils = require 'pl.utils' +local path = require 'pl.path' + +local app = {} + +--- return the name of the current script running. +-- The name will be the name as passed on the command line +-- @return string filename +function app.script_name() + if _G.arg and _G.arg[0] then + return _G.arg[0] + end + return utils.raise("No script name found") +end + +--- prefixes the current script's path to the Lua module path. +-- Applies to both the source and the binary module paths. It makes it easy for +-- the main file of a multi-file program to access its modules in the same directory. +-- `base` allows these modules to be put in a specified subdirectory, to allow for +-- cleaner deployment and resolve potential conflicts between a script name and its +-- library directory. +-- +-- Note: the path is prefixed, so it is searched first when requiring modules. +-- @string base optional base directory (absolute, or relative path). +-- @treturn string the current script's path with a trailing slash +function app.require_here (base) + local p = path.dirname(app.script_name()) + if not path.isabs(p) then + p = path.join(path.currentdir(),p) + end + if p:sub(-1,-1) ~= path.sep then + p = p..path.sep + end + if base then + if path.is_windows then + base = base:gsub('/','\\') + end + if path.isabs(base) then + p = base .. path.sep + else + p = p..base..path.sep + end + end + local so_ext = path.is_windows and 'dll' or 'so' + local lsep = package.path:find '^;' and '' or ';' + local csep = package.cpath:find '^;' and '' or ';' + package.path = ('%s?.lua;%s?%sinit.lua%s%s'):format(p,p,path.sep,lsep,package.path) + package.cpath = ('%s?.%s%s%s'):format(p,so_ext,csep,package.cpath) + return p +end + +--- return a suitable path for files private to this application. +-- These will look like '~/.SNAME/file', with '~' as with expanduser and +-- SNAME is the name of the script without .lua extension. +-- If the directory does not exist, it will be created. +-- @string file a filename (w/out path) +-- @return a full pathname, or nil +-- @return cannot create directory error +-- @usage +-- -- when run from a script called 'testapp' (on Windows): +-- local app = require 'pl.app' +-- print(app.appfile 'test.txt') +-- -- C:\Documents and Settings\steve\.testapp\test.txt +function app.appfile(file) + local sfullname, err = app.script_name() + if not sfullname then return utils.raise(err) end + local sname = path.basename(sfullname) + local name = path.splitext(sname) + local dir = path.join(path.expanduser('~'),'.'..name) + if not path.isdir(dir) then + local ret = path.mkdir(dir) + if not ret then return utils.raise('cannot create '..dir) end + end + return path.join(dir,file) +end + +--- return string indicating operating system. +-- @return 'Windows','OSX' or whatever uname returns (e.g. 'Linux') +function app.platform() + if path.is_windows then + return 'Windows' + else + local f = io.popen('uname') + local res = f:read() + if res == 'Darwin' then res = 'OSX' end + f:close() + return res + end +end + +--- return the full command-line used to invoke this script. +-- It will not include the scriptname itself, see `app.script_name`. +-- @return command-line +-- @return name of Lua program used +-- @usage +-- -- execute: lua -lluacov -e 'print(_VERSION)' myscript.lua +-- +-- -- myscript.lua +-- print(require("pl.app").lua()) --> "lua -lluacov -e 'print(_VERSION)'", "lua" +function app.lua() + local args = _G.arg + if not args then + return utils.raise "not in a main program" + end + + local cmd = {} + local i = -1 + while true do + table.insert(cmd, 1, args[i]) + if not args[i-1] then + return utils.quote_arg(cmd), args[i] + end + i = i - 1 + end +end + +--- parse command-line arguments into flags and parameters. +-- Understands GNU-style command-line flags; short (`-f`) and long (`--flag`). +-- +-- These may be given a value with either '=' or ':' (`-k:2`,`--alpha=3.2`,`-n2`), +-- a number value can be given without a space. If the flag is marked +-- as having a value, then a space-separated value is also accepted (`-i hello`), +-- see the `flags_with_values` argument). +-- +-- Multiple short args can be combined like so: ( `-abcd`). +-- +-- When specifying the `flags_valid` parameter, its contents can also contain +-- aliasses, to convert short/long flags to the same output name. See the +-- example below. +-- +-- Note: if a flag is repeated, the last value wins. +-- @tparam {string} args an array of strings (default is the global `arg`) +-- @tab flags_with_values any flags that take values, either list or hash +-- table e.g. `{ out=true }` or `{ "out" }`. +-- @tab flags_valid (optional) flags that are valid, either list or hashtable. +-- If not given, everything +-- will be accepted(everything in `flags_with_values` will automatically be allowed) +-- @return a table of flags (flag=value pairs) +-- @return an array of parameters +-- @raise if args is nil, then the global `args` must be available! +-- @usage +-- -- Simple form: +-- local flags, params = app.parse_args(nil, +-- { "hello", "world" }, -- list of flags taking values +-- { "l", "a", "b"}) -- list of allowed flags (value ones will be added) +-- +-- -- More complex example using aliasses: +-- local valid = { +-- long = "l", -- if 'l' is specified, it is reported as 'long' +-- new = { "n", "old" }, -- here both 'n' and 'old' will go into 'new' +-- } +-- local values = { +-- "value", -- will automatically be added to the allowed set of flags +-- "new", -- will mark 'n' and 'old' as requiring a value as well +-- } +-- local flags, params = app.parse_args(nil, values, valid) +-- +-- -- command: myapp.lua -l --old:hello --value world param1 param2 +-- -- will yield: +-- flags = { +-- long = true, -- input from 'l' +-- new = "hello", -- input from 'old' +-- value = "world", -- allowed because it was in 'values', note: space separated! +-- } +-- params = { +-- [1] = "param1" +-- [2] = "param2" +-- } +function app.parse_args (args,flags_with_values, flags_valid) + if not args then + args = _G.arg + if not args then utils.raise "Not in a main program: 'arg' not found" end + end + + local with_values = {} + for k,v in pairs(flags_with_values or {}) do + if type(k) == "number" then + k = v + end + with_values[k] = true + end + + local valid + if not flags_valid then + -- if no allowed flags provided, we create a table that always returns + -- the keyname, no matter what you look up + valid = setmetatable({},{ __index = function(_, key) return key end }) + else + valid = {} + for k,aliasses in pairs(flags_valid) do + if type(k) == "number" then -- array/list entry + k = aliasses + end + if type(aliasses) == "string" then -- single alias + aliasses = { aliasses } + end + if type(aliasses) == "table" then -- list of aliasses + -- it's the alternate name, so add the proper mappings + for i, alias in ipairs(aliasses) do + valid[alias] = k + end + end + valid[k] = k + end + do + local new_with_values = {} -- needed to prevent "invalid key to 'next'" error + for k,v in pairs(with_values) do + if not valid[k] then + valid[k] = k -- add the with_value entry as a valid one + new_with_values[k] = true + else + new_with_values[valid[k]] = true --set, but by its alias + end + end + with_values = new_with_values + end + end + + -- now check that all flags with values are reported as such under all + -- of their aliasses + for k, main_alias in pairs(valid) do + if with_values[main_alias] then + with_values[k] = true + end + end + + local _args = {} + local flags = {} + local i = 1 + while i <= #args do + local a = args[i] + local v = a:match('^-(.+)') + local is_long + if not v then + -- we have a parameter + _args[#_args+1] = a + else + -- it's a flag + if v:find '^-' then + is_long = true + v = v:sub(2) + end + if with_values[v] then + if i == #args or args[i+1]:find '^-' then + return utils.raise ("no value for '"..v.."'") + end + flags[valid[v]] = args[i+1] + i = i + 1 + else + -- a value can also be indicated with = or : + local var,val = utils.splitv (v,'[=:]', false, 2) + var = var or v + val = val or true + if not is_long then + if #var > 1 then + if var:find '.%d+' then -- short flag, number value + val = var:sub(2) + var = var:sub(1,1) + else -- multiple short flags + for i = 1,#var do + local f = var:sub(i,i) + if not valid[f] then + return utils.raise("unknown flag '"..f.."'") + else + f = valid[f] + end + flags[f] = true + end + val = nil -- prevents use of var as a flag below + end + else -- single short flag (can have value, defaults to true) + val = val or true + end + end + if val then + if not valid[var] then + return utils.raise("unknown flag '"..var.."'") + else + var = valid[var] + end + flags[var] = val + end + end + end + i = i + 1 + end + return flags,_args +end + +return app diff --git a/Data/Libraries/Penlight/lua/pl/array2d.lua b/Data/Libraries/Penlight/lua/pl/array2d.lua new file mode 100644 index 0000000..2bc13b9 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/array2d.lua @@ -0,0 +1,579 @@ +--- Operations on two-dimensional arrays. +-- See @{02-arrays.md.Operations_on_two_dimensional_tables|The Guide} +-- +-- The size of the arrays is determined by using the length operator `#` hence +-- the module is not `nil` safe, and the usual precautions apply. +-- +-- Note: all functions taking `i1,j1,i2,j2` as arguments will normalize the +-- arguments using `default_range`. +-- +-- Dependencies: `pl.utils`, `pl.tablex`, `pl.types` +-- @module pl.array2d + +local tonumber,tostring,io,ipairs,string,table = + _G.tonumber,_G.tostring,_G.io,_G.ipairs,_G.string,_G.table +local setmetatable,getmetatable = setmetatable,getmetatable + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local types = require 'pl.types' +local imap,tmap,reduce,keys,tmap2,tset,index_by = tablex.imap,tablex.map,tablex.reduce,tablex.keys,tablex.map2,tablex.set,tablex.index_by +local remove = table.remove +local splitv,fprintf,assert_arg = utils.splitv,utils.fprintf,utils.assert_arg +local byte = string.byte +local stdout = io.stdout +local min = math.min + + +local array2d = {} + +local function obj (int,out) + local mt = getmetatable(int) + if mt then + setmetatable(out,mt) + end + return out +end + +local function makelist (res) + return setmetatable(res, require('pl.List')) +end + +--- return the row and column size. +-- Size is calculated using the Lua length operator #, so usual precautions +-- regarding `nil` values apply. +-- @array2d a a 2d array +-- @treturn int number of rows (`#a`) +-- @treturn int number of cols (`#a[1]`) +function array2d.size (a) + assert_arg(1,a,'table') + return #a,#a[1] +end + +do + local function index (t,k) + return t[k] + end + + --- extract a column from the 2D array. + -- @array2d a 2d array + -- @param j column index + -- @return 1d array + function array2d.column (a,j) + assert_arg(1,a,'table') + return makelist(imap(index,a,j)) + end +end +local column = array2d.column + +--- extract a row from the 2D array. +-- Added in line with `column`, for read-only purposes directly +-- accessing a[i] is more performant. +-- @array2d a 2d array +-- @param i row index +-- @return 1d array (copy of the row) +function array2d.row(a,i) + assert_arg(1,a,'table') + local row = a[i] + local r = {} + for n,v in ipairs(row) do + r[n] = v + end + return makelist(r) +end + +--- map a function over a 2D array +-- @func f a function of at least one argument +-- @array2d a 2d array +-- @param arg an optional extra argument to be passed to the function. +-- @return 2d array +function array2d.map (f,a,arg) + assert_arg(2,a,'table') + f = utils.function_arg(1,f) + return obj(a,imap(function(row) return imap(f,row,arg) end, a)) +end + +--- reduce the rows using a function. +-- @func f a binary function +-- @array2d a 2d array +-- @return 1d array +-- @see pl.tablex.reduce +function array2d.reduce_rows (f,a) + assert_arg(1,a,'table') + return tmap(function(row) return reduce(f,row) end, a) +end + +--- reduce the columns using a function. +-- @func f a binary function +-- @array2d a 2d array +-- @return 1d array +-- @see pl.tablex.reduce +function array2d.reduce_cols (f,a) + assert_arg(1,a,'table') + return tmap(function(c) return reduce(f,column(a,c)) end, keys(a[1])) +end + +--- reduce a 2D array into a scalar, using two operations. +-- @func opc operation to reduce the final result +-- @func opr operation to reduce the rows +-- @param a 2D array +function array2d.reduce2 (opc,opr,a) + assert_arg(3,a,'table') + local tmp = array2d.reduce_rows(opr,a) + return reduce(opc,tmp) +end + +--- map a function over two arrays. +-- They can be both or either 2D arrays +-- @func f function of at least two arguments +-- @int ad order of first array (`1` if `a` is a list/array, `2` if it is a 2d array) +-- @int bd order of second array (`1` if `b` is a list/array, `2` if it is a 2d array) +-- @tab a 1d or 2d array +-- @tab b 1d or 2d array +-- @param arg optional extra argument to pass to function +-- @return 2D array, unless both arrays are 1D +function array2d.map2 (f,ad,bd,a,b,arg) + assert_arg(1,a,'table') + assert_arg(2,b,'table') + f = utils.function_arg(1,f) + if ad == 1 and bd == 2 then + return imap(function(row) + return tmap2(f,a,row,arg) + end, b) + elseif ad == 2 and bd == 1 then + return imap(function(row) + return tmap2(f,row,b,arg) + end, a) + elseif ad == 1 and bd == 1 then + return tmap2(f,a,b) + elseif ad == 2 and bd == 2 then + return tmap2(function(rowa,rowb) + return tmap2(f,rowa,rowb,arg) + end, a,b) + end +end + +--- cartesian product of two 1d arrays. +-- @func f a function of 2 arguments +-- @array t1 a 1d table +-- @array t2 a 1d table +-- @return 2d table +-- @usage product('..',{1,2},{'a','b'}) == {{'1a','2a'},{'1b','2b'}} +function array2d.product (f,t1,t2) + f = utils.function_arg(1,f) + assert_arg(2,t1,'table') + assert_arg(3,t2,'table') + local res = {} + for i,v in ipairs(t2) do + res[i] = tmap(f,t1,v) + end + return res +end + +--- flatten a 2D array. +-- (this goes over columns first.) +-- @array2d t 2d table +-- @return a 1d table +-- @usage flatten {{1,2},{3,4},{5,6}} == {1,2,3,4,5,6} +function array2d.flatten (t) + local res = {} + local k = 1 + local rows, cols = array2d.size(t) + for r = 1, rows do + local row = t[r] + for c = 1, cols do + res[k] = row[c] + k = k + 1 + end + end + return makelist(res) +end + +--- reshape a 2D array. Reshape the aray by specifying a new nr of rows. +-- @array2d t 2d array +-- @int nrows new number of rows +-- @bool co use column-order (Fortran-style) (default false) +-- @return a new 2d array +function array2d.reshape (t,nrows,co) + local nr,nc = array2d.size(t) + local ncols = nr*nc / nrows + local res = {} + local ir,ic = 1,1 + for i = 1,nrows do + local row = {} + for j = 1,ncols do + row[j] = t[ir][ic] + if not co then + ic = ic + 1 + if ic > nc then + ir = ir + 1 + ic = 1 + end + else + ir = ir + 1 + if ir > nr then + ic = ic + 1 + ir = 1 + end + end + end + res[i] = row + end + return obj(t,res) +end + +--- transpose a 2D array. +-- @array2d t 2d array +-- @return a new 2d array +function array2d.transpose(t) + assert_arg(1,t,'table') + local _, c = array2d.size(t) + return array2d.reshape(t,c,true) +end + +--- swap two rows of an array. +-- @array2d t a 2d array +-- @int i1 a row index +-- @int i2 a row index +-- @return t (same, modified 2d array) +function array2d.swap_rows (t,i1,i2) + assert_arg(1,t,'table') + t[i1],t[i2] = t[i2],t[i1] + return t +end + +--- swap two columns of an array. +-- @array2d t a 2d array +-- @int j1 a column index +-- @int j2 a column index +-- @return t (same, modified 2d array) +function array2d.swap_cols (t,j1,j2) + assert_arg(1,t,'table') + for _, row in ipairs(t) do + row[j1],row[j2] = row[j2],row[j1] + end + return t +end + +--- extract the specified rows. +-- @array2d t 2d array +-- @tparam {int} ridx a table of row indices +-- @return a new 2d array with the extracted rows +function array2d.extract_rows (t,ridx) + return obj(t,index_by(t,ridx)) +end + +--- extract the specified columns. +-- @array2d t 2d array +-- @tparam {int} cidx a table of column indices +-- @return a new 2d array with the extracted colums +function array2d.extract_cols (t,cidx) + assert_arg(1,t,'table') + local res = {} + for i = 1,#t do + res[i] = index_by(t[i],cidx) + end + return obj(t,res) +end + +--- remove a row from an array. +-- @function array2d.remove_row +-- @array2d t a 2d array +-- @int i a row index +array2d.remove_row = remove + +--- remove a column from an array. +-- @array2d t a 2d array +-- @int j a column index +function array2d.remove_col (t,j) + assert_arg(1,t,'table') + for i = 1,#t do + remove(t[i],j) + end +end + +do + local function _parse (s) + local r, c = s:match 'R(%d+)C(%d+)' + if r then + r,c = tonumber(r),tonumber(c) + return r,c + end + c,r = s:match '(%a+)(%d+)' + if c then + local cv = 0 + for i = 1, #c do + cv = cv * 26 + byte(c:sub(i,i)) - byte 'A' + 1 + end + return tonumber(r), cv + end + error('bad cell specifier: '..s) + end + + --- parse a spreadsheet range. + -- The range can be specified either as 'A1:B2' or 'R1C1:R2C2'; + -- a special case is a single element (e.g 'A1' or 'R1C1') + -- @string s a range (case insensitive). + -- @treturn int start row + -- @treturn int start col + -- @treturn int end row + -- @treturn int end col + function array2d.parse_range (s) + assert_arg(1,s,'string') + s = s:upper() + if s:find ':' then + local start,finish = splitv(s,':') + local i1,j1 = _parse(start) + local i2,j2 = _parse(finish) + return i1,j1,i2,j2 + else -- single value + local i,j = _parse(s) + return i,j + end + end +end + +--- get a slice of a 2D array using spreadsheet range notation. @see parse_range +-- @array2d t a 2D array +-- @string rstr range expression +-- @return a slice +-- @see array2d.parse_range +-- @see array2d.slice +function array2d.range (t,rstr) + assert_arg(1,t,'table') + return array2d.slice(t,array2d.parse_range(rstr)) +end + +local default_range do + local function norm_value(v, max) + if not v then return v end + if v < 0 then + v = max + v + 1 + end + if v < 1 then v = 1 end + if v > max then v = max end + return v + end + + --- normalizes coordinates to valid positive entries and defaults. + -- Negative indices will be counted from the end, too low, or too high + -- will be limited by the array sizes. + -- @array2d t a 2D array + -- @int i1 start row (default 1) + -- @int j1 start col (default 1) + -- @int i2 end row (default N) + -- @int j2 end col (default M) + -- return i1, j1, i2, j2 + function array2d.default_range (t,i1,j1,i2,j2) + local nr, nc = array2d.size(t) + i1 = norm_value(i1 or 1, nr) + j1 = norm_value(j1 or 1, nc) + i2 = norm_value(i2 or nr, nr) + j2 = norm_value(j2 or nc, nc) + return i1,j1,i2,j2 + end + default_range = array2d.default_range +end + +--- get a slice of a 2D array. Note that if the specified range has +-- a 1D result, the rank of the result will be 1. +-- @array2d t a 2D array +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +-- @return an array, 2D in general but 1D in special cases. +function array2d.slice (t,i1,j1,i2,j2) + assert_arg(1,t,'table') + i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2) + local res = {} + for i = i1,i2 do + local val + local row = t[i] + if j1 == j2 then + val = row[j1] + else + val = {} + for j = j1,j2 do + val[#val+1] = row[j] + end + end + res[#res+1] = val + end + if i1 == i2 then res = res[1] end + return obj(t,res) +end + +--- set a specified range of an array to a value. +-- @array2d t a 2D array +-- @param value the value (may be a function, called as `val(i,j)`) +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +-- @see tablex.set +function array2d.set (t,value,i1,j1,i2,j2) + i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2) + local i = i1 + if types.is_callable(value) then + local old_f = value + value = function(j) + return old_f(i,j) + end + end + while i <= i2 do + tset(t[i],value,j1,j2) + i = i + 1 + end +end + +--- write a 2D array to a file. +-- @array2d t a 2D array +-- @param f a file object (default stdout) +-- @string fmt a format string (default is just to use tostring) +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +function array2d.write (t,f,fmt,i1,j1,i2,j2) + assert_arg(1,t,'table') + f = f or stdout + local rowop + if fmt then + rowop = function(row,j) fprintf(f,fmt,row[j]) end + else + rowop = function(row,j) f:write(tostring(row[j]),' ') end + end + local function newline() + f:write '\n' + end + array2d.forall(t,rowop,newline,i1,j1,i2,j2) +end + +--- perform an operation for all values in a 2D array. +-- @array2d t 2D array +-- @func row_op function to call on each value; `row_op(row,j)` +-- @func end_row_op function to call at end of each row; `end_row_op(i)` +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +function array2d.forall (t,row_op,end_row_op,i1,j1,i2,j2) + assert_arg(1,t,'table') + i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2) + for i = i1,i2 do + local row = t[i] + for j = j1,j2 do + row_op(row,j) + end + if end_row_op then end_row_op(i) end + end +end + +---- move a block from the destination to the source. +-- @array2d dest a 2D array +-- @int di start row in dest +-- @int dj start col in dest +-- @array2d src a 2D array +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +function array2d.move (dest,di,dj,src,i1,j1,i2,j2) + assert_arg(1,dest,'table') + assert_arg(4,src,'table') + i1,j1,i2,j2 = default_range(src,i1,j1,i2,j2) + local nr,nc = array2d.size(dest) + i2, j2 = min(nr,i2), min(nc,j2) + --i1, j1 = max(1,i1), max(1,j1) + dj = dj - 1 + for i = i1,i2 do + local drow, srow = dest[i+di-1], src[i] + for j = j1,j2 do + drow[j+dj] = srow[j] + end + end +end + +--- iterate over all elements in a 2D array, with optional indices. +-- @array2d a 2D array +-- @bool indices with indices (default false) +-- @int i1 start row (default 1) +-- @int j1 start col (default 1) +-- @int i2 end row (default N) +-- @int j2 end col (default M) +-- @return either `value` or `i,j,value` depending on the value of `indices` +function array2d.iter(a,indices,i1,j1,i2,j2) + assert_arg(1,a,'table') + i1,j1,i2,j2 = default_range(a,i1,j1,i2,j2) + local i,j = i1,j1-1 + local row = a[i] + return function() + j = j + 1 + if j > j2 then + j = j1 + i = i + 1 + row = a[i] + if i > i2 then + return nil + end + end + if indices then + return i,j,row[j] + else + return row[j] + end + end +end + +--- iterate over all columns. +-- @array2d a a 2D array +-- @return column, column-index +function array2d.columns(a) + assert_arg(1,a,'table') + local n = #a[1] + local i = 0 + return function() + i = i + 1 + if i > n then return nil end + return column(a,i), i + end +end + +--- iterate over all rows. +-- Returns a copy of the row, for read-only purrposes directly iterating +-- is more performant; `ipairs(a)` +-- @array2d a a 2D array +-- @return row, row-index +function array2d.rows(a) + assert_arg(1,a,'table') + local n = #a + local i = 0 + return function() + i = i + 1 + if i > n then return nil end + return array2d.row(a,i), i + end +end + +--- new array of specified dimensions +-- @int rows number of rows +-- @int cols number of cols +-- @param val initial value; if it's a function then use `val(i,j)` +-- @return new 2d array +function array2d.new(rows,cols,val) + local res = {} + local fun = types.is_callable(val) + for i = 1,rows do + local row = {} + if fun then + for j = 1,cols do row[j] = val(i,j) end + else + for j = 1,cols do row[j] = val end + end + res[i] = row + end + return res +end + +return array2d diff --git a/Data/Libraries/Penlight/lua/pl/class.lua b/Data/Libraries/Penlight/lua/pl/class.lua new file mode 100644 index 0000000..49246ee --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/class.lua @@ -0,0 +1,265 @@ +--- Provides a reuseable and convenient framework for creating classes in Lua. +-- Two possible notations: +-- +-- B = class(A) +-- class.B(A) +-- +-- The latter form creates a named class within the current environment. Note +-- that this implicitly brings in `pl.utils` as a dependency. +-- +-- See the Guide for further @{01-introduction.md.Simplifying_Object_Oriented_Programming_in_Lua|discussion} +-- @module pl.class + +local error, getmetatable, io, pairs, rawget, rawset, setmetatable, tostring, type = + _G.error, _G.getmetatable, _G.io, _G.pairs, _G.rawget, _G.rawset, _G.setmetatable, _G.tostring, _G.type +local compat + +-- this trickery is necessary to prevent the inheritance of 'super' and +-- the resulting recursive call problems. +local function call_ctor (c,obj,...) + local init = rawget(c,'_init') + local parent_with_init = rawget(c,'_parent_with_init') + + if parent_with_init then + if not init then -- inheriting an init + init = rawget(parent_with_init, '_init') + parent_with_init = rawget(parent_with_init, '_parent_with_init') + end + if parent_with_init then -- super() points to one above whereever _init came from + rawset(obj,'super',function(obj,...) + call_ctor(parent_with_init,obj,...) + end) + end + else + -- Without this, calling super() where none exists will sometimes loop and stack overflow + rawset(obj,'super',nil) + end + + local res = init(obj,...) + if parent_with_init then -- If this execution of call_ctor set a super, unset it + rawset(obj,'super',nil) + end + + return res +end + +--- initializes an __instance__ upon creation. +-- @function class:_init +-- @param ... parameters passed to the constructor +-- @usage local Cat = class() +-- function Cat:_init(name) +-- --self:super(name) -- call the ancestor initializer if needed +-- self.name = name +-- end +-- +-- local pussycat = Cat("pussycat") +-- print(pussycat.name) --> pussycat + +--- checks whether an __instance__ is derived from some class. +-- Works the other way around as `class_of`. It has two ways of using; +-- 1) call with a class to check against, 2) call without params. +-- @function instance:is_a +-- @param some_class class to check against, or `nil` to return the class +-- @return `true` if `instance` is derived from `some_class`, or if `some_class == nil` then +-- it returns the class table of the instance +-- @usage local pussycat = Lion() -- assuming Lion derives from Cat +-- if pussycat:is_a(Cat) then +-- -- it's true, it is a Lion, but also a Cat +-- end +-- +-- if pussycat:is_a() == Lion then +-- -- It's true +-- end +local function is_a(self,klass) + if klass == nil then + -- no class provided, so return the class this instance is derived from + return getmetatable(self) + end + local m = getmetatable(self) + if not m then return false end --*can't be an object! + while m do + if m == klass then return true end + m = rawget(m,'_base') + end + return false +end + +--- checks whether an __instance__ is derived from some class. +-- Works the other way around as `is_a`. +-- @function some_class:class_of +-- @param some_instance instance to check against +-- @return `true` if `some_instance` is derived from `some_class` +-- @usage local pussycat = Lion() -- assuming Lion derives from Cat +-- if Cat:class_of(pussycat) then +-- -- it's true +-- end +local function class_of(klass,obj) + if type(klass) ~= 'table' or not rawget(klass,'is_a') then return false end + return klass.is_a(obj,klass) +end + +--- cast an object to another class. +-- It is not clever (or safe!) so use carefully. +-- @param some_instance the object to be changed +-- @function some_class:cast +local function cast (klass, obj) + return setmetatable(obj,klass) +end + + +local function _class_tostring (obj) + local mt = obj._class + local name = rawget(mt,'_name') + setmetatable(obj,nil) + local str = tostring(obj) + setmetatable(obj,mt) + if name then str = name ..str:gsub('table','') end + return str +end + +local function tupdate(td,ts,dont_override) + for k,v in pairs(ts) do + if not dont_override or td[k] == nil then + td[k] = v + end + end +end + +local function _class(base,c_arg,c) + -- the class `c` will be the metatable for all its objects, + -- and they will look up their methods in it. + local mt = {} -- a metatable for the class to support __call and _handler + -- can define class by passing it a plain table of methods + local plain = type(base) == 'table' and not getmetatable(base) + if plain then + c = base + base = c._base + else + c = c or {} + end + + if type(base) == 'table' then + -- our new class is a shallow copy of the base class! + -- but be careful not to wipe out any methods we have been given at this point! + tupdate(c,base,plain) + c._base = base + -- inherit the 'not found' handler, if present + if rawget(c,'_handler') then mt.__index = c._handler end + elseif base ~= nil then + error("must derive from a table type",3) + end + + c.__index = c + setmetatable(c,mt) + if not plain then + if base and rawget(base,'_init') then c._parent_with_init = base end -- For super and inherited init + c._init = nil + end + + if base and rawget(base,'_class_init') then + base._class_init(c,c_arg) + end + + -- expose a ctor which can be called by <classname>(<args>) + mt.__call = function(class_tbl,...) + local obj + if rawget(c,'_create') then obj = c._create(...) end + if not obj then obj = {} end + setmetatable(obj,c) + + if rawget(c,'_init') or rawget(c,'_parent_with_init') then -- constructor exists + local res = call_ctor(c,obj,...) + if res then -- _if_ a ctor returns a value, it becomes the object... + obj = res + setmetatable(obj,c) + end + end + + if base and rawget(base,'_post_init') then + base._post_init(obj) + end + + return obj + end + -- Call Class.catch to set a handler for methods/properties not found in the class! + c.catch = function(self, handler) + if type(self) == "function" then + -- called using . instead of : + handler = self + end + c._handler = handler + mt.__index = handler + end + c.is_a = is_a + c.class_of = class_of + c.cast = cast + c._class = c + + if not rawget(c,'__tostring') then + c.__tostring = _class_tostring + end + + return c +end + +--- create a new class, derived from a given base class. +-- Supporting two class creation syntaxes: +-- either `Name = class(base)` or `class.Name(base)`. +-- The first form returns the class directly and does not set its `_name`. +-- The second form creates a variable `Name` in the current environment set +-- to the class, and also sets `_name`. +-- @function class +-- @param base optional base class +-- @param c_arg optional parameter to class constructor +-- @param c optional table to be used as class +local class +class = setmetatable({},{ + __call = function(fun,...) + return _class(...) + end, + __index = function(tbl,key) + if key == 'class' then + io.stderr:write('require("pl.class").class is deprecated. Use require("pl.class")\n') + return class + end + compat = compat or require 'pl.compat' + local env = compat.getfenv(2) + return function(...) + local c = _class(...) + c._name = key + rawset(env,key,c) + return c + end + end +}) + +class.properties = class() + +function class.properties._class_init(klass) + klass.__index = function(t,key) + -- normal class lookup! + local v = klass[key] + if v then return v end + -- is it a getter? + v = rawget(klass,'get_'..key) + if v then + return v(t) + end + -- is it a field? + return rawget(t,'_'..key) + end + klass.__newindex = function (t,key,value) + -- if there's a setter, use that, otherwise directly set table + local p = 'set_'..key + local setter = klass[p] + if setter then + setter(t,value) + else + rawset(t,key,value) + end + end +end + + +return class + diff --git a/Data/Libraries/Penlight/lua/pl/compat.lua b/Data/Libraries/Penlight/lua/pl/compat.lua new file mode 100644 index 0000000..1707ef6 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/compat.lua @@ -0,0 +1,250 @@ +---------------- +--- Lua 5.1/5.2/5.3 compatibility. +-- Injects `table.pack`, `table.unpack`, and `package.searchpath` in the global +-- environment, to make sure they are available for Lua 5.1 and LuaJIT. +-- +-- All other functions are exported as usual in the returned module table. +-- +-- NOTE: everything in this module is also available in `pl.utils`. +-- @module pl.compat +local compat = {} + +--- boolean flag this is Lua 5.1 (or LuaJIT). +-- @field lua51 +compat.lua51 = _VERSION == 'Lua 5.1' + +--- boolean flag this is LuaJIT. +-- @field jit +compat.jit = (tostring(assert):match('builtin') ~= nil) + +--- boolean flag this is LuaJIT with 5.2 compatibility compiled in. +-- @field jit52 +if compat.jit then + -- 'goto' is a keyword when 52 compatibility is enabled in LuaJit + compat.jit52 = not loadstring("local goto = 1") +end + +--- the directory separator character for the current platform. +-- @field dir_separator +compat.dir_separator = _G.package.config:sub(1,1) + +--- boolean flag this is a Windows platform. +-- @field is_windows +compat.is_windows = compat.dir_separator == '\\' + +--- execute a shell command, in a compatible and platform independent way. +-- This is a compatibility function that returns the same for Lua 5.1 and +-- Lua 5.2+. +-- +-- NOTE: Windows systems can use signed 32bit integer exitcodes. Posix systems +-- only use exitcodes 0-255, anything else is undefined. +-- +-- NOTE2: In Lua 5.2 and 5.3 a Windows exitcode of -1 would not properly be +-- returned, this function will return it properly for all versions. +-- @param cmd a shell command +-- @return true if successful +-- @return actual return code +function compat.execute(cmd) + local res1,res2,res3 = os.execute(cmd) + if res2 == "No error" and res3 == 0 and compat.is_windows then + -- os.execute bug in Lua 5.2/5.3 not reporting -1 properly on Windows + -- this was fixed in 5.4 + res3 = -1 + end + if compat.lua51 and not compat.jit52 then + if compat.is_windows then + return res1==0,res1 + else + res1 = res1 > 255 and res1 / 256 or res1 + return res1==0,res1 + end + else + if compat.is_windows then + return res3==0,res3 + else + return not not res1,res3 + end + end +end + +---------------- +-- Load Lua code as a text or binary chunk (in a Lua 5.2 compatible way). +-- @param ld code string or loader +-- @param[opt] source name of chunk for errors +-- @param[opt] mode 'b', 't' or 'bt' +-- @param[opt] env environment to load the chunk in +-- @function compat.load + +--------------- +-- Get environment of a function (in a Lua 5.1 compatible way). +-- Not 100% compatible, so with Lua 5.2 it may return nil for a function with no +-- global references! +-- Based on code by [Sergey Rozhenko](http://lua-users.org/lists/lua-l/2010-06/msg00313.html) +-- @param f a function or a call stack reference +-- @function compat.getfenv + +--------------- +-- Set environment of a function (in a Lua 5.1 compatible way). +-- @param f a function or a call stack reference +-- @param env a table that becomes the new environment of `f` +-- @function compat.setfenv + +if compat.lua51 then -- define Lua 5.2 style load() + if not compat.jit then -- but LuaJIT's load _is_ compatible + local lua51_load = load + function compat.load(str,src,mode,env) + local chunk,err + if type(str) == 'string' then + if str:byte(1) == 27 and not (mode or 'bt'):find 'b' then + return nil,"attempt to load a binary chunk" + end + chunk,err = loadstring(str,src) + else + chunk,err = lua51_load(str,src) + end + if chunk and env then setfenv(chunk,env) end + return chunk,err + end + else + compat.load = load + end + compat.setfenv, compat.getfenv = setfenv, getfenv +else + compat.load = load + -- setfenv/getfenv replacements for Lua 5.2 + -- by Sergey Rozhenko + -- http://lua-users.org/lists/lua-l/2010-06/msg00313.html + -- Roberto Ierusalimschy notes that it is possible for getfenv to return nil + -- in the case of a function with no globals: + -- http://lua-users.org/lists/lua-l/2010-06/msg00315.html + function compat.setfenv(f, t) + f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) + local name + local up = 0 + repeat + up = up + 1 + name = debug.getupvalue(f, up) + until name == '_ENV' or name == nil + if name then + debug.upvaluejoin(f, up, function() return name end, 1) -- use unique upvalue + debug.setupvalue(f, up, t) + end + if f ~= 0 then return f end + end + + function compat.getfenv(f) + local f = f or 0 + f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func) + local name, val + local up = 0 + repeat + up = up + 1 + name, val = debug.getupvalue(f, up) + until name == '_ENV' or name == nil + return val + end +end + + +--- Global exported functions (for Lua 5.1 & LuaJIT) +-- @section lua52 + +--- pack an argument list into a table. +-- @param ... any arguments +-- @return a table with field n set to the length +-- @function table.pack +if not table.pack then + function table.pack (...) -- luacheck: ignore + return {n=select('#',...); ...} + end +end + +--- unpack a table and return the elements. +-- +-- NOTE: this version does NOT honor the n field, and hence it is not nil-safe. +-- See `utils.unpack` for a version that is nil-safe. +-- @param t table to unpack +-- @param[opt] i index from which to start unpacking, defaults to 1 +-- @param[opt] t index of the last element to unpack, defaults to #t +-- @return multiple return values from the table +-- @function table.unpack +-- @see utils.unpack +if not table.unpack then + table.unpack = unpack -- luacheck: ignore +end + +--- return the full path where a file name would be matched. +-- This function was introduced in Lua 5.2, so this compatibility version +-- will be injected in Lua 5.1 engines. +-- @string name file name, possibly dotted +-- @string path a path-template in the same form as package.path or package.cpath +-- @string[opt] sep template separate character to be replaced by path separator. Default: "." +-- @string[opt] rep the path separator to use, defaults to system separator. Default; "/" on Unixes, "\" on Windows. +-- @see path.package_path +-- @function package.searchpath +-- @return on success: path of the file +-- @return on failure: nil, error string listing paths tried +if not package.searchpath then + function package.searchpath (name,path,sep,rep) -- luacheck: ignore + if type(name) ~= "string" then + error(("bad argument #1 to 'searchpath' (string expected, got %s)"):format(type(path)), 2) + end + if type(path) ~= "string" then + error(("bad argument #2 to 'searchpath' (string expected, got %s)"):format(type(path)), 2) + end + if sep ~= nil and type(sep) ~= "string" then + error(("bad argument #3 to 'searchpath' (string expected, got %s)"):format(type(path)), 2) + end + if rep ~= nil and type(rep) ~= "string" then + error(("bad argument #4 to 'searchpath' (string expected, got %s)"):format(type(path)), 2) + end + sep = sep or "." + rep = rep or compat.dir_separator + do + local s, e = name:find(sep, nil, true) + while s do + name = name:sub(1, s-1) .. rep .. name:sub(e+1, -1) + s, e = name:find(sep, s + #rep + 1, true) + end + end + local tried = {} + for m in path:gmatch('[^;]+') do + local nm = m:gsub('?', name) + tried[#tried+1] = nm + local f = io.open(nm,'r') + if f then f:close(); return nm end + end + return nil, "\tno file '" .. table.concat(tried, "'\n\tno file '") .. "'" + end +end + +--- Global exported functions (for Lua < 5.4) +-- @section lua54 + +--- raise a warning message. +-- This functions mimics the `warn` function added in Lua 5.4. +-- @function warn +-- @param ... any arguments +if not warn then -- luacheck: ignore + local enabled = false + function warn(arg1, ...) -- luacheck: ignore + if type(arg1) == "string" and arg1:sub(1, 1) == "@" then + -- control message + if arg1 == "@on" then + enabled = true + return + end + if arg1 == "@off" then + enabled = false + return + end + return -- ignore unknown control messages + end + if enabled then + io.stderr:write("Lua warning: ", arg1, ...) + io.stderr:write("\n") + end + end +end + +return compat diff --git a/Data/Libraries/Penlight/lua/pl/comprehension.lua b/Data/Libraries/Penlight/lua/pl/comprehension.lua new file mode 100644 index 0000000..39be7c5 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/comprehension.lua @@ -0,0 +1,285 @@ +--- List comprehensions implemented in Lua. +-- +-- See the [wiki page](http://lua-users.org/wiki/ListComprehensions) +-- +-- local C= require 'pl.comprehension' . new() +-- +-- C ('x for x=1,10') () +-- ==> {1,2,3,4,5,6,7,8,9,10} +-- C 'x^2 for x=1,4' () +-- ==> {1,4,9,16} +-- C '{x,x^2} for x=1,4' () +-- ==> {{1,1},{2,4},{3,9},{4,16}} +-- C '2*x for x' {1,2,3} +-- ==> {2,4,6} +-- dbl = C '2*x for x' +-- dbl {10,20,30} +-- ==> {20,40,60} +-- C 'x for x if x % 2 == 0' {1,2,3,4,5} +-- ==> {2,4} +-- C '{x,y} for x = 1,2 for y = 1,2' () +-- ==> {{1,1},{1,2},{2,1},{2,2}} +-- C '{x,y} for x for y' ({1,2},{10,20}) +-- ==> {{1,10},{1,20},{2,10},{2,20}} +-- assert(C 'sum(x^2 for x)' {2,3,4} == 2^2+3^2+4^2) +-- +-- (c) 2008 David Manura. Licensed under the same terms as Lua (MIT license). +-- +-- Dependencies: `pl.utils`, `pl.luabalanced` +-- +-- See @{07-functional.md.List_Comprehensions|the Guide} +-- @module pl.comprehension + +local utils = require 'pl.utils' + +local status,lb = pcall(require, "pl.luabalanced") +if not status then + lb = require 'luabalanced' +end + +local math_max = math.max +local table_concat = table.concat + +-- fold operations +-- http://en.wikipedia.org/wiki/Fold_(higher-order_function) +local ops = { + list = {init=' {} ', accum=' __result[#__result+1] = (%s) '}, + table = {init=' {} ', accum=' local __k, __v = %s __result[__k] = __v '}, + sum = {init=' 0 ', accum=' __result = __result + (%s) '}, + min = {init=' nil ', accum=' local __tmp = %s ' .. + ' if __result then if __tmp < __result then ' .. + '__result = __tmp end else __result = __tmp end '}, + max = {init=' nil ', accum=' local __tmp = %s ' .. + ' if __result then if __tmp > __result then ' .. + '__result = __tmp end else __result = __tmp end '}, +} + + +-- Parses comprehension string expr. +-- Returns output expression list <out> string, array of for types +-- ('=', 'in' or nil) <fortypes>, array of input variable name +-- strings <invarlists>, array of input variable value strings +-- <invallists>, array of predicate expression strings <preds>, +-- operation name string <opname>, and number of placeholder +-- parameters <max_param>. +-- +-- The is equivalent to the mathematical set-builder notation: +-- +-- <opname> { <out> | <invarlist> in <invallist> , <preds> } +-- +-- @usage "x^2 for x" -- array values +-- @usage "x^2 for x=1,10,2" -- numeric for +-- @usage "k^v for k,v in pairs(_1)" -- iterator for +-- @usage "(x+y)^2 for x for y if x > y" -- nested +-- +local function parse_comprehension(expr) + local pos = 1 + + -- extract opname (if exists) + local opname + local tok, post = expr:match('^%s*([%a_][%w_]*)%s*%(()', pos) + local pose = #expr + 1 + if tok then + local tok2, posb = lb.match_bracketed(expr, post-1) + assert(tok2, 'syntax error') + if expr:match('^%s*$', posb) then + opname = tok + pose = posb - 1 + pos = post + end + end + opname = opname or "list" + + -- extract out expression list + local out; out, pos = lb.match_explist(expr, pos) + assert(out, "syntax error: missing expression list") + out = table_concat(out, ', ') + + -- extract "for" clauses + local fortypes = {} + local invarlists = {} + local invallists = {} + while 1 do + local post = expr:match('^%s*for%s+()', pos) + if not post then break end + pos = post + + -- extract input vars + local iv; iv, pos = lb.match_namelist(expr, pos) + assert(#iv > 0, 'syntax error: zero variables') + for _,ident in ipairs(iv) do + assert(not ident:match'^__', + "identifier " .. ident .. " may not contain __ prefix") + end + invarlists[#invarlists+1] = iv + + -- extract '=' or 'in' (optional) + local fortype, post = expr:match('^(=)%s*()', pos) + if not fortype then fortype, post = expr:match('^(in)%s+()', pos) end + if fortype then + pos = post + -- extract input value range + local il; il, pos = lb.match_explist(expr, pos) + assert(#il > 0, 'syntax error: zero expressions') + assert(fortype ~= '=' or #il == 2 or #il == 3, + 'syntax error: numeric for requires 2 or three expressions') + fortypes[#invarlists] = fortype + invallists[#invarlists] = il + else + fortypes[#invarlists] = false + invallists[#invarlists] = false + end + end + assert(#invarlists > 0, 'syntax error: missing "for" clause') + + -- extract "if" clauses + local preds = {} + while 1 do + local post = expr:match('^%s*if%s+()', pos) + if not post then break end + pos = post + local pred; pred, pos = lb.match_expression(expr, pos) + assert(pred, 'syntax error: predicated expression not found') + preds[#preds+1] = pred + end + + -- extract number of parameter variables (name matching "_%d+") + local stmp = ''; lb.gsub(expr, function(u, sin) -- strip comments/strings + if u == 'e' then stmp = stmp .. ' ' .. sin .. ' ' end + end) + local max_param = 0; stmp:gsub('[%a_][%w_]*', function(s) + local s = s:match('^_(%d+)$') + if s then max_param = math_max(max_param, tonumber(s)) end + end) + + if pos ~= pose then + assert(false, "syntax error: unrecognized " .. expr:sub(pos)) + end + + --DEBUG: + --print('----\n', string.format("%q", expr), string.format("%q", out), opname) + --for k,v in ipairs(invarlists) do print(k,v, invallists[k]) end + --for k,v in ipairs(preds) do print(k,v) end + + return out, fortypes, invarlists, invallists, preds, opname, max_param +end + + +-- Create Lua code string representing comprehension. +-- Arguments are in the form returned by parse_comprehension. +local function code_comprehension( + out, fortypes, invarlists, invallists, preds, opname, max_param +) + local op = assert(ops[opname]) + local code = op.accum:gsub('%%s', out) + + for i=#preds,1,-1 do local pred = preds[i] + code = ' if ' .. pred .. ' then ' .. code .. ' end ' + end + for i=#invarlists,1,-1 do + if not fortypes[i] then + local arrayname = '__in' .. i + local idx = '__idx' .. i + code = + ' for ' .. idx .. ' = 1, #' .. arrayname .. ' do ' .. + ' local ' .. invarlists[i][1] .. ' = ' .. arrayname .. '['..idx..'] ' .. + code .. ' end ' + else + code = + ' for ' .. + table_concat(invarlists[i], ', ') .. + ' ' .. fortypes[i] .. ' ' .. + table_concat(invallists[i], ', ') .. + ' do ' .. code .. ' end ' + end + end + code = ' local __result = ( ' .. op.init .. ' ) ' .. code + return code +end + + +-- Convert code string represented by code_comprehension +-- into Lua function. Also must pass ninputs = #invarlists, +-- max_param, and invallists (from parse_comprehension). +-- Uses environment env. +local function wrap_comprehension(code, ninputs, max_param, invallists, env) + assert(ninputs > 0) + local ts = {} + for i=1,max_param do + ts[#ts+1] = '_' .. i + end + for i=1,ninputs do + if not invallists[i] then + local name = '__in' .. i + ts[#ts+1] = name + end + end + if #ts > 0 then + code = ' local ' .. table_concat(ts, ', ') .. ' = ... ' .. code + end + code = code .. ' return __result ' + --print('DEBUG:', code) + local f, err = utils.load(code,'tmp','t',env) + if not f then assert(false, err .. ' with generated code ' .. code) end + return f +end + + +-- Build Lua function from comprehension string. +-- Uses environment env. +local function build_comprehension(expr, env) + local out, fortypes, invarlists, invallists, preds, opname, max_param + = parse_comprehension(expr) + local code = code_comprehension( + out, fortypes, invarlists, invallists, preds, opname, max_param) + local f = wrap_comprehension(code, #invarlists, max_param, invallists, env) + return f +end + + +-- Creates new comprehension cache. +-- Any list comprehension function created are set to the environment +-- env (defaults to caller of new). +local function new(env) + -- Note: using a single global comprehension cache would have had + -- security implications (e.g. retrieving cached functions created + -- in other environments). + -- The cache lookup function could have instead been written to retrieve + -- the caller's environment, lookup up the cache private to that + -- environment, and then looked up the function in that cache. + -- That would avoid the need for this <new> call to + -- explicitly manage caches; however, that might also have an undue + -- performance penalty. + + if not env then + env = utils.getfenv(2) + end + + local mt = {} + local cache = setmetatable({}, mt) + + -- Index operator builds, caches, and returns Lua function + -- corresponding to comprehension expression string. + -- + -- Example: f = comprehension['x^2 for x'] + -- + function mt:__index(expr) + local f = build_comprehension(expr, env) + self[expr] = f -- cache + return f + end + + -- Convenience syntax. + -- Allows comprehension 'x^2 for x' instead of comprehension['x^2 for x']. + mt.__call = mt.__index + + cache.new = new + + return cache +end + + +local comprehension = {} +comprehension.new = new + +return comprehension diff --git a/Data/Libraries/Penlight/lua/pl/config.lua b/Data/Libraries/Penlight/lua/pl/config.lua new file mode 100644 index 0000000..2e6db0c --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/config.lua @@ -0,0 +1,207 @@ +--- Reads configuration files into a Lua table. +-- Understands INI files, classic Unix config files, and simple +-- delimited columns of values. See @{06-data.md.Reading_Configuration_Files|the Guide} +-- +-- # test.config +-- # Read timeout in seconds +-- read.timeout=10 +-- # Write timeout in seconds +-- write.timeout=5 +-- #acceptable ports +-- ports = 1002,1003,1004 +-- +-- -- readconfig.lua +-- local config = require 'config' +-- local t = config.read 'test.config' +-- print(pretty.write(t)) +-- +-- ### output ##### +-- { +-- ports = { +-- 1002, +-- 1003, +-- 1004 +-- }, +-- write_timeout = 5, +-- read_timeout = 10 +-- } +-- +-- @module pl.config + +local type,tonumber,ipairs,io, table = _G.type,_G.tonumber,_G.ipairs,_G.io,_G.table + +local function split(s,re) + local res = {} + local t_insert = table.insert + re = '[^'..re..']+' + for k in s:gmatch(re) do t_insert(res,k) end + return res +end + +local function strip(s) + return s:gsub('^%s+',''):gsub('%s+$','') +end + +local function strip_quotes (s) + return s:gsub("['\"](.*)['\"]",'%1') +end + +local config = {} + +--- like io.lines(), but allows for lines to be continued with '\'. +-- @param file a file-like object (anything where read() returns the next line) or a filename. +-- Defaults to stardard input. +-- @return an iterator over the lines, or nil +-- @return error 'not a file-like object' or 'file is nil' +function config.lines(file) + local f,openf,err + local line = '' + if type(file) == 'string' then + f,err = io.open(file,'r') + if not f then return nil,err end + openf = true + else + f = file or io.stdin + if not file.read then return nil, 'not a file-like object' end + end + if not f then return nil, 'file is nil' end + return function() + local l = f:read() + while l do + -- only for non-blank lines that don't begin with either ';' or '#' + if l:match '%S' and not l:match '^%s*[;#]' then + -- does the line end with '\'? + local i = l:find '\\%s*$' + if i then -- if so, + line = line..l:sub(1,i-1) + elseif line == '' then + return l + else + l = line..l + line = '' + return l + end + end + l = f:read() + end + if openf then f:close() end + end +end + +--- read a configuration file into a table +-- @param file either a file-like object or a string, which must be a filename +-- @tab[opt] cnfg a configuration table that may contain these fields: +-- +-- * `smart` try to deduce what kind of config file we have (default false) +-- * `variabilize` make names into valid Lua identifiers (default true) +-- * `convert_numbers` try to convert values into numbers (default true) +-- * `trim_space` ensure that there is no starting or trailing whitespace with values (default true) +-- * `trim_quotes` remove quotes from strings (default false) +-- * `list_delim` delimiter to use when separating columns (default ',') +-- * `keysep` separator between key and value pairs (default '=') +-- +-- @return a table containing items, or `nil` +-- @return error message (same as @{config.lines} +function config.read(file,cnfg) + local auto + + local iter,err = config.lines(file) + if not iter then return nil,err end + local line = iter() + cnfg = cnfg or {} + if cnfg.smart then + auto = true + if line:match '^[^=]+=' then + cnfg.keysep = '=' + elseif line:match '^[^:]+:' then + cnfg.keysep = ':' + cnfg.list_delim = ':' + elseif line:match '^%S+%s+' then + cnfg.keysep = ' ' + -- more than two columns assume that it's a space-delimited list + -- cf /etc/fstab with /etc/ssh/ssh_config + if line:match '^%S+%s+%S+%s+%S+' then + cnfg.list_delim = ' ' + end + cnfg.variabilize = false + end + end + + + local function check_cnfg (var,def) + local val = cnfg[var] + if val == nil then return def else return val end + end + + local initial_digits = '^[%d%+%-]' + local t = {} + local top_t = t + local variabilize = check_cnfg ('variabilize',true) + local list_delim = check_cnfg('list_delim',',') + local convert_numbers = check_cnfg('convert_numbers',true) + local convert_boolean = check_cnfg('convert_boolean',false) + local trim_space = check_cnfg('trim_space',true) + local trim_quotes = check_cnfg('trim_quotes',false) + local ignore_assign = check_cnfg('ignore_assign',false) + local keysep = check_cnfg('keysep','=') + local keypat = keysep == ' ' and '%s+' or '%s*'..keysep..'%s*' + if list_delim == ' ' then list_delim = '%s+' end + + local function process_name(key) + if variabilize then + key = key:gsub('[^%w]','_') + end + return key + end + + local function process_value(value) + if list_delim and value:find(list_delim) then + value = split(value,list_delim) + for i,v in ipairs(value) do + value[i] = process_value(v) + end + elseif convert_numbers and value:find(initial_digits) then + local val = tonumber(value) + if not val and value:match ' kB$' then + value = value:gsub(' kB','') + val = tonumber(value) + end + if val then value = val end + elseif convert_boolean and value == 'true' then + return true + elseif convert_boolean and value == 'false' then + return false + end + if type(value) == 'string' then + if trim_space then value = strip(value) end + if not trim_quotes and auto and value:match '^"' then + trim_quotes = true + end + if trim_quotes then value = strip_quotes(value) end + end + return value + end + + while line do + if line:find('^%[') then -- section! + local section = process_name(line:match('%[([^%]]+)%]')) + t = top_t + t[section] = {} + t = t[section] + else + line = line:gsub('^%s*','') + local i1,i2 = line:find(keypat) + if i1 and not ignore_assign then -- key,value assignment + local key = process_name(line:sub(1,i1-1)) + local value = process_value(line:sub(i2+1)) + t[key] = value + else -- a plain list of values... + t[#t+1] = process_value(line) + end + end + line = iter() + end + return top_t +end + +return config diff --git a/Data/Libraries/Penlight/lua/pl/data.lua b/Data/Libraries/Penlight/lua/pl/data.lua new file mode 100644 index 0000000..a565ebc --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/data.lua @@ -0,0 +1,654 @@ +--- Reading and querying simple tabular data. +-- +-- data.read 'test.txt' +-- ==> {{10,20},{2,5},{40,50},fieldnames={'x','y'},delim=','} +-- +-- Provides a way of creating basic SQL-like queries. +-- +-- require 'pl' +-- local d = data.read('xyz.txt') +-- local q = d:select('x,y,z where x > 3 and z < 2 sort by y') +-- for x,y,z in q do +-- print(x,y,z) +-- end +-- +-- See @{06-data.md.Reading_Columnar_Data|the Guide} +-- +-- Dependencies: `pl.utils`, `pl.array2d` (fallback methods) +-- @module pl.data + +local utils = require 'pl.utils' +local _DEBUG = rawget(_G,'_DEBUG') + +local patterns,function_arg,usplit,array_tostring = utils.patterns,utils.function_arg,utils.split,utils.array_tostring +local append,concat = table.insert,table.concat +local gsub = string.gsub +local io = io +local _G,print,type,tonumber,ipairs,setmetatable = _G,print,type,tonumber,ipairs,setmetatable + + +local data = {} + +local parse_select + +local function rstrip(s) + return (s:gsub('%s+$','')) +end + +local function strip (s) + return (rstrip(s):gsub('^%s*','')) +end + +-- This gives `l` the standard List metatable, +-- pulling in the List module. +local function makelist(l) + return setmetatable(l, require('pl.List')) +end + +local function map(fun,t) + local res = {} + for i = 1,#t do + res[i] = fun(t[i]) + end + return res +end + +local function split(line,delim,csv,n) + local massage + -- CSV fields may be double-quoted and may contain commas! + if csv and line:match '"' then + line = line:gsub('"([^"]+)"',function(str) + local s,cnt = str:gsub(',','\001') + if cnt > 0 then massage = true end + return s + end) + if massage then + massage = function(s) return (s:gsub('\001',',')) end + end + end + local res = (usplit(line,delim,false,n)) + if csv then + -- restore CSV commas-in-fields + if massage then res = map(massage,res) end + -- in CSV mode trailiing commas are significant! + if line:match ',$' then append(res,'') end + end + return makelist(res) +end + +local function find(t,v) + for i = 1,#t do + if v == t[i] then return i end + end +end + +local DataMT = { + column_by_name = function(self,name) + if type(name) == 'number' then + name = '$'..name + end + local arr = {} + for res in data.query(self,name) do + append(arr,res) + end + return makelist(arr) + end, + + copy_select = function(self,condn) + condn = parse_select(condn,self) + local iter = data.query(self,condn) + local res = {} + local row = makelist{iter()} + while #row > 0 do + append(res,row) + row = makelist{iter()} + end + res.delim = self.delim + return data.new(res,split(condn.fields,',')) + end, + + column_names = function(self) + return self.fieldnames + end, +} + +local array2d + +DataMT.__index = function(self,name) + local f = DataMT[name] + if f then return f end + if not array2d then + array2d = require 'pl.array2d' + end + return array2d[name] +end + +--- return a particular column as a list of values (method). +-- @param name either name of column, or numerical index. +-- @function Data.column_by_name + +--- return a query iterator on this data (method). +-- @string condn the query expression +-- @function Data.select +-- @see data.query + +--- return a row iterator on this data (method). +-- @string condn the query expression +-- @function Data.select_row + +--- return a new data object based on this query (method). +-- @string condn the query expression +-- @function Data.copy_select + +--- return the field names of this data object (method). +-- @function Data.column_names + +--- write out a row (method). +-- @param f file-like object +-- @function Data.write_row + +--- write data out to file (method). +-- @param f file-like object +-- @function Data.write + + +-- [guessing delimiter] We check for comma, tab and spaces in that order. +-- [issue] any other delimiters to be checked? +local delims = {',', '\t', ' ', ';'} + +local function guess_delim (line) + if line=='' then return ' ' end + for _,delim in ipairs(delims) do + if line:find(delim) then + return delim == ' ' and '%s+' or delim + end + end + return ' ' +end + +-- [file parameter] If it's a string, we try open as a filename. If nil, then +-- either stdin or stdout depending on the mode. Otherwise, check if this is +-- a file-like object (implements read or write depending) +local function open_file (f,mode) + local opened, err + local reading = mode == 'r' + if type(f) == 'string' then + if f == 'stdin' then + f = io.stdin + elseif f == 'stdout' then + f = io.stdout + else + f,err = io.open(f,mode) + if not f then return nil,err end + opened = true + end + end + if f and ((reading and not f.read) or (not reading and not f.write)) then + return nil, "not a file-like object" + end + return f,nil,opened +end + +--- read a delimited file in a Lua table. +-- By default, attempts to treat first line as separated list of fieldnames. +-- @param file a filename or a file-like object +-- @tab cnfg parsing options +-- @string cnfg.delim a string pattern to split fields +-- @array cnfg.fieldnames (i.e. don't read from first line) +-- @bool cnfg.no_convert (default is to try conversion on first data line) +-- @tab cnfg.convert table of custom conversion functions with column keys +-- @int cnfg.numfields indices of columns known to be numbers +-- @bool cnfg.last_field_collect only split as many fields as fieldnames. +-- @int cnfg.thousands_dot thousands separator in Excel CSV is '.' +-- @bool cnfg.csv fields may be double-quoted and contain commas; +-- Also, empty fields are considered to be equivalent to zero. +-- @return `data` object, or `nil` +-- @return error message. May be a file error, 'not a file-like object' +-- or a conversion error +function data.read(file,cnfg) + local count,line + local D = {} + if not cnfg then cnfg = {} end + local f,err,opened = open_file(file,'r') + if not f then return nil, err end + local thousands_dot = cnfg.thousands_dot + local csv = cnfg.csv + if csv then cnfg.delim = ',' end + + -- note that using dot as the thousands separator (@thousands_dot) + -- requires a special conversion function! For CSV, _empty fields_ are + -- considered to default to numerial zeroes. + local tonumber = tonumber + local function try_number(x) + if thousands_dot then x = x:gsub('%.(...)','%1') end + if csv and x == '' then x = '0' end + local v = tonumber(x) + if v == nil then return nil,"not a number" end + return v + end + + count = 1 + line = f:read() + if not line then return nil, "empty file" end + + -- first question: what is the delimiter? + D.delim = cnfg.delim and cnfg.delim or guess_delim(line) + local delim = D.delim + + local conversion + local numfields = {} + local function append_conversion (idx,conv) + conversion = conversion or {} + append(numfields,idx) + append(conversion,conv) + end + if cnfg.numfields then + for _,n in ipairs(cnfg.numfields) do append_conversion(n,try_number) end + end + + -- some space-delimited data starts with a space. This should not be a column, + -- although it certainly would be for comma-separated, etc. + local stripper + if delim == '%s+' and line:find(delim) == 1 then + stripper = function(s) return s:gsub('^%s+','') end + line = stripper(line) + end + -- first line will usually be field names. Unless fieldnames are specified, + -- we check if it contains purely numerical values for the case of reading + -- plain data files. + if not cnfg.fieldnames then + local fields,nums + fields = split(line,delim,csv) + if not cnfg.convert then + nums = map(tonumber,fields) + if #nums == #fields then -- they're ALL numbers! + append(D,nums) -- add the first converted row + -- and specify conversions for subsequent rows + for i = 1,#nums do append_conversion(i,try_number) end + else -- we'll try to check numbers just now.. + nums = nil + end + else -- [explicit column conversions] (any deduced number conversions will be added) + for idx,conv in pairs(cnfg.convert) do append_conversion(idx,conv) end + end + if nums == nil then + cnfg.fieldnames = fields + end + line = f:read() + count = count + 1 + if stripper then line = stripper(line) end + elseif type(cnfg.fieldnames) == 'string' then + cnfg.fieldnames = split(cnfg.fieldnames,delim,csv) + end + local nfields + -- at this point, the column headers have been read in. If the first + -- row consisted of numbers, it has already been added to the dataset. + if cnfg.fieldnames then + D.fieldnames = cnfg.fieldnames + -- [collecting end field] If @last_field_collect then we'll + -- only split as many fields as there are fieldnames + if cnfg.last_field_collect then + nfields = #D.fieldnames + end + -- [implicit column conversion] unless @no_convert, we need the numerical field indices + -- of the first data row. These can also be specified explicitly by @numfields. + if not cnfg.no_convert then + local fields = split(line,D.delim,csv,nfields) + for i = 1,#fields do + if not find(numfields,i) and try_number(fields[i]) then + append_conversion(i,try_number) + end + end + end + end + -- keep going until finished + while line do + if not line:find ('^%s*$') then -- [blank lines] ignore them! + if stripper then line = stripper(line) end + local fields = split(line,delim,csv,nfields) + if conversion then -- there were field conversions... + for k = 1,#numfields do + local i,conv = numfields[k],conversion[k] + local val,err = conv(fields[i]) + if val == nil then + return nil, err..": "..fields[i].." at line "..count + else + fields[i] = val + end + end + end + append(D,fields) + end + line = f:read() + count = count + 1 + end + if opened then f:close() end + if delim == '%s+' then D.delim = ' ' end + if not D.fieldnames then D.fieldnames = {} end + return data.new(D) +end + +local function write_row (data,f,row,delim) + data.temp = array_tostring(row,data.temp) + f:write(concat(data.temp,delim),'\n') +end + +function DataMT:write_row(f,row) + write_row(self,f,row,self.delim) +end + +--- write 2D data to a file. +-- Does not assume that the data has actually been +-- generated with `new` or `read`. +-- @param data 2D array +-- @param file filename or file-like object +-- @tparam[opt] {string} fieldnames list of fields (optional) +-- @string[opt='\t'] delim delimiter (default tab) +-- @return true or nil, error +function data.write (data,file,fieldnames,delim) + local f,err,opened = open_file(file,'w') + if not f then return nil, err end + if not fieldnames then + fieldnames = data.fieldnames + end + delim = delim or '\t' + if fieldnames and #fieldnames > 0 then + f:write(concat(fieldnames,delim),'\n') + end + for i = 1,#data do + write_row(data,f,data[i],delim) + end + if opened then f:close() end + return true +end + + +function DataMT:write(file) + data.write(self,file,self.fieldnames,self.delim) +end + +local function massage_fieldnames (fields,copy) + -- fieldnames must be valid Lua identifiers; ignore any surrounding padding + -- but keep the original fieldnames... + for i = 1,#fields do + local f = strip(fields[i]) + copy[i] = f + fields[i] = f:gsub('%W','_') + end +end + +--- create a new dataset from a table of rows. +-- Can specify the fieldnames, else the table must have a field called +-- 'fieldnames', which is either a string of delimiter-separated names, +-- or a table of names. <br> +-- If the table does not have a field called 'delim', then an attempt will be +-- made to guess it from the fieldnames string, defaults otherwise to tab. +-- @param d the table. +-- @tparam[opt] {string} fieldnames optional fieldnames +-- @return the table. +function data.new (d,fieldnames) + d.fieldnames = d.fieldnames or fieldnames or '' + if not d.delim and type(d.fieldnames) == 'string' then + d.delim = guess_delim(d.fieldnames) + d.fieldnames = split(d.fieldnames,d.delim) + end + d.fieldnames = makelist(d.fieldnames) + d.original_fieldnames = {} + massage_fieldnames(d.fieldnames,d.original_fieldnames) + setmetatable(d,DataMT) + -- a query with just the fieldname will return a sequence + -- of values, which seq.copy turns into a table. + return d +end + +local sorted_query = [[ +return function (t) + local i = 0 + local v + local ls = {} + for i,v in ipairs(t) do + if CONDITION then + ls[#ls+1] = v + end + end + table.sort(ls,function(v1,v2) + return SORT_EXPR + end) + local n = #ls + return function() + i = i + 1 + v = ls[i] + if i > n then return end + return FIELDLIST + end +end +]] + +-- question: is this optimized case actually worth the extra code? +local simple_query = [[ +return function (t) + local n = #t + local i = 0 + local v + return function() + repeat + i = i + 1 + v = t[i] + until i > n or CONDITION + if i > n then return end + return FIELDLIST + end +end +]] + +local function is_string (s) + return type(s) == 'string' +end + +local field_error + +local function fieldnames_as_string (data) + return concat(data.fieldnames,',') +end + +local function massage_fields(data,f) + local idx + if f:find '^%d+$' then + idx = tonumber(f) + else + idx = find(data.fieldnames,f) + end + if idx then + return 'v['..idx..']' + else + field_error = f..' not found in '..fieldnames_as_string(data) + return f + end +end + + +local function process_select (data,parms) + --- preparing fields ---- + field_error = nil + local fields = parms.fields + local numfields = fields:find '%$' or #data.fieldnames == 0 + if fields:find '^%s*%*%s*' then + if not numfields then + fields = fieldnames_as_string(data) + else + local ncol = #data[1] + fields = {} + for i = 1,ncol do append(fields,'$'..i) end + fields = concat(fields,',') + end + end + local idpat = patterns.IDEN + if numfields then + idpat = '%$(%d+)' + else + -- massage field names to replace non-identifier chars + fields = rstrip(fields):gsub('[^,%w]','_') + end + local massage_fields = utils.bind1(massage_fields,data) + local ret = gsub(fields,idpat,massage_fields) + if field_error then return nil,field_error end + parms.fields = fields + parms.proc_fields = ret + parms.where = parms.where or 'true' + if is_string(parms.where) then + parms.where = gsub(parms.where,idpat,massage_fields) + field_error = nil + end + return true +end + + +parse_select = function(s,data) + local endp + local parms = {} + local w1,w2 = s:find('where ') + local s1,s2 = s:find('sort by ') + if w1 then -- where clause! + endp = (s1 or 0)-1 + parms.where = s:sub(w2+1,endp) + end + if s1 then -- sort by clause (must be last!) + parms.sort_by = s:sub(s2+1) + end + endp = (w1 or s1 or 0)-1 + parms.fields = s:sub(1,endp) + local status,err = process_select(data,parms) + if not status then return nil,err + else return parms end +end + +--- create a query iterator from a select string. +-- Select string has this format: <br> +-- FIELDLIST [ where LUA-CONDN [ sort by FIELD] ]<br> +-- FIELDLIST is a comma-separated list of valid fields, or '*'. <br> <br> +-- The condition can also be a table, with fields 'fields' (comma-sep string or +-- table), 'sort_by' (string) and 'where' (Lua expression string or function) +-- @param data table produced by read +-- @param condn select string or table +-- @param context a list of tables to be searched when resolving functions +-- @param return_row if true, wrap the results in a row table +-- @return an iterator over the specified fields, or nil +-- @return an error message +function data.query(data,condn,context,return_row) + local err + if is_string(condn) then + condn,err = parse_select(condn,data) + if not condn then return nil,err end + elseif type(condn) == 'table' then + if type(condn.fields) == 'table' then + condn.fields = concat(condn.fields,',') + end + if not condn.proc_fields then + local status,err = process_select(data,condn) + if not status then return nil,err end + end + else + return nil, "condition must be a string or a table" + end + local query + if condn.sort_by then -- use sorted_query + query = sorted_query + else + query = simple_query + end + local fields = condn.proc_fields or condn.fields + if return_row then + fields = '{'..fields..'}' + end + query = query:gsub('FIELDLIST',fields) + if is_string(condn.where) then + query = query:gsub('CONDITION',condn.where) + condn.where = nil + else + query = query:gsub('CONDITION','_condn(v)') + condn.where = function_arg(0,condn.where,'condition.where must be callable') + end + if condn.sort_by then + local expr,sort_var,sort_dir + local sort_by = condn.sort_by + local i1,i2 = sort_by:find('%s+') + if i1 then + sort_var,sort_dir = sort_by:sub(1,i1-1),sort_by:sub(i2+1) + else + sort_var = sort_by + sort_dir = 'asc' + end + if sort_var:match '^%$' then sort_var = sort_var:sub(2) end + sort_var = massage_fields(data,sort_var) + if field_error then return nil,field_error end + if sort_dir == 'asc' then + sort_dir = '<' + else + sort_dir = '>' + end + expr = ('%s %s %s'):format(sort_var:gsub('v','v1'),sort_dir,sort_var:gsub('v','v2')) + query = query:gsub('SORT_EXPR',expr) + end + if condn.where then + query = 'return function(_condn) '..query..' end' + end + if _DEBUG then print(query) end + + local fn,err = utils.load(query,'tmp') + if not fn then return nil,err end + fn = fn() -- get the function + if condn.where then + fn = fn(condn.where) + end + local qfun = fn(data) + if context then + -- [specifying context for condition] @context is a list of tables which are + -- 'injected'into the condition's custom context + append(context,_G) + local lookup = {} + utils.setfenv(qfun,lookup) + setmetatable(lookup,{ + __index = function(tbl,key) + -- _G.print(tbl,key) + for k,t in ipairs(context) do + if t[key] then return t[key] end + end + end + }) + end + return qfun +end + + +DataMT.select = data.query +DataMT.select_row = function(d,condn,context) + return data.query(d,condn,context,true) +end + +--- Filter input using a query. +-- @string Q a query string +-- @param infile filename or file-like object +-- @param outfile filename or file-like object +-- @bool dont_fail true if you want to return an error, not just fail +function data.filter (Q,infile,outfile,dont_fail) + local d = data.read(infile or 'stdin') + local out = open_file(outfile or 'stdout') + local iter,err = d:select(Q) + local delim = d.delim + if not iter then + err = 'error: '..err + if dont_fail then + return nil,err + else + utils.quit(1,err) + end + end + while true do + local res = {iter()} + if #res == 0 then break end + out:write(concat(res,delim),'\n') + end +end + +return data + diff --git a/Data/Libraries/Penlight/lua/pl/dir.lua b/Data/Libraries/Penlight/lua/pl/dir.lua new file mode 100644 index 0000000..b19c8fc --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/dir.lua @@ -0,0 +1,524 @@ +--- Listing files in directories and creating/removing directory paths. +-- +-- Dependencies: `pl.utils`, `pl.path` +-- +-- Soft Dependencies: `alien`, `ffi` (either are used on Windows for copying/moving files) +-- @module pl.dir + +local utils = require 'pl.utils' +local path = require 'pl.path' +local is_windows = path.is_windows +local ldir = path.dir +local mkdir = path.mkdir +local rmdir = path.rmdir +local sub = string.sub +local os,pcall,ipairs,pairs,require,setmetatable = os,pcall,ipairs,pairs,require,setmetatable +local remove = os.remove +local append = table.insert +local assert_arg,assert_string,raise = utils.assert_arg,utils.assert_string,utils.raise + +local exists, isdir = path.exists, path.isdir +local sep = path.sep + +local dir = {} + +local function makelist(l) + return setmetatable(l, require('pl.List')) +end + +local function assert_dir (n,val) + assert_arg(n,val,'string',path.isdir,'not a directory',4) +end + +local function filemask(mask) + mask = utils.escape(path.normcase(mask)) + return '^'..mask:gsub('%%%*','.*'):gsub('%%%?','.')..'$' +end + +--- Test whether a file name matches a shell pattern. +-- Both parameters are case-normalized if operating system is +-- case-insensitive. +-- @string filename A file name. +-- @string pattern A shell pattern. The only special characters are +-- `'*'` and `'?'`: `'*'` matches any sequence of characters and +-- `'?'` matches any single character. +-- @treturn bool +-- @raise dir and mask must be strings +function dir.fnmatch(filename,pattern) + assert_string(1,filename) + assert_string(2,pattern) + return path.normcase(filename):find(filemask(pattern)) ~= nil +end + +--- Return a list of all file names within an array which match a pattern. +-- @tab filenames An array containing file names. +-- @string pattern A shell pattern. +-- @treturn List(string) List of matching file names. +-- @raise dir and mask must be strings +function dir.filter(filenames,pattern) + assert_arg(1,filenames,'table') + assert_string(2,pattern) + local res = {} + local mask = filemask(pattern) + for i,f in ipairs(filenames) do + if path.normcase(f):find(mask) then append(res,f) end + end + return makelist(res) +end + +local function _listfiles(dirname,filemode,match) + local res = {} + local check = utils.choose(filemode,path.isfile,path.isdir) + if not dirname then dirname = '.' end + for f in ldir(dirname) do + if f ~= '.' and f ~= '..' then + local p = path.join(dirname,f) + if check(p) and (not match or match(f)) then + append(res,p) + end + end + end + return makelist(res) +end + +--- return a list of all files in a directory which match a shell pattern. +-- @string dirname A directory. If not given, all files in current directory are returned. +-- @string mask A shell pattern. If not given, all files are returned. +-- @treturn {string} list of files +-- @raise dirname and mask must be strings +function dir.getfiles(dirname,mask) + assert_dir(1,dirname) + if mask then assert_string(2,mask) end + local match + if mask then + mask = filemask(mask) + match = function(f) + return path.normcase(f):find(mask) + end + end + return _listfiles(dirname,true,match) +end + +--- return a list of all subdirectories of the directory. +-- @string dirname A directory +-- @treturn {string} a list of directories +-- @raise dir must be a a valid directory +function dir.getdirectories(dirname) + assert_dir(1,dirname) + return _listfiles(dirname,false) +end + +local alien,ffi,ffi_checked,CopyFile,MoveFile,GetLastError,win32_errors,cmd_tmpfile + +local function execute_command(cmd,parms) + if not cmd_tmpfile then cmd_tmpfile = path.tmpname () end + local err = path.is_windows and ' > ' or ' 2> ' + cmd = cmd..' '..parms..err..utils.quote_arg(cmd_tmpfile) + local ret = utils.execute(cmd) + if not ret then + local err = (utils.readfile(cmd_tmpfile):gsub('\n(.*)','')) + remove(cmd_tmpfile) + return false,err + else + remove(cmd_tmpfile) + return true + end +end + +local function find_ffi_copyfile () + if not ffi_checked then + ffi_checked = true + local res + res,alien = pcall(require,'alien') + if not res then + alien = nil + res, ffi = pcall(require,'ffi') + end + if not res then + ffi = nil + return + end + else + return + end + if alien then + -- register the Win32 CopyFile and MoveFile functions + local kernel = alien.load('kernel32.dll') + CopyFile = kernel.CopyFileA + CopyFile:types{'string','string','int',ret='int',abi='stdcall'} + MoveFile = kernel.MoveFileA + MoveFile:types{'string','string',ret='int',abi='stdcall'} + GetLastError = kernel.GetLastError + GetLastError:types{ret ='int', abi='stdcall'} + elseif ffi then + ffi.cdef [[ + int CopyFileA(const char *src, const char *dest, int iovr); + int MoveFileA(const char *src, const char *dest); + int GetLastError(); + ]] + CopyFile = ffi.C.CopyFileA + MoveFile = ffi.C.MoveFileA + GetLastError = ffi.C.GetLastError + end + win32_errors = { + ERROR_FILE_NOT_FOUND = 2, + ERROR_PATH_NOT_FOUND = 3, + ERROR_ACCESS_DENIED = 5, + ERROR_WRITE_PROTECT = 19, + ERROR_BAD_UNIT = 20, + ERROR_NOT_READY = 21, + ERROR_WRITE_FAULT = 29, + ERROR_READ_FAULT = 30, + ERROR_SHARING_VIOLATION = 32, + ERROR_LOCK_VIOLATION = 33, + ERROR_HANDLE_DISK_FULL = 39, + ERROR_BAD_NETPATH = 53, + ERROR_NETWORK_BUSY = 54, + ERROR_DEV_NOT_EXIST = 55, + ERROR_FILE_EXISTS = 80, + ERROR_OPEN_FAILED = 110, + ERROR_INVALID_NAME = 123, + ERROR_BAD_PATHNAME = 161, + ERROR_ALREADY_EXISTS = 183, + } +end + +local function two_arguments (f1,f2) + return utils.quote_arg(f1)..' '..utils.quote_arg(f2) +end + +local function file_op (is_copy,src,dest,flag) + if flag == 1 and path.exists(dest) then + return false,"cannot overwrite destination" + end + if is_windows then + -- if we haven't tried to load Alien/LuaJIT FFI before, then do so + find_ffi_copyfile() + -- fallback if there's no Alien, just use DOS commands *shudder* + -- 'rename' involves a copy and then deleting the source. + if not CopyFile then + if path.is_windows then + src = src:gsub("/","\\") + dest = dest:gsub("/","\\") + end + local res, err = execute_command('copy',two_arguments(src,dest)) + if not res then return false,err end + if not is_copy then + return execute_command('del',utils.quote_arg(src)) + end + return true + else + if path.isdir(dest) then + dest = path.join(dest,path.basename(src)) + end + local ret + if is_copy then ret = CopyFile(src,dest,flag) + else ret = MoveFile(src,dest) end + if ret == 0 then + local err = GetLastError() + for name,value in pairs(win32_errors) do + if value == err then return false,name end + end + return false,"Error #"..err + else return true + end + end + else -- for Unix, just use cp for now + return execute_command(is_copy and 'cp' or 'mv', + two_arguments(src,dest)) + end +end + +--- copy a file. +-- @string src source file +-- @string dest destination file or directory +-- @bool flag true if you want to force the copy (default) +-- @treturn bool operation succeeded +-- @raise src and dest must be strings +function dir.copyfile (src,dest,flag) + assert_string(1,src) + assert_string(2,dest) + flag = flag==nil or flag + return file_op(true,src,dest,flag and 0 or 1) +end + +--- move a file. +-- @string src source file +-- @string dest destination file or directory +-- @treturn bool operation succeeded +-- @raise src and dest must be strings +function dir.movefile (src,dest) + assert_string(1,src) + assert_string(2,dest) + return file_op(false,src,dest,0) +end + +local function _dirfiles(dirname,attrib) + local dirs = {} + local files = {} + for f in ldir(dirname) do + if f ~= '.' and f ~= '..' then + local p = path.join(dirname,f) + local mode = attrib(p,'mode') + if mode=='directory' then + append(dirs,f) + else + append(files,f) + end + end + end + return makelist(dirs), makelist(files) +end + + +--- return an iterator which walks through a directory tree starting at root. +-- The iterator returns (root,dirs,files) +-- Note that dirs and files are lists of names (i.e. you must say path.join(root,d) +-- to get the actual full path) +-- If bottom_up is false (or not present), then the entries at the current level are returned +-- before we go deeper. This means that you can modify the returned list of directories before +-- continuing. +-- This is a clone of os.walk from the Python libraries. +-- @string root A starting directory +-- @bool bottom_up False if we start listing entries immediately. +-- @bool follow_links follow symbolic links +-- @return an iterator returning root,dirs,files +-- @raise root must be a directory +function dir.walk(root,bottom_up,follow_links) + assert_dir(1,root) + local attrib + if path.is_windows or not follow_links then + attrib = path.attrib + else + attrib = path.link_attrib + end + + local to_scan = { root } + local to_return = {} + local iter = function() + while #to_scan > 0 do + local current_root = table.remove(to_scan) + local dirs,files = _dirfiles(current_root, attrib) + for _, d in ipairs(dirs) do + table.insert(to_scan, current_root..path.sep..d) + end + if not bottom_up then + return current_root, dirs, files + else + table.insert(to_return, { current_root, dirs, files }) + end + end + if #to_return > 0 then + return utils.unpack(table.remove(to_return)) + end + end + + return iter +end + +--- remove a whole directory tree. +-- Symlinks in the tree will be deleted without following them. +-- @string fullpath A directory path (must be an actual directory, not a symlink) +-- @return true or nil +-- @return error if failed +-- @raise fullpath must be a string +function dir.rmtree(fullpath) + assert_dir(1,fullpath) + if path.islink(fullpath) then return false,'will not follow symlink' end + for root,dirs,files in dir.walk(fullpath,true) do + if path.islink(root) then + -- sub dir is a link, remove link, do not follow + if is_windows then + -- Windows requires using "rmdir". Deleting the link like a file + -- will instead delete all files from the target directory!! + local res, err = rmdir(root) + if not res then return nil,err .. ": " .. root end + else + local res, err = remove(root) + if not res then return nil,err .. ": " .. root end + end + else + for i,f in ipairs(files) do + local res, err = remove(path.join(root,f)) + if not res then return nil,err .. ": " .. path.join(root,f) end + end + local res, err = rmdir(root) + if not res then return nil,err .. ": " .. root end + end + end + return true +end + + +do + local dirpat + if path.is_windows then + dirpat = '(.+)\\[^\\]+$' + else + dirpat = '(.+)/[^/]+$' + end + + local _makepath + function _makepath(p) + -- windows root drive case + if p:find '^%a:[\\]*$' then + return true + end + if not path.isdir(p) then + local subp = p:match(dirpat) + if subp then + local ok, err = _makepath(subp) + if not ok then return nil, err end + end + return mkdir(p) + else + return true + end + end + + --- create a directory path. + -- This will create subdirectories as necessary! + -- @string p A directory path + -- @return true on success, nil + errormsg on failure + -- @raise failure to create + function dir.makepath (p) + assert_string(1,p) + if path.is_windows then + p = p:gsub("/", "\\") + end + return _makepath(path.abspath(p)) + end +end + +--- clone a directory tree. Will always try to create a new directory structure +-- if necessary. +-- @string path1 the base path of the source tree +-- @string path2 the new base path for the destination +-- @func file_fun an optional function to apply on all files +-- @bool verbose an optional boolean to control the verbosity of the output. +-- It can also be a logging function that behaves like print() +-- @return true, or nil +-- @return error message, or list of failed directory creations +-- @return list of failed file operations +-- @raise path1 and path2 must be strings +-- @usage clonetree('.','../backup',copyfile) +function dir.clonetree (path1,path2,file_fun,verbose) + assert_string(1,path1) + assert_string(2,path2) + if verbose == true then verbose = print end + local abspath,normcase,isdir,join = path.abspath,path.normcase,path.isdir,path.join + local faildirs,failfiles = {},{} + if not isdir(path1) then return raise 'source is not a valid directory' end + path1 = abspath(normcase(path1)) + path2 = abspath(normcase(path2)) + if verbose then verbose('normalized:',path1,path2) end + -- particularly NB that the new path isn't fully contained in the old path + if path1 == path2 then return raise "paths are the same" end + local _,i2 = path2:find(path1,1,true) + if i2 == #path1 and path2:sub(i2+1,i2+1) == path.sep then + return raise 'destination is a subdirectory of the source' + end + local cp = path.common_prefix (path1,path2) + local idx = #cp + if idx == 0 then -- no common path, but watch out for Windows paths! + if path1:sub(2,2) == ':' then idx = 3 end + end + for root,dirs,files in dir.walk(path1) do + local opath = path2..root:sub(idx) + if verbose then verbose('paths:',opath,root) end + if not isdir(opath) then + local ret = dir.makepath(opath) + if not ret then append(faildirs,opath) end + if verbose then verbose('creating:',opath,ret) end + end + if file_fun then + for i,f in ipairs(files) do + local p1 = join(root,f) + local p2 = join(opath,f) + local ret = file_fun(p1,p2) + if not ret then append(failfiles,p2) end + if verbose then + verbose('files:',p1,p2,ret) + end + end + end + end + return true,faildirs,failfiles +end + + +-- each entry of the stack is an array with three items: +-- 1. the name of the directory +-- 2. the lfs iterator function +-- 3. the lfs iterator userdata +local function treeiter(iterstack) + local diriter = iterstack[#iterstack] + if not diriter then + return -- done + end + + local dirname = diriter[1] + local entry = diriter[2](diriter[3]) + if not entry then + table.remove(iterstack) + return treeiter(iterstack) -- tail-call to try next + end + + if entry ~= "." and entry ~= ".." then + entry = dirname .. sep .. entry + if exists(entry) then -- Just in case a symlink is broken. + local is_dir = isdir(entry) + if is_dir then + table.insert(iterstack, { entry, ldir(entry) }) + end + return entry, is_dir + end + end + + return treeiter(iterstack) -- tail-call to try next +end + + +--- return an iterator over all entries in a directory tree +-- @string d a directory +-- @return an iterator giving pathname and mode (true for dir, false otherwise) +-- @raise d must be a non-empty string +function dir.dirtree( d ) + assert( d and d ~= "", "directory parameter is missing or empty" ) + + local last = sub ( d, -1 ) + if last == sep or last == '/' then + d = sub( d, 1, -2 ) + end + + local iterstack = { {d, ldir(d)} } + + return treeiter, iterstack +end + + +--- Recursively returns all the file starting at _path_. It can optionally take a shell pattern and +-- only returns files that match _shell_pattern_. If a pattern is given it will do a case insensitive search. +-- @string start_path A directory. If not given, all files in current directory are returned. +-- @string shell_pattern A shell pattern. If not given, all files are returned. +-- @treturn List(string) containing all the files found recursively starting at _path_ and filtered by _shell_pattern_. +-- @raise start_path must be a directory +function dir.getallfiles( start_path, shell_pattern ) + assert_dir(1,start_path) + shell_pattern = shell_pattern or "*" + + local files = {} + local normcase = path.normcase + for filename, mode in dir.dirtree( start_path ) do + if not mode then + local mask = filemask( shell_pattern ) + if normcase(filename):find( mask ) then + files[#files + 1] = filename + end + end + end + + return makelist(files) +end + +return dir diff --git a/Data/Libraries/Penlight/lua/pl/file.lua b/Data/Libraries/Penlight/lua/pl/file.lua new file mode 100644 index 0000000..b8058c4 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/file.lua @@ -0,0 +1,55 @@ +--- File manipulation functions: reading, writing, moving and copying. +-- +-- This module wraps a number of functions from other modules into a +-- file related module for convenience. +-- +-- Dependencies: `pl.utils`, `pl.dir`, `pl.path` +-- @module pl.file +local os = os +local utils = require 'pl.utils' +local dir = require 'pl.dir' +local path = require 'pl.path' + +local file = {} + +--- return the contents of a file as a string. +-- This function is a copy of `utils.readfile`. +-- @function file.read +file.read = utils.readfile + +--- write a string to a file. +-- This function is a copy of `utils.writefile`. +-- @function file.write +file.write = utils.writefile + +--- copy a file. +-- This function is a copy of `dir.copyfile`. +-- @function file.copy +file.copy = dir.copyfile + +--- move a file. +-- This function is a copy of `dir.movefile`. +-- @function file.move +file.move = dir.movefile + +--- Return the time of last access as the number of seconds since the epoch. +-- This function is a copy of `path.getatime`. +-- @function file.access_time +file.access_time = path.getatime + +---Return when the file was created. +-- This function is a copy of `path.getctime`. +-- @function file.creation_time +file.creation_time = path.getctime + +--- Return the time of last modification. +-- This function is a copy of `path.getmtime`. +-- @function file.modified_time +file.modified_time = path.getmtime + +--- Delete a file. +-- This function is a copy of `os.remove`. +-- @function file.delete +file.delete = os.remove + +return file diff --git a/Data/Libraries/Penlight/lua/pl/func.lua b/Data/Libraries/Penlight/lua/pl/func.lua new file mode 100644 index 0000000..daac0fc --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/func.lua @@ -0,0 +1,393 @@ +--- Functional helpers like composition, binding and placeholder expressions. +-- Placeholder expressions are useful for short anonymous functions, and were +-- inspired by the Boost Lambda library. +-- +-- > utils.import 'pl.func' +-- > ls = List{10,20,30} +-- > = ls:map(_1+1) +-- {11,21,31} +-- +-- They can also be used to _bind_ particular arguments of a function. +-- +-- > p = bind(print,'start>',_0) +-- > p(10,20,30) +-- > start> 10 20 30 +-- +-- See @{07-functional.md.Creating_Functions_from_Functions|the Guide} +-- +-- Dependencies: `pl.utils`, `pl.tablex` +-- @module pl.func +local type,setmetatable,getmetatable,rawset = type,setmetatable,getmetatable,rawset +local concat,append = table.concat,table.insert +local tostring = tostring +local utils = require 'pl.utils' +local pairs,rawget,unpack,pack = pairs,rawget,utils.unpack,utils.pack +local tablex = require 'pl.tablex' +local map = tablex.map +local _DEBUG = rawget(_G,'_DEBUG') +local assert_arg = utils.assert_arg + +local func = {} + +-- metatable for Placeholder Expressions (PE) +local _PEMT = {} + +local function P (t) + setmetatable(t,_PEMT) + return t +end + +func.PE = P + +local function isPE (obj) + return getmetatable(obj) == _PEMT +end + +func.isPE = isPE + +-- construct a placeholder variable (e.g _1 and _2) +local function PH (idx) + return P {op='X',repr='_'..idx, index=idx} +end + +-- construct a constant placeholder variable (e.g _C1 and _C2) +local function CPH (idx) + return P {op='X',repr='_C'..idx, index=idx} +end + +func._1,func._2,func._3,func._4,func._5 = PH(1),PH(2),PH(3),PH(4),PH(5) +func._0 = P{op='X',repr='...',index=0} + +function func.Var (name) + local ls = utils.split(name,'[%s,]+') + local res = {} + for i = 1, #ls do + append(res,P{op='X',repr=ls[i],index=0}) + end + return unpack(res) +end + +function func._ (value) + return P{op='X',repr=value,index='wrap'} +end + +local repr + +func.Nil = func.Var 'nil' + +function _PEMT.__index(obj,key) + return P{op='[]',obj,key} +end + +function _PEMT.__call(fun,...) + return P{op='()',fun,...} +end + +function _PEMT.__tostring (e) + return repr(e) +end + +function _PEMT.__unm(arg) + return P{op='unm',arg} +end + +function func.Not (arg) + return P{op='not',arg} +end + +function func.Len (arg) + return P{op='#',arg} +end + + +local function binreg(context,t) + for name,op in pairs(t) do + rawset(context,name,function(x,y) + return P{op=op,x,y} + end) + end +end + +local function import_name (name,fun,context) + rawset(context,name,function(...) + return P{op='()',fun,...} + end) +end + +local imported_functions = {} + +local function is_global_table (n) + return type(_G[n]) == 'table' +end + +--- wrap a table of functions. This makes them available for use in +-- placeholder expressions. +-- @string tname a table name +-- @tab context context to put results, defaults to environment of caller +function func.import(tname,context) + assert_arg(1,tname,'string',is_global_table,'arg# 1: not a name of a global table') + local t = _G[tname] + context = context or _G + for name,fun in pairs(t) do + import_name(name,fun,context) + imported_functions[fun] = name + end +end + +--- register a function for use in placeholder expressions. +-- @func fun a function +-- @string[opt] name an optional name +-- @return a placeholder functiond +function func.register (fun,name) + assert_arg(1,fun,'function') + if name then + assert_arg(2,name,'string') + imported_functions[fun] = name + end + return function(...) + return P{op='()',fun,...} + end +end + +function func.lookup_imported_name (fun) + return imported_functions[fun] +end + +local function _arg(...) return ... end + +function func.Args (...) + return P{op='()',_arg,...} +end + +-- binary operators with their precedences (see Lua manual) +-- precedences might be incremented by one before use depending on +-- left- or right-associativity, space them out +local binary_operators = { + ['or'] = 0, + ['and'] = 2, + ['=='] = 4, ['~='] = 4, ['<'] = 4, ['>'] = 4, ['<='] = 4, ['>='] = 4, + ['..'] = 6, + ['+'] = 8, ['-'] = 8, + ['*'] = 10, ['/'] = 10, ['%'] = 10, + ['^'] = 14 +} + +-- unary operators with their precedences +local unary_operators = { + ['not'] = 12, ['#'] = 12, ['unm'] = 12 +} + +-- comparisons (as prefix functions) +binreg (func,{And='and',Or='or',Eq='==',Lt='<',Gt='>',Le='<=',Ge='>='}) + +-- standard binary operators (as metamethods) +binreg (_PEMT,{__add='+',__sub='-',__mul='*',__div='/',__mod='%',__pow='^',__concat='..'}) + +binreg (_PEMT,{__eq='=='}) + +--- all elements of a table except the first. +-- @tab ls a list-like table. +function func.tail (ls) + assert_arg(1,ls,'table') + local res = {} + for i = 2,#ls do + append(res,ls[i]) + end + return res +end + +--- create a string representation of a placeholder expression. +-- @param e a placeholder expression +-- @param lastpred not used +function repr (e,lastpred) + local tail = func.tail + if isPE(e) then + local pred = binary_operators[e.op] or unary_operators[e.op] + if pred then + -- binary or unary operator + local s + if binary_operators[e.op] then + local left_pred = pred + local right_pred = pred + if e.op == '..' or e.op == '^' then + left_pred = left_pred + 1 + else + right_pred = right_pred + 1 + end + local left_arg = repr(e[1], left_pred) + local right_arg = repr(e[2], right_pred) + s = left_arg..' '..e.op..' '..right_arg + else + local op = e.op == 'unm' and '-' or e.op + s = op..' '..repr(e[1], pred) + end + if lastpred and lastpred > pred then + s = '('..s..')' + end + return s + else -- either postfix, or a placeholder + local ls = map(repr,e) + if e.op == '[]' then + return ls[1]..'['..ls[2]..']' + elseif e.op == '()' then + local fn + if ls[1] ~= nil then -- was _args, undeclared! + fn = ls[1] + else + fn = '' + end + return fn..'('..concat(tail(ls),',')..')' + else + return e.repr + end + end + elseif type(e) == 'string' then + return '"'..e..'"' + elseif type(e) == 'function' then + local name = func.lookup_imported_name(e) + if name then return name else return tostring(e) end + else + return tostring(e) --should not really get here! + end +end +func.repr = repr + +-- collect all the non-PE values in this PE into vlist, and replace each occurence +-- with a constant PH (_C1, etc). Return the maximum placeholder index found. +local collect_values +function collect_values (e,vlist) + if isPE(e) then + if e.op ~= 'X' then + local m = 0 + for i = 1,#e do + local subx = e[i] + local pe = isPE(subx) + if pe then + if subx.op == 'X' and subx.index == 'wrap' then + subx = subx.repr + pe = false + else + m = math.max(m,collect_values(subx,vlist)) + end + end + if not pe then + append(vlist,subx) + e[i] = CPH(#vlist) + end + end + return m + else -- was a placeholder, it has an index... + return e.index + end + else -- plain value has no placeholder dependence + return 0 + end +end +func.collect_values = collect_values + +--- instantiate a PE into an actual function. First we find the largest placeholder used, +-- e.g. _2; from this a list of the formal parameters can be build. Then we collect and replace +-- any non-PE values from the PE, and build up a constant binding list. +-- Finally, the expression can be compiled, and e.__PE_function is set. +-- @param e a placeholder expression +-- @return a function +function func.instantiate (e) + local consts,values,parms = {},{},{} + local rep, err, fun + local n = func.collect_values(e,values) + for i = 1,#values do + append(consts,'_C'..i) + if _DEBUG then print(i,values[i]) end + end + for i =1,n do + append(parms,'_'..i) + end + consts = concat(consts,',') + parms = concat(parms,',') + rep = repr(e) + local fstr = ('return function(%s) return function(%s) return %s end end'):format(consts,parms,rep) + if _DEBUG then print(fstr) end + fun,err = utils.load(fstr,'fun') + if not fun then return nil,err end + fun = fun() -- get wrapper + fun = fun(unpack(values)) -- call wrapper (values could be empty) + e.__PE_function = fun + return fun +end + +--- instantiate a PE unless it has already been done. +-- @param e a placeholder expression +-- @return the function +function func.I(e) + if rawget(e,'__PE_function') then + return e.__PE_function + else return func.instantiate(e) + end +end + +utils.add_function_factory(_PEMT,func.I) + +--- bind the first parameter of the function to a value. +-- @function func.bind1 +-- @func fn a function of one or more arguments +-- @param p a value +-- @return a function of one less argument +-- @usage (bind1(math.max,10))(20) == math.max(10,20) +func.bind1 = utils.bind1 +func.curry = func.bind1 + +--- create a function which chains two functions. +-- @func f a function of at least one argument +-- @func g a function of at least one argument +-- @return a function +-- @usage printf = compose(io.write,string.format) +function func.compose (f,g) + return function(...) return f(g(...)) end +end + +--- bind the arguments of a function to given values. +-- `bind(fn,v,_2)` is equivalent to `bind1(fn,v)`. +-- @func fn a function of at least one argument +-- @param ... values or placeholder variables +-- @return a function +-- @usage (bind(f,_1,a))(b) == f(a,b) +-- @usage (bind(f,_2,_1))(a,b) == f(b,a) +function func.bind(fn,...) + local args = pack(...) + local holders,parms,bvalues,values = {},{},{'fn'},{} + local nv,maxplace,varargs = 1,0,false + for i = 1,args.n do + local a = args[i] + if isPE(a) and a.op == 'X' then + append(holders,a.repr) + maxplace = math.max(maxplace,a.index) + if a.index == 0 then varargs = true end + else + local v = '_v'..nv + append(bvalues,v) + append(holders,v) + append(values,a) + nv = nv + 1 + end + end + for np = 1,maxplace do + append(parms,'_'..np) + end + if varargs then append(parms,'...') end + bvalues = concat(bvalues,',') + parms = concat(parms,',') + holders = concat(holders,',') + local fstr = ([[ +return function (%s) + return function(%s) return fn(%s) end +end +]]):format(bvalues,parms,holders) + if _DEBUG then print(fstr) end + local res = utils.load(fstr) + res = res() + return res(fn,unpack(values)) +end + +return func + + diff --git a/Data/Libraries/Penlight/lua/pl/import_into.lua b/Data/Libraries/Penlight/lua/pl/import_into.lua new file mode 100644 index 0000000..6dd2741 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/import_into.lua @@ -0,0 +1,91 @@ +-------------- +-- PL loader, for loading all PL libraries, only on demand. +-- Whenever a module is implicitly accesssed, the table will have the module automatically injected. +-- (e.g. `_ENV.tablex`) +-- then that module is dynamically loaded. The submodules are all brought into +-- the table that is provided as the argument, or returned in a new table. +-- If a table is provided, that table's metatable is clobbered, but the values are not. +-- This module returns a single function, which is passed the environment. +-- If this is `true`, then return a 'shadow table' as the module +-- See @{01-introduction.md.To_Inject_or_not_to_Inject_|the Guide} + +-- @module pl.import_into + +return function(env) + local mod + if env == true then + mod = {} + env = {} + end + local env = env or {} + + local modules = { + utils = true,path=true,dir=true,tablex=true,stringio=true,sip=true, + input=true,seq=true,lexer=true,stringx=true, + config=true,pretty=true,data=true,func=true,text=true, + operator=true,lapp=true,array2d=true, + comprehension=true,xml=true,types=true, + test = true, app = true, file = true, class = true, + luabalanced = true, permute = true, template = true, + url = true, compat = true, + -- classes -- + List = true, Map = true, Set = true, + OrderedMap = true, MultiMap = true, Date = true, + } + rawset(env,'utils',require 'pl.utils') + + for name,klass in pairs(env.utils.stdmt) do + klass.__index = function(t,key) + return require ('pl.'..name)[key] + end; + end + + -- ensure that we play nice with libraries that also attach a metatable + -- to the global table; always forward to a custom __index if we don't + -- match + + local _hook,_prev_index + local gmt = {} + local prevenvmt = getmetatable(env) + if prevenvmt then + _prev_index = prevenvmt.__index + if prevenvmt.__newindex then + gmt.__index = prevenvmt.__newindex + end + end + + function gmt.hook(handler) + _hook = handler + end + + function gmt.__index(t,name) + local found = modules[name] + -- either true, or the name of the module containing this class. + -- either way, we load the required module and make it globally available. + if found then + -- e..g pretty.dump causes pl.pretty to become available as 'pretty' + rawset(env,name,require('pl.'..name)) + return env[name] + else + local res + if _hook then + res = _hook(t,name) + if res then return res end + end + if _prev_index then + return _prev_index(t,name) + end + end + end + + if mod then + function gmt.__newindex(t,name,value) + mod[name] = value + rawset(t,name,value) + end + end + + setmetatable(env,gmt) + + return env,mod or env +end diff --git a/Data/Libraries/Penlight/lua/pl/init.lua b/Data/Libraries/Penlight/lua/pl/init.lua new file mode 100644 index 0000000..c27a890 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/init.lua @@ -0,0 +1,11 @@ +-------------- +-- Entry point for loading all PL libraries only on demand, into the global space. +-- Requiring 'pl' means that whenever a module is implicitly accesssed +-- (e.g. `utils.split`) +-- then that module is dynamically loaded. The submodules are all brought into +-- the global space. +--Updated to use @{pl.import_into} +-- @module pl +require'pl.import_into'(_G) + +if rawget(_G,'PENLIGHT_STRICT') then require 'pl.strict' end diff --git a/Data/Libraries/Penlight/lua/pl/input.lua b/Data/Libraries/Penlight/lua/pl/input.lua new file mode 100644 index 0000000..f81de87 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/input.lua @@ -0,0 +1,171 @@ +--- Iterators for extracting words or numbers from an input source. +-- +-- require 'pl' +-- local total,n = seq.sum(input.numbers()) +-- print('average',total/n) +-- +-- _source_ is defined as a string or a file-like object (i.e. has a read() method which returns the next line) +-- +-- See @{06-data.md.Reading_Unstructured_Text_Data|here} +-- +-- Dependencies: `pl.utils` +-- @module pl.input +local strfind = string.find +local strsub = string.sub +local strmatch = string.match +local utils = require 'pl.utils' +local unpack = utils.unpack +local pairs,type,tonumber = pairs,type,tonumber +local patterns = utils.patterns +local io = io + +local input = {} + +--- create an iterator over all tokens. +-- based on allwords from PiL, 7.1 +-- @func getter any function that returns a line of text +-- @string pattern +-- @string[opt] fn Optionally can pass a function to process each token as it's found. +-- @return an iterator +function input.alltokens (getter,pattern,fn) + local line = getter() -- current line + local pos = 1 -- current position in the line + return function () -- iterator function + while line do -- repeat while there are lines + local s, e = strfind(line, pattern, pos) + if s then -- found a word? + pos = e + 1 -- next position is after this token + local res = strsub(line, s, e) -- return the token + if fn then res = fn(res) end + return res + else + line = getter() -- token not found; try next line + pos = 1 -- restart from first position + end + end + return nil -- no more lines: end of traversal + end +end +local alltokens = input.alltokens + +-- question: shd this _split_ a string containing line feeds? + +--- create a function which grabs the next value from a source. If the source is a string, then the getter +-- will return the string and thereafter return nil. If not specified then the source is assumed to be stdin. +-- @param f a string or a file-like object (i.e. has a read() method which returns the next line) +-- @return a getter function +function input.create_getter(f) + if f then + if type(f) == 'string' then + local ls = utils.split(f,'\n') + local i,n = 0,#ls + return function() + i = i + 1 + if i > n then return nil end + return ls[i] + end + else + -- anything that supports the read() method! + if not f.read then error('not a file-like object') end + return function() return f:read() end + end + else + return io.read -- i.e. just read from stdin + end +end + +--- generate a sequence of numbers from a source. +-- @param f A source +-- @return An iterator +function input.numbers(f) + return alltokens(input.create_getter(f), + '('..patterns.FLOAT..')',tonumber) +end + +--- generate a sequence of words from a source. +-- @param f A source +-- @return An iterator +function input.words(f) + return alltokens(input.create_getter(f),"%w+") +end + +local function apply_tonumber (no_fail,...) + local args = {...} + for i = 1,#args do + local n = tonumber(args[i]) + if n == nil then + if not no_fail then return nil,args[i] end + else + args[i] = n + end + end + return args +end + +--- parse an input source into fields. +-- By default, will fail if it cannot convert a field to a number. +-- @param ids a list of field indices, or a maximum field index +-- @string delim delimiter to parse fields (default space) +-- @param f a source @see create_getter +-- @tab opts option table, `{no_fail=true}` +-- @return an iterator with the field values +-- @usage for x,y in fields {2,3} do print(x,y) end -- 2nd and 3rd fields from stdin +function input.fields (ids,delim,f,opts) + local sep + local s + local getter = input.create_getter(f) + local no_fail = opts and opts.no_fail + local no_convert = opts and opts.no_convert + if not delim or delim == ' ' then + delim = '%s' + sep = '%s+' + s = '%s*' + else + sep = delim + s = '' + end + local max_id = 0 + if type(ids) == 'table' then + for i,id in pairs(ids) do + if id > max_id then max_id = id end + end + else + max_id = ids + ids = {} + for i = 1,max_id do ids[#ids+1] = i end + end + local pat = '[^'..delim..']*' + local k = 1 + for i = 1,max_id do + if ids[k] == i then + k = k + 1 + s = s..'('..pat..')' + else + s = s..pat + end + if i < max_id then + s = s..sep + end + end + local linecount = 1 + return function() + local line,results,err + repeat + line = getter() + linecount = linecount + 1 + if not line then return nil end + if no_convert then + results = {strmatch(line,s)} + else + results,err = apply_tonumber(no_fail,strmatch(line,s)) + if not results then + utils.quit("line "..(linecount-1)..": cannot convert '"..err.."' to number") + end + end + until #results > 0 + return unpack(results) + end +end + +return input + diff --git a/Data/Libraries/Penlight/lua/pl/lapp.lua b/Data/Libraries/Penlight/lua/pl/lapp.lua new file mode 100644 index 0000000..56d1d9d --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/lapp.lua @@ -0,0 +1,451 @@ +--- Simple command-line parsing using human-readable specification. +-- Supports GNU-style parameters. +-- +-- lapp = require 'pl.lapp' +-- local args = lapp [[ +-- Does some calculations +-- -o,--offset (default 0.0) Offset to add to scaled number +-- -s,--scale (number) Scaling factor +-- <number> (number) Number to be scaled +-- ]] +-- +-- print(args.offset + args.scale * args.number) +-- +-- Lines beginning with `'-'` are flags; there may be a short and a long name; +-- lines beginning with `'<var>'` are arguments. Anything in parens after +-- the flag/argument is either a default, a type name or a range constraint. +-- +-- See @{08-additional.md.Command_line_Programs_with_Lapp|the Guide} +-- +-- Dependencies: `pl.sip` +-- @module pl.lapp + +local status,sip = pcall(require,'pl.sip') +if not status then + sip = require 'sip' +end +local match = sip.match_at_start +local append,tinsert = table.insert,table.insert + +sip.custom_pattern('X','(%a[%w_%-]*)') + +local function lines(s) return s:gmatch('([^\n]*)\n') end +local function lstrip(str) return str:gsub('^%s+','') end +local function strip(str) return lstrip(str):gsub('%s+$','') end +local function at(s,k) return s:sub(k,k) end + +local lapp = {} + +local open_files,parms,aliases,parmlist,usage,script + +lapp.callback = false -- keep Strict happy + +local filetypes = { + stdin = {io.stdin,'file-in'}, stdout = {io.stdout,'file-out'}, + stderr = {io.stderr,'file-out'} +} + +--- controls whether to dump usage on error. +-- Defaults to true +lapp.show_usage_error = true + +--- quit this script immediately. +-- @string msg optional message +-- @bool no_usage suppress 'usage' display +function lapp.quit(msg,no_usage) + if no_usage == 'throw' then + error(msg) + end + if msg then + io.stderr:write(msg..'\n\n') + end + if not no_usage then + io.stderr:write(usage) + end + os.exit(1) +end + +--- print an error to stderr and quit. +-- @string msg a message +-- @bool no_usage suppress 'usage' display +function lapp.error(msg,no_usage) + if not lapp.show_usage_error then + no_usage = true + elseif lapp.show_usage_error == 'throw' then + no_usage = 'throw' + end + lapp.quit(script..': '..msg,no_usage) +end + +--- open a file. +-- This will quit on error, and keep a list of file objects for later cleanup. +-- @string file filename +-- @string[opt] opt same as second parameter of `io.open` +function lapp.open (file,opt) + local val,err = io.open(file,opt) + if not val then lapp.error(err,true) end + append(open_files,val) + return val +end + +--- quit if the condition is false. +-- @bool condn a condition +-- @string msg message text +function lapp.assert(condn,msg) + if not condn then + lapp.error(msg) + end +end + +local function range_check(x,min,max,parm) + lapp.assert(min <= x and max >= x,parm..' out of range') +end + +local function xtonumber(s) + local val = tonumber(s) + if not val then lapp.error("unable to convert to number: "..s) end + return val +end + +local types = {} + +local builtin_types = {string=true,number=true,['file-in']='file',['file-out']='file',boolean=true} + +local function convert_parameter(ps,val) + if ps.converter then + val = ps.converter(val) + end + if ps.type == 'number' then + val = xtonumber(val) + elseif builtin_types[ps.type] == 'file' then + val = lapp.open(val,(ps.type == 'file-in' and 'r') or 'w' ) + elseif ps.type == 'boolean' then + return val + end + if ps.constraint then + ps.constraint(val) + end + return val +end + +--- add a new type to Lapp. These appear in parens after the value like +-- a range constraint, e.g. '<ival> (integer) Process PID' +-- @string name name of type +-- @param converter either a function to convert values, or a Lua type name. +-- @func[opt] constraint optional function to verify values, should use lapp.error +-- if failed. +function lapp.add_type (name,converter,constraint) + types[name] = {converter=converter,constraint=constraint} +end + +local function force_short(short) + lapp.assert(#short==1,short..": short parameters should be one character") +end + +-- deducing type of variable from default value; +local function process_default (sval,vtype) + local val, success + if not vtype or vtype == 'number' then + val = tonumber(sval) + end + if val then -- we have a number! + return val,'number' + elseif filetypes[sval] then + local ft = filetypes[sval] + return ft[1],ft[2] + else + if sval == 'true' and not vtype then + return true, 'boolean' + end + if sval:match '^["\']' then sval = sval:sub(2,-2) end + + local ps = types[vtype] or {} + ps.type = vtype + + local show_usage_error = lapp.show_usage_error + lapp.show_usage_error = "throw" + success, val = pcall(convert_parameter, ps, sval) + lapp.show_usage_error = show_usage_error + if success then + return val, vtype or 'string' + end + + return sval,vtype or 'string' + end +end + +--- process a Lapp options string. +-- Usually called as `lapp()`. +-- @string str the options text +-- @tparam {string} args a table of arguments (default is `_G.arg`) +-- @return a table with parameter-value pairs +function lapp.process_options_string(str,args) + local results = {} + local varargs + local arg = args or _G.arg + open_files = {} + parms = {} + aliases = {} + parmlist = {} + + local function check_varargs(s) + local res,cnt = s:gsub('^%.%.%.%s*','') + return res, (cnt > 0) + end + + local function set_result(ps,parm,val) + parm = type(parm) == "string" and parm:gsub("%W", "_") or parm -- so foo-bar becomes foo_bar in Lua + if not ps.varargs then + results[parm] = val + else + if not results[parm] then + results[parm] = { val } + else + append(results[parm],val) + end + end + end + + usage = str + + for _,a in ipairs(arg) do + if a == "-h" or a == "--help" then + return lapp.quit() + end + end + + + for line in lines(str) do + local res = {} + local optparm,defval,vtype,constraint,rest + line = lstrip(line) + local function check(str) + return match(str,line,res) + end + + -- flags: either '-<short>', '-<short>,--<long>' or '--<long>' + if check '-$v{short}, --$o{long} $' or check '-$v{short} $' or check '--$o{long} $' then + if res.long then + optparm = res.long:gsub('[^%w%-]','_') -- I'm not sure the $o pattern will let anything else through? + if #res.rest == 1 then optparm = optparm .. res.rest end + if res.short then aliases[res.short] = optparm end + else + optparm = res.short + end + if res.short and not lapp.slack then force_short(res.short) end + res.rest, varargs = check_varargs(res.rest) + elseif check '$<{name} $' then -- is it <parameter_name>? + -- so <input file...> becomes input_file ... + optparm,rest = res.name:match '([^%.]+)(.*)' + optparm = optparm:gsub('%A','_') + varargs = rest == '...' + append(parmlist,optparm) + end + -- this is not a pure doc line and specifies the flag/parameter type + if res.rest then + line = res.rest + res = {} + local optional + -- do we have ([optional] [<type>] [default <val>])? + if match('$({def} $',line,res) or match('$({def}',line,res) then + local typespec = strip(res.def) + local ftype, rest = typespec:match('^(%S+)(.*)$') + rest = strip(rest) + if ftype == 'optional' then + ftype, rest = rest:match('^(%S+)(.*)$') + rest = strip(rest) + optional = true + end + local default + if ftype == 'default' then + default = true + if rest == '' then lapp.error("value must follow default") end + else -- a type specification + if match('$f{min}..$f{max}',ftype,res) then + -- a numerical range like 1..10 + local min,max = res.min,res.max + vtype = 'number' + constraint = function(x) + range_check(x,min,max,optparm) + end + elseif not ftype:match '|' then -- plain type + vtype = ftype + else + -- 'enum' type is a string which must belong to + -- one of several distinct values + local enums = ftype + local enump = '|' .. enums .. '|' + vtype = 'string' + constraint = function(s) + lapp.assert(enump:match('|'..s..'|'), + "value '"..s.."' not in "..enums + ) + end + end + end + res.rest = rest + typespec = res.rest + -- optional 'default value' clause. Type is inferred as + -- 'string' or 'number' if there's no explicit type + if default or match('default $r{rest}',typespec,res) then + defval,vtype = process_default(res.rest,vtype) + end + else -- must be a plain flag, no extra parameter required + defval = false + vtype = 'boolean' + end + local ps = { + type = vtype, + defval = defval, + required = defval == nil and not optional, + comment = res.rest or optparm, + constraint = constraint, + varargs = varargs + } + varargs = nil + if types[vtype] then + local converter = types[vtype].converter + if type(converter) == 'string' then + ps.type = converter + else + ps.converter = converter + end + ps.constraint = types[vtype].constraint + elseif not builtin_types[vtype] and vtype then + lapp.error(vtype.." is unknown type") + end + parms[optparm] = ps + end + end + -- cool, we have our parms, let's parse the command line args + local iparm = 1 + local iextra = 1 + local i = 1 + local parm,ps,val + local end_of_flags = false + + local function check_parm (parm) + local eqi = parm:find '[=:]' + if eqi then + tinsert(arg,i+1,parm:sub(eqi+1)) + parm = parm:sub(1,eqi-1) + end + return parm,eqi + end + + local function is_flag (parm) + return parms[aliases[parm] or parm] + end + + while i <= #arg do + local theArg = arg[i] + local res = {} + -- after '--' we don't parse args and they end up in + -- the array part of the result (args[1] etc) + if theArg == '--' then + end_of_flags = true + iparm = #parmlist + 1 + i = i + 1 + theArg = arg[i] + if not theArg then + break + end + end + -- look for a flag, -<short flags> or --<long flag> + if not end_of_flags and (match('--$S{long}',theArg,res) or match('-$S{short}',theArg,res)) then + if res.long then -- long option + parm = check_parm(res.long) + elseif #res.short == 1 or is_flag(res.short) then + parm = res.short + else + local parmstr,eq = check_parm(res.short) + if not eq then + parm = at(parmstr,1) + local flag = is_flag(parm) + if flag and flag.type ~= 'boolean' then + --if isdigit(at(parmstr,2)) then + -- a short option followed by a digit is an exception (for AW;)) + -- push ahead into the arg array + tinsert(arg,i+1,parmstr:sub(2)) + else + -- push multiple flags into the arg array! + for k = 2,#parmstr do + tinsert(arg,i+k-1,'-'..at(parmstr,k)) + end + end + else + parm = parmstr + end + end + if aliases[parm] then parm = aliases[parm] end + if not parms[parm] and (parm == 'h' or parm == 'help') then + lapp.quit() + end + else -- a parameter + parm = parmlist[iparm] + if not parm then + -- extra unnamed parameters are indexed starting at 1 + parm = iextra + ps = { type = 'string' } + parms[parm] = ps + iextra = iextra + 1 + else + ps = parms[parm] + end + if not ps.varargs then + iparm = iparm + 1 + end + val = theArg + end + ps = parms[parm] + if not ps then lapp.error("unrecognized parameter: "..parm) end + if ps.type ~= 'boolean' then -- we need a value! This should follow + if not val then + i = i + 1 + val = arg[i] + theArg = val + end + lapp.assert(val,parm.." was expecting a value") + else -- toggle boolean flags (usually false -> true) + val = not ps.defval + end + ps.used = true + val = convert_parameter(ps,val) + set_result(ps,parm,val) + if builtin_types[ps.type] == 'file' then + set_result(ps,parm..'_name',theArg) + end + if lapp.callback then + lapp.callback(parm,theArg,res) + end + i = i + 1 + val = nil + end + -- check unused parms, set defaults and check if any required parameters were missed + for parm,ps in pairs(parms) do + if not ps.used then + if ps.required then lapp.error("missing required parameter: "..parm) end + set_result(ps,parm,ps.defval) + end + end + return results +end + +if arg then + script = arg[0] + script = script or rawget(_G,"LAPP_SCRIPT") or "unknown" + -- strip dir and extension to get current script name + script = script:gsub('.+[\\/]',''):gsub('%.%a+$','') +else + script = "inter" +end + + +setmetatable(lapp, { + __call = function(tbl,str,args) return lapp.process_options_string(str,args) end, +}) + + +return lapp + + diff --git a/Data/Libraries/Penlight/lua/pl/lexer.lua b/Data/Libraries/Penlight/lua/pl/lexer.lua new file mode 100644 index 0000000..9219716 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/lexer.lua @@ -0,0 +1,515 @@ +--- Lexical scanner for creating a sequence of tokens from text. +-- `lexer.scan(s)` returns an iterator over all tokens found in the +-- string `s`. This iterator returns two values, a token type string +-- (such as 'string' for quoted string, 'iden' for identifier) and the value of the +-- token. +-- +-- Versions specialized for Lua and C are available; these also handle block comments +-- and classify keywords as 'keyword' tokens. For example: +-- +-- > s = 'for i=1,n do' +-- > for t,v in lexer.lua(s) do print(t,v) end +-- keyword for +-- iden i +-- = = +-- number 1 +-- , , +-- iden n +-- keyword do +-- +-- See the Guide for further @{06-data.md.Lexical_Scanning|discussion} +-- @module pl.lexer + +local strfind = string.find +local strsub = string.sub +local append = table.insert + + +local function assert_arg(idx,val,tp) + if type(val) ~= tp then + error("argument "..idx.." must be "..tp, 2) + end +end + +local lexer = {} + +local NUMBER1 = '^[%+%-]?%d+%.?%d*[eE][%+%-]?%d+' +local NUMBER1a = '^[%+%-]?%d*%.%d+[eE][%+%-]?%d+' +local NUMBER2 = '^[%+%-]?%d+%.?%d*' +local NUMBER2a = '^[%+%-]?%d*%.%d+' +local NUMBER3 = '^0x[%da-fA-F]+' +local NUMBER4 = '^%d+%.?%d*[eE][%+%-]?%d+' +local NUMBER4a = '^%d*%.%d+[eE][%+%-]?%d+' +local NUMBER5 = '^%d+%.?%d*' +local NUMBER5a = '^%d*%.%d+' +local IDEN = '^[%a_][%w_]*' +local WSPACE = '^%s+' +local STRING1 = "^(['\"])%1" -- empty string +local STRING2 = [[^(['"])(\*)%2%1]] +local STRING3 = [[^(['"]).-[^\](\*)%2%1]] +local CHAR1 = "^''" +local CHAR2 = [[^'(\*)%1']] +local CHAR3 = [[^'.-[^\](\*)%1']] +local PREPRO = '^#.-[^\\]\n' + +local plain_matches,lua_matches,cpp_matches,lua_keyword,cpp_keyword + +local function tdump(tok) + return tok,tok +end + +local function ndump(tok,options) + if options and options.number then + tok = tonumber(tok) + end + return "number",tok +end + +-- regular strings, single or double quotes; usually we want them +-- without the quotes +local function sdump(tok,options) + if options and options.string then + tok = tok:sub(2,-2) + end + return "string",tok +end + +-- long Lua strings need extra work to get rid of the quotes +local function sdump_l(tok,options,findres) + if options and options.string then + local quotelen = 3 + if findres[3] then + quotelen = quotelen + findres[3]:len() + end + tok = tok:sub(quotelen, -quotelen) + if tok:sub(1, 1) == "\n" then + tok = tok:sub(2) + end + end + return "string",tok +end + +local function chdump(tok,options) + if options and options.string then + tok = tok:sub(2,-2) + end + return "char",tok +end + +local function cdump(tok) + return "comment",tok +end + +local function wsdump (tok) + return "space",tok +end + +local function pdump (tok) + return "prepro",tok +end + +local function plain_vdump(tok) + return "iden",tok +end + +local function lua_vdump(tok) + if lua_keyword[tok] then + return "keyword",tok + else + return "iden",tok + end +end + +local function cpp_vdump(tok) + if cpp_keyword[tok] then + return "keyword",tok + else + return "iden",tok + end +end + +--- create a plain token iterator from a string or file-like object. +-- @tparam string|file s a string or a file-like object with `:read()` method returning lines. +-- @tab matches an optional match table - array of token descriptions. +-- A token is described by a `{pattern, action}` pair, where `pattern` should match +-- token body and `action` is a function called when a token of described type is found. +-- @tab[opt] filter a table of token types to exclude, by default `{space=true}` +-- @tab[opt] options a table of options; by default, `{number=true,string=true}`, +-- which means convert numbers and strip string quotes. +function lexer.scan(s,matches,filter,options) + local file = type(s) ~= 'string' and s + filter = filter or {space=true} + options = options or {number=true,string=true} + if filter then + if filter.space then filter[wsdump] = true end + if filter.comments then + filter[cdump] = true + end + end + if not matches then + if not plain_matches then + plain_matches = { + {WSPACE,wsdump}, + {NUMBER3,ndump}, + {IDEN,plain_vdump}, + {NUMBER1,ndump}, + {NUMBER1a,ndump}, + {NUMBER2,ndump}, + {NUMBER2a,ndump}, + {STRING1,sdump}, + {STRING2,sdump}, + {STRING3,sdump}, + {'^.',tdump} + } + end + matches = plain_matches + end + + local line_nr = 0 + local next_line = file and file:read() + local sz = file and 0 or #s + local idx = 1 + + local tlist_i + local tlist + + local first_hit = true + + local function iter(res) + local tp = type(res) + + if tlist then -- returning the inserted token list + local cur = tlist[tlist_i] + if cur then + tlist_i = tlist_i + 1 + return cur[1], cur[2] + else + tlist = nil + end + end + + if tp == 'string' then -- search up to some special pattern + local i1,i2 = strfind(s,res,idx) + if i1 then + local tok = strsub(s,i1,i2) + idx = i2 + 1 + return '', tok + else + idx = sz + 1 + return '', '' + end + + elseif tp == 'table' then -- insert a token list + tlist_i = 1 + tlist = res + return '', '' + + elseif tp ~= 'nil' then -- return position + return line_nr, idx + + else -- look for next token + if first_hit then + if not file then line_nr = 1 end + first_hit = false + end + + if idx > sz then + if file then + if not next_line then + return -- past the end of file, done + end + s = next_line + line_nr = line_nr + 1 + next_line = file:read() + if next_line then + s = s .. '\n' + end + idx, sz = 1, #s + else + return -- past the end of input, done + end + end + + for _,m in ipairs(matches) do + local pat = m[1] + local fun = m[2] + local findres = {strfind(s,pat,idx)} + local i1, i2 = findres[1], findres[2] + if i1 then + local tok = strsub(s,i1,i2) + idx = i2 + 1 + local ret1, ret2 + if not (filter and filter[fun]) then + lexer.finished = idx > sz + ret1, ret2 = fun(tok, options, findres) + end + if not file and tok:find("\n") then + -- Update line number. + local _, newlines = tok:gsub("\n", {}) + line_nr = line_nr + newlines + end + if ret1 then + return ret1, ret2 -- found a match + else + return iter() -- tail-call to try again + end + end + end + end + end + + return iter +end + +local function isstring (s) + return type(s) == 'string' +end + +--- insert tokens into a stream. +-- @param tok a token stream +-- @param a1 a string is the type, a table is a token list and +-- a function is assumed to be a token-like iterator (returns type & value) +-- @string a2 a string is the value +function lexer.insert (tok,a1,a2) + if not a1 then return end + local ts + if isstring(a1) and isstring(a2) then + ts = {{a1,a2}} + elseif type(a1) == 'function' then + ts = {} + for t,v in a1() do + append(ts,{t,v}) + end + else + ts = a1 + end + tok(ts) +end + +--- get everything in a stream upto a newline. +-- @param tok a token stream +-- @return a string +function lexer.getline (tok) + local _,v = tok('.-\n') + return v +end + +--- get current line number. +-- @param tok a token stream +-- @return the line number. +-- if the input source is a file-like object, +-- also return the column. +function lexer.lineno (tok) + return tok(0) +end + +--- get the rest of the stream. +-- @param tok a token stream +-- @return a string +function lexer.getrest (tok) + local _,v = tok('.+') + return v +end + +--- get the Lua keywords as a set-like table. +-- So `res["and"]` etc would be `true`. +-- @return a table +function lexer.get_keywords () + if not lua_keyword then + lua_keyword = { + ["and"] = true, ["break"] = true, ["do"] = true, + ["else"] = true, ["elseif"] = true, ["end"] = true, + ["false"] = true, ["for"] = true, ["function"] = true, + ["if"] = true, ["in"] = true, ["local"] = true, ["nil"] = true, + ["not"] = true, ["or"] = true, ["repeat"] = true, + ["return"] = true, ["then"] = true, ["true"] = true, + ["until"] = true, ["while"] = true + } + end + return lua_keyword +end + +--- create a Lua token iterator from a string or file-like object. +-- Will return the token type and value. +-- @string s the string +-- @tab[opt] filter a table of token types to exclude, by default `{space=true,comments=true}` +-- @tab[opt] options a table of options; by default, `{number=true,string=true}`, +-- which means convert numbers and strip string quotes. +function lexer.lua(s,filter,options) + filter = filter or {space=true,comments=true} + lexer.get_keywords() + if not lua_matches then + lua_matches = { + {WSPACE,wsdump}, + {NUMBER3,ndump}, + {IDEN,lua_vdump}, + {NUMBER4,ndump}, + {NUMBER4a,ndump}, + {NUMBER5,ndump}, + {NUMBER5a,ndump}, + {STRING1,sdump}, + {STRING2,sdump}, + {STRING3,sdump}, + {'^%-%-%[(=*)%[.-%]%1%]',cdump}, + {'^%-%-.-\n',cdump}, + {'^%[(=*)%[.-%]%1%]',sdump_l}, + {'^==',tdump}, + {'^~=',tdump}, + {'^<=',tdump}, + {'^>=',tdump}, + {'^%.%.%.',tdump}, + {'^%.%.',tdump}, + {'^.',tdump} + } + end + return lexer.scan(s,lua_matches,filter,options) +end + +--- create a C/C++ token iterator from a string or file-like object. +-- Will return the token type type and value. +-- @string s the string +-- @tab[opt] filter a table of token types to exclude, by default `{space=true,comments=true}` +-- @tab[opt] options a table of options; by default, `{number=true,string=true}`, +-- which means convert numbers and strip string quotes. +function lexer.cpp(s,filter,options) + filter = filter or {space=true,comments=true} + if not cpp_keyword then + cpp_keyword = { + ["class"] = true, ["break"] = true, ["do"] = true, ["sizeof"] = true, + ["else"] = true, ["continue"] = true, ["struct"] = true, + ["false"] = true, ["for"] = true, ["public"] = true, ["void"] = true, + ["private"] = true, ["protected"] = true, ["goto"] = true, + ["if"] = true, ["static"] = true, ["const"] = true, ["typedef"] = true, + ["enum"] = true, ["char"] = true, ["int"] = true, ["bool"] = true, + ["long"] = true, ["float"] = true, ["true"] = true, ["delete"] = true, + ["double"] = true, ["while"] = true, ["new"] = true, + ["namespace"] = true, ["try"] = true, ["catch"] = true, + ["switch"] = true, ["case"] = true, ["extern"] = true, + ["return"] = true,["default"] = true,['unsigned'] = true,['signed'] = true, + ["union"] = true, ["volatile"] = true, ["register"] = true,["short"] = true, + } + end + if not cpp_matches then + cpp_matches = { + {WSPACE,wsdump}, + {PREPRO,pdump}, + {NUMBER3,ndump}, + {IDEN,cpp_vdump}, + {NUMBER4,ndump}, + {NUMBER4a,ndump}, + {NUMBER5,ndump}, + {NUMBER5a,ndump}, + {CHAR1,chdump}, + {CHAR2,chdump}, + {CHAR3,chdump}, + {STRING1,sdump}, + {STRING2,sdump}, + {STRING3,sdump}, + {'^//.-\n',cdump}, + {'^/%*.-%*/',cdump}, + {'^==',tdump}, + {'^!=',tdump}, + {'^<=',tdump}, + {'^>=',tdump}, + {'^->',tdump}, + {'^&&',tdump}, + {'^||',tdump}, + {'^%+%+',tdump}, + {'^%-%-',tdump}, + {'^%+=',tdump}, + {'^%-=',tdump}, + {'^%*=',tdump}, + {'^/=',tdump}, + {'^|=',tdump}, + {'^%^=',tdump}, + {'^::',tdump}, + {'^.',tdump} + } + end + return lexer.scan(s,cpp_matches,filter,options) +end + +--- get a list of parameters separated by a delimiter from a stream. +-- @param tok the token stream +-- @string[opt=')'] endtoken end of list. Can be '\n' +-- @string[opt=','] delim separator +-- @return a list of token lists. +function lexer.get_separated_list(tok,endtoken,delim) + endtoken = endtoken or ')' + delim = delim or ',' + local parm_values = {} + local level = 1 -- used to count ( and ) + local tl = {} + local function tappend (tl,t,val) + val = val or t + append(tl,{t,val}) + end + local is_end + if endtoken == '\n' then + is_end = function(t,val) + return t == 'space' and val:find '\n' + end + else + is_end = function (t) + return t == endtoken + end + end + local token,value + while true do + token,value=tok() + if not token then return nil,'EOS' end -- end of stream is an error! + if is_end(token,value) and level == 1 then + append(parm_values,tl) + break + elseif token == '(' then + level = level + 1 + tappend(tl,'(') + elseif token == ')' then + level = level - 1 + if level == 0 then -- finished with parm list + append(parm_values,tl) + break + else + tappend(tl,')') + end + elseif token == delim and level == 1 then + append(parm_values,tl) -- a new parm + tl = {} + else + tappend(tl,token,value) + end + end + return parm_values,{token,value} +end + +--- get the next non-space token from the stream. +-- @param tok the token stream. +function lexer.skipws (tok) + local t,v = tok() + while t == 'space' do + t,v = tok() + end + return t,v +end + +local skipws = lexer.skipws + +--- get the next token, which must be of the expected type. +-- Throws an error if this type does not match! +-- @param tok the token stream +-- @string expected_type the token type +-- @bool no_skip_ws whether we should skip whitespace +function lexer.expecting (tok,expected_type,no_skip_ws) + assert_arg(1,tok,'function') + assert_arg(2,expected_type,'string') + local t,v + if no_skip_ws then + t,v = tok() + else + t,v = skipws(tok) + end + if t ~= expected_type then error ("expecting "..expected_type,2) end + return v +end + +return lexer diff --git a/Data/Libraries/Penlight/lua/pl/luabalanced.lua b/Data/Libraries/Penlight/lua/pl/luabalanced.lua new file mode 100644 index 0000000..a1f7dc6 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/luabalanced.lua @@ -0,0 +1,264 @@ +--- Extract delimited Lua sequences from strings. +-- Inspired by Damian Conway's Text::Balanced in Perl. <br/> +-- <ul> +-- <li>[1] <a href="http://lua-users.org/wiki/LuaBalanced">Lua Wiki Page</a></li> +-- <li>[2] http://search.cpan.org/dist/Text-Balanced/lib/Text/Balanced.pm</li> +-- </ul> <br/> +-- <pre class=example> +-- local lb = require "pl.luabalanced" +-- --Extract Lua expression starting at position 4. +-- print(lb.match_expression("if x^2 + x > 5 then print(x) end", 4)) +-- --> x^2 + x > 5 16 +-- --Extract Lua string starting at (default) position 1. +-- print(lb.match_string([["test\"123" .. "more"]])) +-- --> "test\"123" 12 +-- </pre> +-- (c) 2008, David Manura, Licensed under the same terms as Lua (MIT license). +-- @class module +-- @name pl.luabalanced + +local M = {} + +local assert = assert + +-- map opening brace <-> closing brace. +local ends = { ['('] = ')', ['{'] = '}', ['['] = ']' } +local begins = {}; for k,v in pairs(ends) do begins[v] = k end + + +-- Match Lua string in string <s> starting at position <pos>. +-- Returns <string>, <posnew>, where <string> is the matched +-- string (or nil on no match) and <posnew> is the character +-- following the match (or <pos> on no match). +-- Supports all Lua string syntax: "...", '...', [[...]], [=[...]=], etc. +local function match_string(s, pos) + pos = pos or 1 + local posa = pos + local c = s:sub(pos,pos) + if c == '"' or c == "'" then + pos = pos + 1 + while 1 do + pos = assert(s:find("[" .. c .. "\\]", pos), 'syntax error') + if s:sub(pos,pos) == c then + local part = s:sub(posa, pos) + return part, pos + 1 + else + pos = pos + 2 + end + end + else + local sc = s:match("^%[(=*)%[", pos) + if sc then + local _; _, pos = s:find("%]" .. sc .. "%]", pos) + assert(pos) + local part = s:sub(posa, pos) + return part, pos + 1 + else + return nil, pos + end + end +end +M.match_string = match_string + + +-- Match bracketed Lua expression, e.g. "(...)", "{...}", "[...]", "[[...]]", +-- [=[...]=], etc. +-- Function interface is similar to match_string. +local function match_bracketed(s, pos) + pos = pos or 1 + local posa = pos + local ca = s:sub(pos,pos) + if not ends[ca] then + return nil, pos + end + local stack = {} + while 1 do + pos = s:find('[%(%{%[%)%}%]\"\']', pos) + assert(pos, 'syntax error: unbalanced') + local c = s:sub(pos,pos) + if c == '"' or c == "'" then + local part; part, pos = match_string(s, pos) + assert(part) + elseif ends[c] then -- open + local mid, posb + if c == '[' then mid, posb = s:match('^%[(=*)%[()', pos) end + if mid then + pos = s:match('%]' .. mid .. '%]()', posb) + assert(pos, 'syntax error: long string not terminated') + if #stack == 0 then + local part = s:sub(posa, pos-1) + return part, pos + end + else + stack[#stack+1] = c + pos = pos + 1 + end + else -- close + assert(stack[#stack] == assert(begins[c]), 'syntax error: unbalanced') + stack[#stack] = nil + if #stack == 0 then + local part = s:sub(posa, pos) + return part, pos+1 + end + pos = pos + 1 + end + end +end +M.match_bracketed = match_bracketed + + +-- Match Lua comment, e.g. "--...\n", "--[[...]]", "--[=[...]=]", etc. +-- Function interface is similar to match_string. +local function match_comment(s, pos) + pos = pos or 1 + if s:sub(pos, pos+1) ~= '--' then + return nil, pos + end + pos = pos + 2 + local partt, post = match_string(s, pos) + if partt then + return '--' .. partt, post + end + local part; part, pos = s:match('^([^\n]*\n?)()', pos) + return '--' .. part, pos +end + + +-- Match Lua expression, e.g. "a + b * c[e]". +-- Function interface is similar to match_string. +local wordop = {['and']=true, ['or']=true, ['not']=true} +local is_compare = {['>']=true, ['<']=true, ['~']=true} +local function match_expression(s, pos) + pos = pos or 1 + local _ + local posa = pos + local lastident + local poscs, posce + while pos do + local c = s:sub(pos,pos) + if c == '"' or c == "'" or c == '[' and s:find('^[=%[]', pos+1) then + local part; part, pos = match_string(s, pos) + assert(part, 'syntax error') + elseif c == '-' and s:sub(pos+1,pos+1) == '-' then + -- note: handle adjacent comments in loop to properly support + -- backtracing (poscs/posce). + poscs = pos + while s:sub(pos,pos+1) == '--' do + local part; part, pos = match_comment(s, pos) + assert(part) + pos = s:match('^%s*()', pos) + posce = pos + end + elseif c == '(' or c == '{' or c == '[' then + _, pos = match_bracketed(s, pos) + elseif c == '=' and s:sub(pos+1,pos+1) == '=' then + pos = pos + 2 -- skip over two-char op containing '=' + elseif c == '=' and is_compare[s:sub(pos-1,pos-1)] then + pos = pos + 1 -- skip over two-char op containing '=' + elseif c:match'^[%)%}%];,=]' then + local part = s:sub(posa, pos-1) + return part, pos + elseif c:match'^[%w_]' then + local newident,newpos = s:match('^([%w_]+)()', pos) + if pos ~= posa and not wordop[newident] then -- non-first ident + local pose = ((posce == pos) and poscs or pos) - 1 + while s:match('^%s', pose) do pose = pose - 1 end + local ce = s:sub(pose,pose) + if ce:match'[%)%}\'\"%]]' or + ce:match'[%w_]' and not wordop[lastident] + then + local part = s:sub(posa, pos-1) + return part, pos + end + end + lastident, pos = newident, newpos + else + pos = pos + 1 + end + pos = s:find('[%(%{%[%)%}%]\"\';,=%w_%-]', pos) + end + local part = s:sub(posa, #s) + return part, #s+1 +end +M.match_expression = match_expression + + +-- Match name list (zero or more names). E.g. "a,b,c" +-- Function interface is similar to match_string, +-- but returns array as match. +local function match_namelist(s, pos) + pos = pos or 1 + local list = {} + while 1 do + local c = #list == 0 and '^' or '^%s*,%s*' + local item, post = s:match(c .. '([%a_][%w_]*)%s*()', pos) + if item then pos = post else break end + list[#list+1] = item + end + return list, pos +end +M.match_namelist = match_namelist + + +-- Match expression list (zero or more expressions). E.g. "a+b,b*c". +-- Function interface is similar to match_string, +-- but returns array as match. +local function match_explist(s, pos) + pos = pos or 1 + local list = {} + while 1 do + if #list ~= 0 then + local post = s:match('^%s*,%s*()', pos) + if post then pos = post else break end + end + local item; item, pos = match_expression(s, pos) + assert(item, 'syntax error') + list[#list+1] = item + end + return list, pos +end +M.match_explist = match_explist + + +-- Replace snippets of code in Lua code string <s> +-- using replacement function f(u,sin) --> sout. +-- <u> is the type of snippet ('c' = comment, 's' = string, +-- 'e' = any other code). +-- Snippet is replaced with <sout> (unless <sout> is nil or false, in +-- which case the original snippet is kept) +-- This is somewhat analogous to string.gsub . +local function gsub(s, f) + local pos = 1 + local posa = 1 + local sret = '' + while 1 do + pos = s:find('[%-\'\"%[]', pos) + if not pos then break end + if s:match('^%-%-', pos) then + local exp = s:sub(posa, pos-1) + if #exp > 0 then sret = sret .. (f('e', exp) or exp) end + local comment; comment, pos = match_comment(s, pos) + sret = sret .. (f('c', assert(comment)) or comment) + posa = pos + else + local posb = s:find('^[\'\"%[]', pos) + local str + if posb then str, pos = match_string(s, posb) end + if str then + local exp = s:sub(posa, posb-1) + if #exp > 0 then sret = sret .. (f('e', exp) or exp) end + sret = sret .. (f('s', str) or str) + posa = pos + else + pos = pos + 1 + end + end + end + local exp = s:sub(posa) + if #exp > 0 then sret = sret .. (f('e', exp) or exp) end + return sret +end +M.gsub = gsub + + +return M diff --git a/Data/Libraries/Penlight/lua/pl/operator.lua b/Data/Libraries/Penlight/lua/pl/operator.lua new file mode 100644 index 0000000..60eaffd --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/operator.lua @@ -0,0 +1,209 @@ +--- Lua operators available as functions. +-- +-- (similar to the Python module of the same name) +-- +-- There is a module field `optable` which maps the operator strings +-- onto these functions, e.g. `operator.optable['()']==operator.call` +-- +-- Operator strings like '>' and '{}' can be passed to most Penlight functions +-- expecting a function argument. +-- +-- @module pl.operator + +local strfind = string.find + +local operator = {} + +--- apply function to some arguments **()** +-- @param fn a function or callable object +-- @param ... arguments +function operator.call(fn,...) + return fn(...) +end + +--- get the indexed value from a table **[]** +-- @param t a table or any indexable object +-- @param k the key +function operator.index(t,k) + return t[k] +end + +--- returns true if arguments are equal **==** +-- @param a value +-- @param b value +function operator.eq(a,b) + return a==b +end + +--- returns true if arguments are not equal **~=** + -- @param a value +-- @param b value +function operator.neq(a,b) + return a~=b +end + +--- returns true if a is less than b **<** +-- @param a value +-- @param b value +function operator.lt(a,b) + return a < b +end + +--- returns true if a is less or equal to b **<=** +-- @param a value +-- @param b value +function operator.le(a,b) + return a <= b +end + +--- returns true if a is greater than b **>** +-- @param a value +-- @param b value +function operator.gt(a,b) + return a > b +end + +--- returns true if a is greater or equal to b **>=** +-- @param a value +-- @param b value +function operator.ge(a,b) + return a >= b +end + +--- returns length of string or table **#** +-- @param a a string or a table +function operator.len(a) + return #a +end + +--- add two values **+** +-- @param a value +-- @param b value +function operator.add(a,b) + return a+b +end + +--- subtract b from a **-** +-- @param a value +-- @param b value +function operator.sub(a,b) + return a-b +end + +--- multiply two values __*__ +-- @param a value +-- @param b value +function operator.mul(a,b) + return a*b +end + +--- divide first value by second **/** +-- @param a value +-- @param b value +function operator.div(a,b) + return a/b +end + +--- raise first to the power of second **^** +-- @param a value +-- @param b value +function operator.pow(a,b) + return a^b +end + +--- modulo; remainder of a divided by b **%** +-- @param a value +-- @param b value +function operator.mod(a,b) + return a%b +end + +--- concatenate two values (either strings or `__concat` defined) **..** +-- @param a value +-- @param b value +function operator.concat(a,b) + return a..b +end + +--- return the negative of a value **-** +-- @param a value +function operator.unm(a) + return -a +end + +--- false if value evaluates as true **not** +-- @param a value +function operator.lnot(a) + return not a +end + +--- true if both values evaluate as true **and** +-- @param a value +-- @param b value +function operator.land(a,b) + return a and b +end + +--- true if either value evaluate as true **or** +-- @param a value +-- @param b value +function operator.lor(a,b) + return a or b +end + +--- make a table from the arguments **{}** +-- @param ... non-nil arguments +-- @return a table +function operator.table (...) + return {...} +end + +--- match two strings **~**. +-- uses @{string.find} +function operator.match (a,b) + return strfind(a,b)~=nil +end + +--- the null operation. +-- @param ... arguments +-- @return the arguments +function operator.nop (...) + return ... +end + +---- Map from operator symbol to function. +-- Most of these map directly from operators; +-- But note these extras +-- +-- * __'()'__ `call` +-- * __'[]'__ `index` +-- * __'{}'__ `table` +-- * __'~'__ `match` +-- +-- @table optable +-- @field operator + operator.optable = { + ['+']=operator.add, + ['-']=operator.sub, + ['*']=operator.mul, + ['/']=operator.div, + ['%']=operator.mod, + ['^']=operator.pow, + ['..']=operator.concat, + ['()']=operator.call, + ['[]']=operator.index, + ['<']=operator.lt, + ['<=']=operator.le, + ['>']=operator.gt, + ['>=']=operator.ge, + ['==']=operator.eq, + ['~=']=operator.neq, + ['#']=operator.len, + ['and']=operator.land, + ['or']=operator.lor, + ['{}']=operator.table, + ['~']=operator.match, + ['']=operator.nop, +} + +return operator diff --git a/Data/Libraries/Penlight/lua/pl/path.lua b/Data/Libraries/Penlight/lua/pl/path.lua new file mode 100644 index 0000000..a438a9d --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/path.lua @@ -0,0 +1,575 @@ +--- Path manipulation and file queries. +-- +-- This is modelled after Python's os.path library (10.1); see @{04-paths.md|the Guide}. +-- +-- NOTE: the functions assume the paths being dealt with to originate +-- from the OS the application is running on. Windows drive letters are not +-- to be used when running on a Unix system for example. The one exception +-- is Windows paths to allow both forward and backward slashes (since Lua +-- also accepts those) +-- +-- Dependencies: `pl.utils`, `lfs` +-- @module pl.path + +-- imports and locals +local _G = _G +local sub = string.sub +local getenv = os.getenv +local tmpnam = os.tmpname +local package = package +local append, concat, remove = table.insert, table.concat, table.remove +local utils = require 'pl.utils' +local assert_string,raise = utils.assert_string,utils.raise + +local res,lfs = _G.pcall(_G.require,'lfs') +if not res then + error("pl.path requires LuaFileSystem") +end + +local attrib = lfs.attributes +local currentdir = lfs.currentdir +local link_attrib = lfs.symlinkattributes + +local path = {} + +local function err_func(name, param, err, code) + local ret = ("%s failed"):format(tostring(name)) + if param ~= nil then + ret = ret .. (" for '%s'"):format(tostring(param)) + end + ret = ret .. (": %s"):format(tostring(err)) + if code ~= nil then + ret = ret .. (" (code %s)"):format(tostring(code)) + end + return ret +end + +--- Lua iterator over the entries of a given directory. +-- Implicit link to [`luafilesystem.dir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function dir +path.dir = lfs.dir + +--- Creates a directory. +-- Implicit link to [`luafilesystem.mkdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function mkdir +path.mkdir = function(d) + local ok, err, code = lfs.mkdir(d) + if not ok then + return ok, err_func("mkdir", d, err, code), code + end + return ok, err, code +end + +--- Removes a directory. +-- Implicit link to [`luafilesystem.rmdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function rmdir +path.rmdir = function(d) + local ok, err, code = lfs.rmdir(d) + if not ok then + return ok, err_func("rmdir", d, err, code), code + end + return ok, err, code +end + +--- Gets attributes. +-- Implicit link to [`luafilesystem.attributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function attrib +path.attrib = function(d, r) + local ok, err, code = attrib(d, r) + if not ok then + return ok, err_func("attrib", d, err, code), code + end + return ok, err, code +end + +--- Get the working directory. +-- Implicit link to [`luafilesystem.currentdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function currentdir +path.currentdir = function() + local ok, err, code = currentdir() + if not ok then + return ok, err_func("currentdir", nil, err, code), code + end + return ok, err, code +end + +--- Gets symlink attributes. +-- Implicit link to [`luafilesystem.symlinkattributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function link_attrib +path.link_attrib = function(d, r) + local ok, err, code = link_attrib(d, r) + if not ok then + return ok, err_func("link_attrib", d, err, code), code + end + return ok, err, code +end + +--- Changes the working directory. +-- On Windows, if a drive is specified, it also changes the current drive. If +-- only specifying the drive, it will only switch drive, but not modify the path. +-- Implicit link to [`luafilesystem.chdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- @function chdir +path.chdir = function(d) + local ok, err, code = lfs.chdir(d) + if not ok then + return ok, err_func("chdir", d, err, code), code + end + return ok, err, code +end + +--- is this a directory? +-- @string P A file path +function path.isdir(P) + assert_string(1,P) + if P:match("\\$") then + P = P:sub(1,-2) + end + return attrib(P,'mode') == 'directory' +end + +--- is this a file? +-- @string P A file path +function path.isfile(P) + assert_string(1,P) + return attrib(P,'mode') == 'file' +end + +-- is this a symbolic link? +-- @string P A file path +function path.islink(P) + assert_string(1,P) + if link_attrib then + return link_attrib(P,'mode')=='link' + else + return false + end +end + +--- return size of a file. +-- @string P A file path +function path.getsize(P) + assert_string(1,P) + return attrib(P,'size') +end + +--- does a path exist? +-- @string P A file path +-- @return the file path if it exists (either as file, directory, socket, etc), nil otherwise +function path.exists(P) + assert_string(1,P) + return attrib(P,'mode') ~= nil and P +end + +--- Return the time of last access as the number of seconds since the epoch. +-- @string P A file path +function path.getatime(P) + assert_string(1,P) + return attrib(P,'access') +end + +--- Return the time of last modification as the number of seconds since the epoch. +-- @string P A file path +function path.getmtime(P) + assert_string(1,P) + return attrib(P,'modification') +end + +---Return the system's ctime as the number of seconds since the epoch. +-- @string P A file path +function path.getctime(P) + assert_string(1,P) + return path.attrib(P,'change') +end + + +local function at(s,i) + return sub(s,i,i) +end + +path.is_windows = utils.is_windows + +local sep, other_sep, seps +-- constant sep is the directory separator for this platform. +-- constant dirsep is the separator in the PATH environment variable +if path.is_windows then + path.sep = '\\'; other_sep = '/' + path.dirsep = ';' + seps = { ['/'] = true, ['\\'] = true } +else + path.sep = '/' + path.dirsep = ':' + seps = { ['/'] = true } +end +sep = path.sep + +--- are we running Windows? +-- @class field +-- @name path.is_windows + +--- path separator for this platform. +-- @class field +-- @name path.sep + +--- separator for PATH for this platform +-- @class field +-- @name path.dirsep + +--- given a path, return the directory part and a file part. +-- if there's no directory part, the first value will be empty +-- @string P A file path +-- @return directory part +-- @return file part +-- @usage +-- local dir, file = path.splitpath("some/dir/myfile.txt") +-- assert(dir == "some/dir") +-- assert(file == "myfile.txt") +-- +-- local dir, file = path.splitpath("some/dir/") +-- assert(dir == "some/dir") +-- assert(file == "") +-- +-- local dir, file = path.splitpath("some_dir") +-- assert(dir == "") +-- assert(file == "some_dir") +function path.splitpath(P) + assert_string(1,P) + local i = #P + local ch = at(P,i) + while i > 0 and ch ~= sep and ch ~= other_sep do + i = i - 1 + ch = at(P,i) + end + if i == 0 then + return '',P + else + return sub(P,1,i-1), sub(P,i+1) + end +end + +--- return an absolute path. +-- @string P A file path +-- @string[opt] pwd optional start path to use (default is current dir) +function path.abspath(P,pwd) + assert_string(1,P) + if pwd then assert_string(2,pwd) end + local use_pwd = pwd ~= nil + if not use_pwd and not currentdir() then return P end + P = P:gsub('[\\/]$','') + pwd = pwd or currentdir() + if not path.isabs(P) then + P = path.join(pwd,P) + elseif path.is_windows and not use_pwd and at(P,2) ~= ':' and at(P,2) ~= '\\' then + P = pwd:sub(1,2)..P -- attach current drive to path like '\\fred.txt' + end + return path.normpath(P) +end + +--- given a path, return the root part and the extension part. +-- if there's no extension part, the second value will be empty +-- @string P A file path +-- @treturn string root part (everything upto the "."", maybe empty) +-- @treturn string extension part (including the ".", maybe empty) +-- @usage +-- local file_path, ext = path.splitext("/bonzo/dog_stuff/cat.txt") +-- assert(file_path == "/bonzo/dog_stuff/cat") +-- assert(ext == ".txt") +-- +-- local file_path, ext = path.splitext("") +-- assert(file_path == "") +-- assert(ext == "") +function path.splitext(P) + assert_string(1,P) + local i = #P + local ch = at(P,i) + while i > 0 and ch ~= '.' do + if seps[ch] then + return P,'' + end + i = i - 1 + ch = at(P,i) + end + if i == 0 then + return P,'' + else + return sub(P,1,i-1),sub(P,i) + end +end + +--- return the directory part of a path +-- @string P A file path +-- @treturn string everything before the last dir-separator +-- @see splitpath +-- @usage +-- path.dirname("/some/path/file.txt") -- "/some/path" +-- path.dirname("file.txt") -- "" (empty string) +function path.dirname(P) + assert_string(1,P) + local p1 = path.splitpath(P) + return p1 +end + +--- return the file part of a path +-- @string P A file path +-- @treturn string +-- @see splitpath +-- @usage +-- path.basename("/some/path/file.txt") -- "file.txt" +-- path.basename("/some/path/file/") -- "" (empty string) +function path.basename(P) + assert_string(1,P) + local _,p2 = path.splitpath(P) + return p2 +end + +--- get the extension part of a path. +-- @string P A file path +-- @treturn string +-- @see splitext +-- @usage +-- path.extension("/some/path/file.txt") -- ".txt" +-- path.extension("/some/path/file_txt") -- "" (empty string) +function path.extension(P) + assert_string(1,P) + local _,p2 = path.splitext(P) + return p2 +end + +--- is this an absolute path? +-- @string P A file path +-- @usage +-- path.isabs("hello/path") -- false +-- path.isabs("/hello/path") -- true +-- -- Windows; +-- path.isabs("hello\path") -- false +-- path.isabs("\hello\path") -- true +-- path.isabs("C:\hello\path") -- true +-- path.isabs("C:hello\path") -- false +function path.isabs(P) + assert_string(1,P) + if path.is_windows and at(P,2) == ":" then + return seps[at(P,3)] ~= nil + end + return seps[at(P,1)] ~= nil +end + +--- return the path resulting from combining the individual paths. +-- if the second (or later) path is absolute, we return the last absolute path (joined with any non-absolute paths following). +-- empty elements (except the last) will be ignored. +-- @string p1 A file path +-- @string p2 A file path +-- @string ... more file paths +-- @treturn string the combined path +-- @usage +-- path.join("/first","second","third") -- "/first/second/third" +-- path.join("first","second/third") -- "first/second/third" +-- path.join("/first","/second","third") -- "/second/third" +function path.join(p1,p2,...) + assert_string(1,p1) + assert_string(2,p2) + if select('#',...) > 0 then + local p = path.join(p1,p2) + local args = {...} + for i = 1,#args do + assert_string(i,args[i]) + p = path.join(p,args[i]) + end + return p + end + if path.isabs(p2) then return p2 end + local endc = at(p1,#p1) + if endc ~= path.sep and endc ~= other_sep and endc ~= "" then + p1 = p1..path.sep + end + return p1..p2 +end + +--- normalize the case of a pathname. On Unix, this returns the path unchanged, +-- for Windows it converts; +-- +-- * the path to lowercase +-- * forward slashes to backward slashes +-- @string P A file path +-- @usage path.normcase("/Some/Path/File.txt") +-- -- Windows: "\some\path\file.txt" +-- -- Others : "/Some/Path/File.txt" +function path.normcase(P) + assert_string(1,P) + if path.is_windows then + return P:gsub('/','\\'):lower() + else + return P + end +end + +--- normalize a path name. +-- `A//B`, `A/./B`, and `A/foo/../B` all become `A/B`. +-- +-- An empty path results in '.'. +-- @string P a file path +function path.normpath(P) + assert_string(1,P) + -- Split path into anchor and relative path. + local anchor = '' + if path.is_windows then + if P:match '^\\\\' then -- UNC + anchor = '\\\\' + P = P:sub(3) + elseif seps[at(P, 1)] then + anchor = '\\' + P = P:sub(2) + elseif at(P, 2) == ':' then + anchor = P:sub(1, 2) + P = P:sub(3) + if seps[at(P, 1)] then + anchor = anchor..'\\' + P = P:sub(2) + end + end + P = P:gsub('/','\\') + else + -- According to POSIX, in path start '//' and '/' are distinct, + -- but '///+' is equivalent to '/'. + if P:match '^//' and at(P, 3) ~= '/' then + anchor = '//' + P = P:sub(3) + elseif at(P, 1) == '/' then + anchor = '/' + P = P:match '^/*(.*)$' + end + end + local parts = {} + for part in P:gmatch('[^'..sep..']+') do + if part == '..' then + if #parts ~= 0 and parts[#parts] ~= '..' then + remove(parts) + else + append(parts, part) + end + elseif part ~= '.' then + append(parts, part) + end + end + P = anchor..concat(parts, sep) + if P == '' then P = '.' end + return P +end + +--- relative path from current directory or optional start point +-- @string P a path +-- @string[opt] start optional start point (default current directory) +function path.relpath (P,start) + assert_string(1,P) + if start then assert_string(2,start) end + local split,min,append = utils.split, math.min, table.insert + P = path.abspath(P,start) + start = start or currentdir() + local compare + if path.is_windows then + P = P:gsub("/","\\") + start = start:gsub("/","\\") + compare = function(v) return v:lower() end + else + compare = function(v) return v end + end + local startl, Pl = split(start,sep), split(P,sep) + local n = min(#startl,#Pl) + if path.is_windows and n > 0 and at(Pl[1],2) == ':' and Pl[1] ~= startl[1] then + return P + end + local k = n+1 -- default value if this loop doesn't bail out! + for i = 1,n do + if compare(startl[i]) ~= compare(Pl[i]) then + k = i + break + end + end + local rell = {} + for i = 1, #startl-k+1 do rell[i] = '..' end + if k <= #Pl then + for i = k,#Pl do append(rell,Pl[i]) end + end + return table.concat(rell,sep) +end + + +--- Replace a starting '~' with the user's home directory. +-- In windows, if HOME isn't set, then USERPROFILE is used in preference to +-- HOMEDRIVE HOMEPATH. This is guaranteed to be writeable on all versions of Windows. +-- @string P A file path +function path.expanduser(P) + assert_string(1,P) + if at(P,1) == '~' then + local home = getenv('HOME') + if not home then -- has to be Windows + home = getenv 'USERPROFILE' or (getenv 'HOMEDRIVE' .. getenv 'HOMEPATH') + end + return home..sub(P,2) + else + return P + end +end + + +---Return a suitable full path to a new temporary file name. +-- unlike os.tmpname(), it always gives you a writeable path (uses TEMP environment variable on Windows) +function path.tmpname () + local res = tmpnam() + -- On Windows if Lua is compiled using MSVC14 os.tmpname + -- already returns an absolute path within TEMP env variable directory, + -- no need to prepend it. + if path.is_windows and not res:find(':') then + res = getenv('TEMP')..res + end + return res +end + +--- return the largest common prefix path of two paths. +-- @string path1 a file path +-- @string path2 a file path +-- @return the common prefix (Windows: separators will be normalized, casing will be original) +function path.common_prefix (path1,path2) + assert_string(1,path1) + assert_string(2,path2) + -- get them in order! + if #path1 > #path2 then path2,path1 = path1,path2 end + local compare + if path.is_windows then + path1 = path1:gsub("/", "\\") + path2 = path2:gsub("/", "\\") + compare = function(v) return v:lower() end + else + compare = function(v) return v end + end + for i = 1,#path1 do + if compare(at(path1,i)) ~= compare(at(path2,i)) then + local cp = path1:sub(1,i-1) + if at(path1,i-1) ~= sep then + cp = path.dirname(cp) + end + return cp + end + end + if at(path2,#path1+1) ~= sep then path1 = path.dirname(path1) end + return path1 + --return '' +end + +--- return the full path where a particular Lua module would be found. +-- Both package.path and package.cpath is searched, so the result may +-- either be a Lua file or a shared library. +-- @string mod name of the module +-- @return on success: path of module, lua or binary +-- @return on error: nil, error string listing paths tried +function path.package_path(mod) + assert_string(1,mod) + local res, err1, err2 + res, err1 = package.searchpath(mod,package.path) + if res then return res,true end + res, err2 = package.searchpath(mod,package.cpath) + if res then return res,false end + return raise ('cannot find module on path\n' .. err1 .. "\n" .. err2) +end + + +---- finis ----- +return path diff --git a/Data/Libraries/Penlight/lua/pl/permute.lua b/Data/Libraries/Penlight/lua/pl/permute.lua new file mode 100644 index 0000000..ce646f4 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/permute.lua @@ -0,0 +1,196 @@ +--- Permutation operations. +-- +-- Dependencies: `pl.utils`, `pl.tablex` +-- @module pl.permute +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local copy = tablex.deepcopy +local append = table.insert +local assert_arg = utils.assert_arg + + +local permute = {} + + +--- an iterator over all order-permutations of the elements of a list. +-- Please note that the same list is returned each time, so do not keep references! +-- @param a list-like table +-- @return an iterator which provides the next permutation as a list +function permute.order_iter(a) + assert_arg(1,a,'table') + + local t = #a + local stack = { 1 } + local function iter() + local h = #stack + local n = t - h + 1 + + local i = stack[h] + if i > t then + return + end + + if n == 0 then + table.remove(stack) + h = h - 1 + + stack[h] = stack[h] + 1 + return a + + elseif i <= n then + + -- put i-th element as the last one + a[n], a[i] = a[i], a[n] + + -- generate all permutations of the other elements + table.insert(stack, 1) + + else + + table.remove(stack) + h = h - 1 + + n = n + 1 + i = stack[h] + + -- restore i-th element + a[n], a[i] = a[i], a[n] + + stack[h] = stack[h] + 1 + end + return iter() -- tail-call + end + + return iter +end + + +--- construct a table containing all the order-permutations of a list. +-- @param a list-like table +-- @return a table of tables +-- @usage permute.order_table {1,2,3} --> {{2,3,1},{3,2,1},{3,1,2},{1,3,2},{2,1,3},{1,2,3}} +function permute.order_table (a) + assert_arg(1,a,'table') + local res = {} + for t in permute.iter(a) do + append(res,copy(t)) + end + return res +end + + + +--- an iterator over all permutations of the elements of the given lists. +-- @param ... list-like tables, they are nil-safe if a length-field `n` is provided (see `utils.pack`) +-- @return an iterator which provides the next permutation as return values in the same order as the provided lists, preceeded by an index +-- @usage +-- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety +-- local bools = utils.pack(true, false) +-- local iter = permute.list_iter(strs, bools) +-- +-- print(iter()) --> 1, one, true +-- print(iter()) --> 2, nil, true +-- print(iter()) --> 3, three, true +-- print(iter()) --> 4, one, false +-- print(iter()) --> 5, nil, false +-- print(iter()) --> 6, three, false +function permute.list_iter(...) + local elements = {...} + local pointers = {} + local sizes = {} + local size = #elements + for i, list in ipairs(elements) do + assert_arg(i,list,'table') + pointers[i] = 1 + sizes[i] = list.n or #list + end + local count = 0 + + return function() + if pointers[size] > sizes[size] then return end -- we're done + count = count + 1 + local r = { n = #elements } + local cascade_up = true + for i = 1, size do + r[i] = elements[i][pointers[i]] + if cascade_up then + pointers[i] = pointers[i] + 1 + if pointers[i] <= sizes[i] then + -- this list is not done yet, stop cascade + cascade_up = false + else + -- this list is done + if i ~= size then + -- reset pointer + pointers[i] = 1 + end + end + end + end + return count, utils.unpack(r) + end +end + + + +--- construct a table containing all the permutations of a set of lists. +-- @param ... list-like tables, they are nil-safe if a length-field `n` is provided +-- @return a list of lists, the sub-lists have an 'n' field for nil-safety +-- @usage +-- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety +-- local bools = utils.pack(true, false) +-- local results = permute.list_table(strs, bools) +-- -- results = { +-- -- { "one, true, n = 2 } +-- -- { nil, true, n = 2 }, +-- -- { "three, true, n = 2 }, +-- -- { "one, false, n = 2 }, +-- -- { nil, false, n = 2 }, +-- -- { "three", false, n = 2 }, +-- -- } +function permute.list_table(...) + local iter = permute.list_iter(...) + local results = {} + local i = 1 + while true do + local values = utils.pack(iter()) + if values[1] == nil then return results end + for i = 1, values.n do values[i] = values[i+1] end + values.n = values.n - 1 + results[i] = values + i = i + 1 + end +end + + +-- backward compat, to be deprecated + +--- deprecated. +-- @param ... +-- @see permute.order_iter +function permute.iter(...) + utils.raise_deprecation { + source = "Penlight " .. utils._VERSION, + message = "function 'iter' was renamed to 'order_iter'", + version_removed = "2.0.0", + deprecated_after = "1.9.2", + } + + return permute.order_iter(...) +end + +--- deprecated. +-- @param ... +-- @see permute.order_iter +function permute.table(...) + utils.raise_deprecation { + source = "Penlight " .. utils._VERSION, + message = "function 'table' was renamed to 'order_table'", + version_removed = "2.0.0", + deprecated_after = "1.9.2", + } + + return permute.order_table(...) +end + +return permute diff --git a/Data/Libraries/Penlight/lua/pl/pretty.lua b/Data/Libraries/Penlight/lua/pl/pretty.lua new file mode 100644 index 0000000..09add30 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/pretty.lua @@ -0,0 +1,437 @@ +--- Pretty-printing Lua tables. +-- Also provides a sandboxed Lua table reader and +-- a function to present large numbers in human-friendly format. +-- +-- Dependencies: `pl.utils`, `pl.lexer`, `pl.stringx`, `debug` +-- @module pl.pretty + +local append = table.insert +local concat = table.concat +local mfloor, mhuge = math.floor, math.huge +local mtype = math.type +local utils = require 'pl.utils' +local lexer = require 'pl.lexer' +local debug = require 'debug' +local quote_string = require'pl.stringx'.quote_string +local assert_arg = utils.assert_arg + +local original_tostring = tostring + +-- Patch tostring to format numbers with better precision +-- and to produce cross-platform results for +-- infinite values and NaN. +local function tostring(value) + if type(value) ~= "number" then + return original_tostring(value) + elseif value ~= value then + return "NaN" + elseif value == mhuge then + return "Inf" + elseif value == -mhuge then + return "-Inf" + elseif (_VERSION ~= "Lua 5.3" or mtype(value) == "integer") and mfloor(value) == value then + return ("%d"):format(value) + else + local res = ("%.14g"):format(value) + if _VERSION == "Lua 5.3" and mtype(value) == "float" and not res:find("%.") then + -- Number is internally a float but looks like an integer. + -- Insert ".0" after first run of digits. + res = res:gsub("%d+", "%0.0", 1) + end + return res + end +end + +local pretty = {} + +local function save_global_env() + local env = {} + env.hook, env.mask, env.count = debug.gethook() + + -- env.hook is "external hook" if is a C hook function + if env.hook~="external hook" then + debug.sethook() + end + + env.string_mt = getmetatable("") + debug.setmetatable("", nil) + return env +end + +local function restore_global_env(env) + if env then + debug.setmetatable("", env.string_mt) + if env.hook~="external hook" then + debug.sethook(env.hook, env.mask, env.count) + end + end +end + +--- Read a string representation of a Lua table. +-- This function loads and runs the string as Lua code, but bails out +-- if it contains a function definition. +-- Loaded string is executed in an empty environment. +-- @string s string to read in `{...}` format, possibly with some whitespace +-- before or after the curly braces. A single line comment may be present +-- at the beginning. +-- @return a table in case of success. +-- If loading the string failed, return `nil` and error message. +-- If executing loaded string failed, return `nil` and the error it raised. +function pretty.read(s) + assert_arg(1,s,'string') + if s:find '^%s*%-%-' then -- may start with a comment.. + s = s:gsub('%-%-.-\n','') + end + if not s:find '^%s*{' then return nil,"not a Lua table" end + if s:find '[^\'"%w_]function[^\'"%w_]' then + local tok = lexer.lua(s) + for t,v in tok do + if t == 'keyword' and v == 'function' then + return nil,"cannot have functions in table definition" + end + end + end + s = 'return '..s + local chunk,err = utils.load(s,'tbl','t',{}) + if not chunk then return nil,err end + local global_env = save_global_env() + local ok,ret = pcall(chunk) + restore_global_env(global_env) + if ok then return ret + else + return nil,ret + end +end + +--- Read a Lua chunk. +-- @string s Lua code. +-- @tab[opt] env environment used to run the code, empty by default. +-- @bool[opt] paranoid abort loading if any looping constructs a found in the code +-- and disable string methods. +-- @return the environment in case of success or `nil` and syntax or runtime error +-- if something went wrong. +function pretty.load (s, env, paranoid) + env = env or {} + if paranoid then + local tok = lexer.lua(s) + for t,v in tok do + if t == 'keyword' + and (v == 'for' or v == 'repeat' or v == 'function' or v == 'goto') + then + return nil,"looping not allowed" + end + end + end + local chunk,err = utils.load(s,'tbl','t',env) + if not chunk then return nil,err end + local global_env = paranoid and save_global_env() + local ok,err = pcall(chunk) + restore_global_env(global_env) + if not ok then return nil,err end + return env +end + +local function quote_if_necessary (v) + if not v then return '' + else + --AAS + if v:find ' ' then v = quote_string(v) end + end + return v +end + +local keywords + +local function is_identifier (s) + return type(s) == 'string' and s:find('^[%a_][%w_]*$') and not keywords[s] +end + +local function quote (s) + if type(s) == 'table' then + return pretty.write(s,'') + else + --AAS + return quote_string(s)-- ('%q'):format(tostring(s)) + end +end + +local function index (numkey,key) + --AAS + if not numkey then + key = quote(key) + key = key:find("^%[") and (" " .. key .. " ") or key + end + return '['..key..']' +end + + +--- Create a string representation of a Lua table. +-- This function never fails, but may complain by returning an +-- extra value. Normally puts out one item per line, using +-- the provided indent; set the second parameter to an empty string +-- if you want output on one line. +-- +-- *NOTE:* this is NOT a serialization function, not a full blown +-- debug function. Checkout out respectively the +-- [serpent](https://github.com/pkulchenko/serpent) +-- or [inspect](https://github.com/kikito/inspect.lua) +-- Lua modules for that if you need them. +-- @tab tbl Table to serialize to a string. +-- @string[opt] space The indent to use. +-- Defaults to two spaces; pass an empty string for no indentation. +-- @bool[opt] not_clever Pass `true` for plain output, e.g `{['key']=1}`. +-- Defaults to `false`. +-- @return a string +-- @return an optional error message +function pretty.write (tbl,space,not_clever) + if type(tbl) ~= 'table' then + local res = tostring(tbl) + if type(tbl) == 'string' then return quote(tbl) end + return res, 'not a table' + end + if not keywords then + keywords = lexer.get_keywords() + end + local set = ' = ' + if space == '' then set = '=' end + space = space or ' ' + local lines = {} + local line = '' + local tables = {} + + + local function put(s) + if #s > 0 then + line = line..s + end + end + + local function putln (s) + if #line > 0 then + line = line..s + append(lines,line) + line = '' + else + append(lines,s) + end + end + + local function eat_last_comma () + local n = #lines + local lastch = lines[n]:sub(-1,-1) + if lastch == ',' then + lines[n] = lines[n]:sub(1,-2) + end + end + + + -- safe versions for iterators since 5.3+ honors metamethods that can throw + -- errors + local ipairs = function(t) + local i = 0 + local ok, v + local getter = function() return t[i] end + return function() + i = i + 1 + ok, v = pcall(getter) + if v == nil or not ok then return end + return i, t[i] + end + end + local pairs = function(t) + local k, v, ok + local getter = function() return next(t, k) end + return function() + ok, k, v = pcall(getter) + if not ok then return end + return k, v + end + end + + local writeit + writeit = function (t,oldindent,indent) + local tp = type(t) + if tp ~= 'string' and tp ~= 'table' then + putln(quote_if_necessary(tostring(t))..',') + elseif tp == 'string' then + -- if t:find('\n') then + -- putln('[[\n'..t..']],') + -- else + -- putln(quote(t)..',') + -- end + --AAS + putln(quote_string(t) ..",") + elseif tp == 'table' then + if tables[t] then + putln('<cycle>,') + return + end + tables[t] = true + local newindent = indent..space + putln('{') + local used = {} + if not not_clever then + for i,val in ipairs(t) do + put(indent) + writeit(val,indent,newindent) + used[i] = true + end + end + local ordered_keys = {} + for k,v in pairs(t) do + if type(k) ~= 'number' then + ordered_keys[#ordered_keys + 1] = k + end + end + table.sort(ordered_keys, function (a, b) + if type(a) == type(b) and type(a) == 'string' then + return a < b + end + return type(a) == 'boolean' or (type(b) ~= 'boolean' and type(a) == 'table') + end) + local function write_entry (key, val) + local tkey = type(key) + local numkey = tkey == 'number' + if not_clever then + key = tostring(key) + put(indent..index(numkey,key)..set) + writeit(val,indent,newindent) + else + if not numkey or not used[key] then -- non-array indices + if tkey ~= 'string' then + key = tostring(key) + end + if numkey or not is_identifier(key) then + key = index(numkey,key) + end + put(indent..key..set) + writeit(val,indent,newindent) + end + end + end + for i = 1, #ordered_keys do + local key = ordered_keys[i] + local val = t[key] + write_entry(key, val) + end + for key,val in pairs(t) do + if type(key) == 'number' then + write_entry(key, val) + end + end + tables[t] = nil + eat_last_comma() + putln(oldindent..'},') + else + putln(tostring(t)..',') + end + end + writeit(tbl,'',space) + eat_last_comma() + return concat(lines,#space > 0 and '\n' or '') +end + +--- Dump a Lua table out to a file or stdout. +-- @tab t The table to write to a file or stdout. +-- @string[opt] filename File name to write too. Defaults to writing +-- to stdout. +function pretty.dump (t, filename) + if not filename then + print(pretty.write(t)) + return true + else + return utils.writefile(filename, pretty.write(t)) + end +end + +--- Dump a series of arguments to stdout for debug purposes. +-- This function is attached to the module table `__call` method, to make it +-- extra easy to access. So the full: +-- +-- print(require("pl.pretty").write({...})) +-- +-- Can be shortened to: +-- +-- require"pl.pretty" (...) +-- +-- Any `nil` entries will be printed as `"<nil>"` to make them explicit. +-- @param ... the parameters to dump to stdout. +-- @usage +-- -- example debug output +-- require"pl.pretty" ("hello", nil, "world", { bye = "world", true} ) +-- +-- -- output: +-- { +-- ["arg 1"] = "hello", +-- ["arg 2"] = "<nil>", +-- ["arg 3"] = "world", +-- ["arg 4"] = { +-- true, +-- bye = "world" +-- } +-- } +function pretty.debug(...) + local n = select("#", ...) + local t = { ... } + for i = 1, n do + local value = t[i] + if value == nil then + value = "<nil>" + end + t[i] = nil + t["arg " .. i] = value + end + + print(pretty.write(t)) + return true +end + + +local memp,nump = {'B','KiB','MiB','GiB'},{'','K','M','B'} + +local function comma (val) + local thou = math.floor(val/1000) + if thou > 0 then return comma(thou)..','.. tostring(val % 1000) + else return tostring(val) end +end + +--- Format large numbers nicely for human consumption. +-- @number num a number. +-- @string[opt] kind one of `'M'` (memory in `KiB`, `MiB`, etc.), +-- `'N'` (postfixes are `'K'`, `'M'` and `'B'`), +-- or `'T'` (use commas as thousands separator), `'N'` by default. +-- @int[opt] prec number of digits to use for `'M'` and `'N'`, `1` by default. +function pretty.number (num,kind,prec) + local fmt = '%.'..(prec or 1)..'f%s' + if kind == 'T' then + return comma(num) + else + local postfixes, fact + if kind == 'M' then + fact = 1024 + postfixes = memp + else + fact = 1000 + postfixes = nump + end + local div = fact + local k = 1 + while num >= div and k <= #postfixes do + div = div * fact + k = k + 1 + end + div = div / fact + if k > #postfixes then k = k - 1; div = div/fact end + if k > 1 then + return fmt:format(num/div,postfixes[k] or 'duh') + else + return num..postfixes[1] + end + end +end + +return setmetatable(pretty, { + __call = function(self, ...) + return self.debug(...) + end +}) diff --git a/Data/Libraries/Penlight/lua/pl/seq.lua b/Data/Libraries/Penlight/lua/pl/seq.lua new file mode 100644 index 0000000..1c08d20 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/seq.lua @@ -0,0 +1,544 @@ +--- Manipulating iterators as sequences. +-- See @{07-functional.md.Sequences|The Guide} +-- +-- Dependencies: `pl.utils`, `pl.types`, `debug` +-- @module pl.seq + +local next,assert,pairs,tonumber,type,setmetatable = next,assert,pairs,tonumber,type,setmetatable +local strfind,format = string.find,string.format +local mrandom = math.random +local tsort,tappend = table.sort,table.insert +local io = io +local utils = require 'pl.utils' +local callable = require 'pl.types'.is_callable +local function_arg = utils.function_arg +local assert_arg = utils.assert_arg +local debug = require 'debug' + +local seq = {} + +-- given a number, return a function(y) which returns true if y > x +-- @param x a number +function seq.greater_than(x) + return function(v) + return tonumber(v) > x + end +end + +-- given a number, returns a function(y) which returns true if y < x +-- @param x a number +function seq.less_than(x) + return function(v) + return tonumber(v) < x + end +end + +-- given any value, return a function(y) which returns true if y == x +-- @param x a value +function seq.equal_to(x) + if type(x) == "number" then + return function(v) + return tonumber(v) == x + end + else + return function(v) + return v == x + end + end +end + +--- given a string, return a function(y) which matches y against the string. +-- @param s a string +function seq.matching(s) + return function(v) + return strfind(v,s) + end +end + +local nexti + +--- sequence adaptor for a table. Note that if any generic function is +-- passed a table, it will automatically use seq.list() +-- @param t a list-like table +-- @usage sum(list(t)) is the sum of all elements of t +-- @usage for x in list(t) do...end +function seq.list(t) + assert_arg(1,t,'table') + if not nexti then + nexti = ipairs{} + end + local key,value = 0 + return function() + key,value = nexti(t,key) + return value + end +end + +--- return the keys of the table. +-- @param t an arbitrary table +-- @return iterator over keys +function seq.keys(t) + assert_arg(1,t,'table') + local key + return function() + key = next(t,key) + return key + end +end + +local list = seq.list +local function default_iter(iter) + if type(iter) == 'table' then return list(iter) + else return iter end +end + +seq.iter = default_iter + +--- create an iterator over a numerical range. Like the standard Python function xrange. +-- @param start a number +-- @param finish a number greater than start +function seq.range(start,finish) + local i = start - 1 + return function() + i = i + 1 + if i > finish then return nil + else return i end + end +end + +-- count the number of elements in the sequence which satisfy the predicate +-- @param iter a sequence +-- @param condn a predicate function (must return either true or false) +-- @param optional argument to be passed to predicate as second argument. +-- @return count +function seq.count(iter,condn,arg) + local i = 0 + seq.foreach(iter,function(val) + if condn(val,arg) then i = i + 1 end + end) + return i +end + +--- return the minimum and the maximum value of the sequence. +-- @param iter a sequence +-- @return minimum value +-- @return maximum value +function seq.minmax(iter) + local vmin,vmax = 1e70,-1e70 + for v in default_iter(iter) do + v = tonumber(v) + if v < vmin then vmin = v end + if v > vmax then vmax = v end + end + return vmin,vmax +end + +--- return the sum and element count of the sequence. +-- @param iter a sequence +-- @param fn an optional function to apply to the values +function seq.sum(iter,fn) + local s = 0 + local i = 0 + for v in default_iter(iter) do + if fn then v = fn(v) end + s = s + v + i = i + 1 + end + return s,i +end + +--- create a table from the sequence. (This will make the result a List.) +-- @param iter a sequence +-- @return a List +-- @usage copy(list(ls)) is equal to ls +-- @usage copy(list {1,2,3}) == List{1,2,3} +function seq.copy(iter) + local res,k = {},1 + for v in default_iter(iter) do + res[k] = v + k = k + 1 + end + setmetatable(res, require('pl.List')) + return res +end + +--- create a table of pairs from the double-valued sequence. +-- @param iter a double-valued sequence +-- @param i1 used to capture extra iterator values +-- @param i2 as with pairs & ipairs +-- @usage copy2(ipairs{10,20,30}) == {{1,10},{2,20},{3,30}} +-- @return a list-like table +function seq.copy2 (iter,i1,i2) + local res,k = {},1 + for v1,v2 in iter,i1,i2 do + res[k] = {v1,v2} + k = k + 1 + end + return res +end + +--- create a table of 'tuples' from a multi-valued sequence. +-- A generalization of copy2 above +-- @param iter a multiple-valued sequence +-- @return a list-like table +function seq.copy_tuples (iter) + iter = default_iter(iter) + local res = {} + local row = {iter()} + while #row > 0 do + tappend(res,row) + row = {iter()} + end + return res +end + +--- return an iterator of random numbers. +-- @param n the length of the sequence +-- @param l same as the first optional argument to math.random +-- @param u same as the second optional argument to math.random +-- @return a sequence +function seq.random(n,l,u) + local rand + assert(type(n) == 'number') + if u then + rand = function() return mrandom(l,u) end + elseif l then + rand = function() return mrandom(l) end + else + rand = mrandom + end + + return function() + if n == 0 then return nil + else + n = n - 1 + return rand() + end + end +end + +--- return an iterator to the sorted elements of a sequence. +-- @param iter a sequence +-- @param comp an optional comparison function (comp(x,y) is true if x < y) +function seq.sort(iter,comp) + local t = seq.copy(iter) + tsort(t,comp) + return list(t) +end + +--- return an iterator which returns elements of two sequences. +-- @param iter1 a sequence +-- @param iter2 a sequence +-- @usage for x,y in seq.zip(ls1,ls2) do....end +function seq.zip(iter1,iter2) + iter1 = default_iter(iter1) + iter2 = default_iter(iter2) + return function() + return iter1(),iter2() + end +end + +--- Makes a table where the key/values are the values and value counts of the sequence. +-- This version works with 'hashable' values like strings and numbers. +-- `pl.tablex.count_map` is more general. +-- @param iter a sequence +-- @return a map-like table +-- @return a table +-- @see pl.tablex.count_map +function seq.count_map(iter) + local t = {} + local v + for s in default_iter(iter) do + v = t[s] + if v then t[s] = v + 1 + else t[s] = 1 end + end + return setmetatable(t, require('pl.Map')) +end + +-- given a sequence, return all the unique values in that sequence. +-- @param iter a sequence +-- @param returns_table true if we return a table, not a sequence +-- @return a sequence or a table; defaults to a sequence. +function seq.unique(iter,returns_table) + local t = seq.count_map(iter) + local res,k = {},1 + for key in pairs(t) do res[k] = key; k = k + 1 end + table.sort(res) + if returns_table then + return res + else + return list(res) + end +end + +--- print out a sequence iter with a separator. +-- @param iter a sequence +-- @param sep the separator (default space) +-- @param nfields maximum number of values per line (default 7) +-- @param fmt optional format function for each value +function seq.printall(iter,sep,nfields,fmt) + local write = io.write + if not sep then sep = ' ' end + if not nfields then + if sep == '\n' then nfields = 1e30 + else nfields = 7 end + end + if fmt then + local fstr = fmt + fmt = function(v) return format(fstr,v) end + end + local k = 1 + for v in default_iter(iter) do + if fmt then v = fmt(v) end + if k < nfields then + write(v,sep) + k = k + 1 + else + write(v,'\n') + k = 1 + end + end + write '\n' +end + +-- return an iterator running over every element of two sequences (concatenation). +-- @param iter1 a sequence +-- @param iter2 a sequence +function seq.splice(iter1,iter2) + iter1 = default_iter(iter1) + iter2 = default_iter(iter2) + local iter = iter1 + return function() + local ret = iter() + if ret == nil then + if iter == iter1 then + iter = iter2 + return iter() + else return nil end + else + return ret + end + end +end + +--- return a sequence where every element of a sequence has been transformed +-- by a function. If you don't supply an argument, then the function will +-- receive both values of a double-valued sequence, otherwise behaves rather like +-- tablex.map. +-- @param fn a function to apply to elements; may take two arguments +-- @param iter a sequence of one or two values +-- @param arg optional argument to pass to function. +function seq.map(fn,iter,arg) + fn = function_arg(1,fn) + iter = default_iter(iter) + return function() + local v1,v2 = iter() + if v1 == nil then return nil end + return fn(v1,arg or v2) or false + end +end + +--- filter a sequence using a predicate function. +-- @param iter a sequence of one or two values +-- @param pred a boolean function; may take two arguments +-- @param arg optional argument to pass to function. +function seq.filter (iter,pred,arg) + pred = function_arg(2,pred) + return function () + local v1,v2 + while true do + v1,v2 = iter() + if v1 == nil then return nil end + if pred(v1,arg or v2) then return v1,v2 end + end + end +end + +--- 'reduce' a sequence using a binary function. +-- @func fn a function of two arguments +-- @param iter a sequence +-- @param initval optional initial value +-- @usage seq.reduce(operator.add,seq.list{1,2,3,4}) == 10 +-- @usage seq.reduce('-',{1,2,3,4,5}) == -13 +function seq.reduce (fn,iter,initval) + fn = function_arg(1,fn) + iter = default_iter(iter) + local val = initval or iter() + if val == nil then return nil end + for v in iter do + val = fn(val,v) + end + return val +end + +--- take the first n values from the sequence. +-- @param iter a sequence of one or two values +-- @param n number of items to take +-- @return a sequence of at most n items +function seq.take (iter,n) + iter = default_iter(iter) + return function() + if n < 1 then return end + local val1,val2 = iter() + if not val1 then return end + n = n - 1 + return val1,val2 + end +end + +--- skip the first n values of a sequence +-- @param iter a sequence of one or more values +-- @param n number of items to skip +function seq.skip (iter,n) + n = n or 1 + for i = 1,n do + if iter() == nil then return list{} end + end + return iter +end + +--- a sequence with a sequence count and the original value. +-- enum(copy(ls)) is a roundabout way of saying ipairs(ls). +-- @param iter a single or double valued sequence +-- @return sequence of (i,v), i = 1..n and v is from iter. +function seq.enum (iter) + local i = 0 + iter = default_iter(iter) + return function () + local val1,val2 = iter() + if not val1 then return end + i = i + 1 + return i,val1,val2 + end +end + +--- map using a named method over a sequence. +-- @param iter a sequence +-- @param name the method name +-- @param arg1 optional first extra argument +-- @param arg2 optional second extra argument +function seq.mapmethod (iter,name,arg1,arg2) + iter = default_iter(iter) + return function() + local val = iter() + if not val then return end + local fn = val[name] + if not fn then error(type(val).." does not have method "..name) end + return fn(val,arg1,arg2) + end +end + +--- a sequence of (last,current) values from another sequence. +-- This will return S(i-1),S(i) if given S(i) +-- @param iter a sequence +function seq.last (iter) + iter = default_iter(iter) + local val, l = iter(), nil + if val == nil then return list{} end + return function () + val,l = iter(),val + if val == nil then return nil end + return val,l + end +end + +--- call the function on each element of the sequence. +-- @param iter a sequence with up to 3 values +-- @param fn a function +function seq.foreach(iter,fn) + fn = function_arg(2,fn) + for i1,i2,i3 in default_iter(iter) do fn(i1,i2,i3) end +end + +---------------------- Sequence Adapters --------------------- + +local SMT + +local function SW (iter,...) + if callable(iter) then + return setmetatable({iter=iter},SMT) + else + return iter,... + end +end + + +-- can't directly look these up in seq because of the wrong argument order... +local map,reduce,mapmethod = seq.map, seq.reduce, seq.mapmethod +local overrides = { + map = function(self,fun,arg) + return map(fun,self,arg) + end, + reduce = function(self,fun,initval) + return reduce(fun,self,initval) + end +} + +SMT = { + __index = function (tbl,key) + local fn = overrides[key] or seq[key] + if fn then + return function(sw,...) return SW(fn(sw.iter,...)) end + else + return function(sw,...) return SW(mapmethod(sw.iter,key,...)) end + end + end, + __call = function (sw) + return sw.iter() + end, +} + +setmetatable(seq,{ + __call = function(tbl,iter,extra) + if not callable(iter) then + if type(iter) == 'table' then iter = seq.list(iter) + else return iter + end + end + if extra then + return setmetatable({iter=function() + return iter(extra) + end},SMT) + else + return setmetatable({iter=iter},SMT) + end + end +}) + +--- create a wrapped iterator over all lines in the file. +-- @param f either a filename, file-like object, or 'STDIN' (for standard input) +-- @param ... for Lua 5.2 only, optional format specifiers, as in `io.read`. +-- @return a sequence wrapper +function seq.lines (f,...) + local iter,obj + if f == 'STDIN' then + f = io.stdin + elseif type(f) == 'string' then + iter,obj = io.lines(f,...) + elseif not f.read then + error("Pass either a string or a file-like object",2) + end + if not iter then + iter,obj = f:lines(...) + end + if obj then -- LuaJIT version returns a function operating on a file + local lines,file = iter,obj + iter = function() return lines(file) end + end + return SW(iter) +end + +function seq.import () + debug.setmetatable(function() end,{ + __index = function(tbl,key) + local s = overrides[key] or seq[key] + if s then return s + else + return function(s,...) return seq.mapmethod(s,key,...) end + end + end + }) +end + +return seq diff --git a/Data/Libraries/Penlight/lua/pl/sip.lua b/Data/Libraries/Penlight/lua/pl/sip.lua new file mode 100644 index 0000000..5d1f2aa --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/sip.lua @@ -0,0 +1,337 @@ +--- Simple Input Patterns (SIP). +-- SIP patterns start with '$', then a +-- one-letter type, and then an optional variable in curly braces. +-- +-- sip.match('$v=$q','name="dolly"',res) +-- ==> res=={'name','dolly'} +-- sip.match('($q{first},$q{second})','("john","smith")',res) +-- ==> res=={second='smith',first='john'} +-- +-- Type names: +-- +-- v identifier +-- i integer +-- f floating-point +-- q quoted string +-- ([{< match up to closing bracket +-- +-- See @{08-additional.md.Simple_Input_Patterns|the Guide} +-- +-- @module pl.sip + +local loadstring = rawget(_G,'loadstring') or load +local unpack = rawget(_G,'unpack') or rawget(table,'unpack') + +local append,concat = table.insert,table.concat +local ipairs,type = ipairs,type +local io,_G = io,_G +local print,rawget = print,rawget + +local patterns = { + FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*', + INTEGER = '[+%-%d]%d*', + IDEN = '[%a_][%w_]*', + OPTION = '[%a_][%w_%-]*', +} + +local function assert_arg(idx,val,tp) + if type(val) ~= tp then + error("argument "..idx.." must be "..tp, 2) + end +end + +local sip = {} + +local brackets = {['<'] = '>', ['('] = ')', ['{'] = '}', ['['] = ']' } +local stdclasses = {a=1,c=0,d=1,l=1,p=0,u=1,w=1,x=1,s=0} + +local function group(s) + return '('..s..')' +end + +-- escape all magic characters except $, which has special meaning +-- Also, un-escape any characters after $, so $( and $[ passes through as is. +local function escape (spec) + return (spec:gsub('[%-%.%+%[%]%(%)%^%%%?%*]','%%%0'):gsub('%$%%(%S)','$%1')) +end + +-- Most spaces within patterns can match zero or more spaces. +-- Spaces between alphanumeric characters or underscores or between +-- patterns that can match these characters, however, must match at least +-- one space. Otherwise '$v $v' would match 'abcd' as {'abc', 'd'}. +-- This function replaces continuous spaces within a pattern with either +-- '%s*' or '%s+' according to this rule. The pattern has already +-- been stripped of pattern names by now. +local function compress_spaces(patt) + return (patt:gsub("()%s+()", function(i1, i2) + local before = patt:sub(i1 - 2, i1 - 1) + if before:match('%$[vifadxlu]') or before:match('^[^%$]?[%w_]$') then + local after = patt:sub(i2, i2 + 1) + if after:match('%$[vifadxlu]') or after:match('^[%w_]') then + return '%s+' + end + end + return '%s*' + end)) +end + +local pattern_map = { + v = group(patterns.IDEN), + i = group(patterns.INTEGER), + f = group(patterns.FLOAT), + o = group(patterns.OPTION), + r = '(%S.*)', + p = '([%a]?[:]?[\\/%.%w_]+)' +} + +function sip.custom_pattern(flag,patt) + pattern_map[flag] = patt +end + +--- convert a SIP pattern into the equivalent Lua string pattern. +-- @param spec a SIP pattern +-- @param options a table; only the <code>at_start</code> field is +-- currently meaningful and ensures that the pattern is anchored +-- at the start of the string. +-- @return a Lua string pattern. +function sip.create_pattern (spec,options) + assert_arg(1,spec,'string') + local fieldnames,fieldtypes = {},{} + + if type(spec) == 'string' then + spec = escape(spec) + else + local res = {} + for i,s in ipairs(spec) do + res[i] = escape(s) + end + spec = concat(res,'.-') + end + + local kount = 1 + + local function addfield (name,type) + name = name or kount + append(fieldnames,name) + fieldtypes[name] = type + kount = kount + 1 + end + + local named_vars = spec:find('{%a+}') + + if options and options.at_start then + spec = '^'..spec + end + if spec:sub(-1,-1) == '$' then + spec = spec:sub(1,-2)..'$r' + if named_vars then spec = spec..'{rest}' end + end + + local names + + if named_vars then + names = {} + spec = spec:gsub('{(%a+)}',function(name) + append(names,name) + return '' + end) + end + spec = compress_spaces(spec) + + local k = 1 + local err + local r = (spec:gsub('%$%S',function(s) + local type,name + type = s:sub(2,2) + if names then name = names[k]; k=k+1 end + -- this kludge is necessary because %q generates two matches, and + -- we want to ignore the first. Not a problem for named captures. + if not names and type == 'q' then + addfield(nil,'Q') + else + addfield(name,type) + end + local res + if pattern_map[type] then + res = pattern_map[type] + elseif type == 'q' then + -- some Lua pattern matching voodoo; we want to match '...' as + -- well as "...", and can use the fact that %n will match a + -- previous capture. Adding the extra field above comes from needing + -- to accommodate the extra spurious match (which is either ' or ") + addfield(name,type) + res = '(["\'])(.-)%'..(kount-2) + else + local endbracket = brackets[type] + if endbracket then + res = '(%b'..type..endbracket..')' + elseif stdclasses[type] or stdclasses[type:lower()] then + res = '(%'..type..'+)' + else + err = "unknown format type or character class" + end + end + return res + end)) + + if err then + return nil,err + else + return r,fieldnames,fieldtypes + end +end + + +local function tnumber (s) + return s == 'd' or s == 'i' or s == 'f' +end + +function sip.create_spec_fun(spec,options) + local fieldtypes,fieldnames + local ls = {} + spec,fieldnames,fieldtypes = sip.create_pattern(spec,options) + if not spec then return spec,fieldnames end + local named_vars = type(fieldnames[1]) == 'string' + for i = 1,#fieldnames do + append(ls,'mm'..i) + end + ls[1] = ls[1] or "mm1" -- behave correctly if there are no patterns + local fun = ('return (function(s,res)\n\tlocal %s = s:match(%q)\n'):format(concat(ls,','),spec) + fun = fun..'\tif not mm1 then return false end\n' + local k=1 + for i,f in ipairs(fieldnames) do + if f ~= '_' then + local var = 'mm'..i + if tnumber(fieldtypes[f]) then + var = 'tonumber('..var..')' + elseif brackets[fieldtypes[f]] then + var = var..':sub(2,-2)' + end + if named_vars then + fun = ('%s\tres.%s = %s\n'):format(fun,f,var) + else + if fieldtypes[f] ~= 'Q' then -- we skip the string-delim capture + fun = ('%s\tres[%d] = %s\n'):format(fun,k,var) + k = k + 1 + end + end + end + end + return fun..'\treturn true\nend)\n', named_vars +end + +--- convert a SIP pattern into a matching function. +-- The returned function takes two arguments, the line and an empty table. +-- If the line matched the pattern, then this function returns true +-- and the table is filled with field-value pairs. +-- @param spec a SIP pattern +-- @param options optional table; {at_start=true} ensures that the pattern +-- is anchored at the start of the string. +-- @return a function if successful, or nil,error +function sip.compile(spec,options) + assert_arg(1,spec,'string') + local fun,names = sip.create_spec_fun(spec,options) + if not fun then return nil,names end + if rawget(_G,'_DEBUG') then print(fun) end + local chunk,err = loadstring(fun,'tmp') + if err then return nil,err end + return chunk(),names +end + +local cache = {} + +--- match a SIP pattern against a string. +-- @param spec a SIP pattern +-- @param line a string +-- @param res a table to receive values +-- @param options (optional) option table +-- @return true or false +function sip.match (spec,line,res,options) + assert_arg(1,spec,'string') + assert_arg(2,line,'string') + assert_arg(3,res,'table') + if not cache[spec] then + cache[spec] = sip.compile(spec,options) + end + return cache[spec](line,res) +end + +--- match a SIP pattern against the start of a string. +-- @param spec a SIP pattern +-- @param line a string +-- @param res a table to receive values +-- @return true or false +function sip.match_at_start (spec,line,res) + return sip.match(spec,line,res,{at_start=true}) +end + +--- given a pattern and a file object, return an iterator over the results +-- @param spec a SIP pattern +-- @param f a file-like object. +function sip.fields (spec,f) + assert_arg(1,spec,'string') + if not f then return nil,"no file object" end + local fun,err = sip.compile(spec) + if not fun then return nil,err end + local res = {} + return function() + while true do + local line = f:read() + if not line then return end + if fun(line,res) then + local values = res + res = {} + return unpack(values) + end + end + end +end + +local read_patterns = {} + +--- register a match which will be used in the read function. +-- @string spec a SIP pattern +-- @func fun a function to be called with the results of the match +-- @see read +function sip.pattern (spec,fun) + assert_arg(1,spec,'string') + local pat,named = sip.compile(spec) + append(read_patterns,{pat=pat,named=named,callback=fun}) +end + +--- enter a loop which applies all registered matches to the input file. +-- @param f a file-like object +-- @array matches optional list of `{spec,fun}` pairs, as for `pattern` above. +function sip.read (f,matches) + local owned,err + if not f then return nil,"no file object" end + if type(f) == 'string' then + f,err = io.open(f) + if not f then return nil,err end + owned = true + end + if matches then + for _,p in ipairs(matches) do + sip.pattern(p[1],p[2]) + end + end + local res = {} + for line in f:lines() do + for _,item in ipairs(read_patterns) do + if item.pat(line,res) then + if item.callback then + if item.named then + item.callback(res) + else + item.callback(unpack(res)) + end + end + res = {} + break + end + end + end + if owned then f:close() end +end + +return sip diff --git a/Data/Libraries/Penlight/lua/pl/strict.lua b/Data/Libraries/Penlight/lua/pl/strict.lua new file mode 100644 index 0000000..67cbf5b --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/strict.lua @@ -0,0 +1,138 @@ +--- Checks uses of undeclared global variables. +-- All global variables must be 'declared' through a regular assignment +-- (even assigning `nil` will do) in a main chunk before being used +-- anywhere or assigned to inside a function. Existing metatables `__newindex` and `__index` +-- metamethods are respected. +-- +-- You can set any table to have strict behaviour using `strict.module`. Creating a new +-- module with `strict.closed_module` makes the module immune to monkey-patching, if +-- you don't wish to encourage monkey business. +-- +-- If the global `PENLIGHT_NO_GLOBAL_STRICT` is defined, then this module won't make the +-- global environment strict - if you just want to explicitly set table strictness. +-- +-- @module pl.strict + +require 'debug' -- for Lua 5.2 +local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget +local strict = {} + +local function what () + local d = getinfo(3, "S") + return d and d.what or "C" +end + +--- make an existing table strict. +-- @string[opt] name name of table +-- @tab[opt] mod the table to protect - if `nil` then we'll return a new table +-- @tab[opt] predeclared - table of variables that are to be considered predeclared. +-- @return the given table, or a new table +-- @usage +-- local M = { hello = "world" } +-- strict.module ("Awesome_Module", M, { +-- Lua = true, -- defines allowed keys +-- }) +-- +-- assert(M.hello == "world") +-- assert(M.Lua == nil) -- access allowed, but has no value yet +-- M.Lua = "Rocks" +-- assert(M.Lua == "Rocks") +-- M.not_allowed = "bad boy" -- throws an error +function strict.module (name,mod,predeclared) + local mt, old_newindex, old_index, old_index_type, global + if predeclared then + global = predeclared.__global + end + if type(mod) == 'table' then + mt = getmetatable(mod) + if mt and rawget(mt,'__declared') then return end -- already patched... + else + mod = {} + end + if mt == nil then + mt = {} + setmetatable(mod, mt) + else + old_newindex = mt.__newindex + old_index = mt.__index + old_index_type = type(old_index) + end + mt.__declared = predeclared or {} + mt.__newindex = function(t, n, v) + if old_newindex then + old_newindex(t, n, v) + if rawget(t,n)~=nil then return end + end + if not mt.__declared[n] then + if global then + local w = what() + if w ~= "main" and w ~= "C" then + error("assign to undeclared global '"..n.."'", 2) + end + end + mt.__declared[n] = true + end + rawset(t, n, v) + end + mt.__index = function(t,n) + if not mt.__declared[n] and what() ~= "C" then + if old_index then + if old_index_type == "table" then + local fallback = old_index[n] + if fallback ~= nil then + return fallback + end + else + local res = old_index(t, n) + if res ~= nil then + return res + end + end + end + local msg = "variable '"..n.."' is not declared" + if name then + msg = msg .. " in '"..tostring(name).."'" + end + error(msg, 2) + end + return rawget(t, n) + end + return mod +end + +--- make all tables in a table strict. +-- So `strict.make_all_strict(_G)` prevents monkey-patching +-- of any global table +-- @tab T the table containing the tables to protect. Table `T` itself will NOT be protected. +function strict.make_all_strict (T) + for k,v in pairs(T) do + if type(v) == 'table' and v ~= T then + strict.module(k,v) + end + end +end + +--- make a new module table which is closed to further changes. +-- @tab mod module table +-- @string name module name +function strict.closed_module (mod,name) + -- No clue to what this is useful for? see tests + -- Deprecate this and remove??? + local M = {} + mod = mod or {} + local mt = getmetatable(mod) + if not mt then + mt = {} + setmetatable(mod,mt) + end + mt.__newindex = function(t,k,v) + M[k] = v + end + return strict.module(name,M) +end + +if not rawget(_G,'PENLIGHT_NO_GLOBAL_STRICT') then + strict.module(nil,_G,{_PROMPT=true,_PROMPT2=true,__global=true}) +end + +return strict diff --git a/Data/Libraries/Penlight/lua/pl/stringio.lua b/Data/Libraries/Penlight/lua/pl/stringio.lua new file mode 100644 index 0000000..666f415 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/stringio.lua @@ -0,0 +1,158 @@ +--- Reading and writing strings using file-like objects. <br> +-- +-- f = stringio.open(text) +-- l1 = f:read() -- read first line +-- n,m = f:read ('*n','*n') -- read two numbers +-- for line in f:lines() do print(line) end -- iterate over all lines +-- f = stringio.create() +-- f:write('hello') +-- f:write('dolly') +-- assert(f:value(),'hellodolly') +-- +-- See @{03-strings.md.File_style_I_O_on_Strings|the Guide}. +-- @module pl.stringio + +local unpack = rawget(_G,'unpack') or rawget(table,'unpack') +local tonumber = tonumber +local concat,append = table.concat,table.insert + +local stringio = {} + +-- Writer class +local SW = {} +SW.__index = SW + +local function xwrite(self,...) + local args = {...} --arguments may not be nil! + for i = 1, #args do + append(self.tbl,args[i]) + end +end + +function SW:write(arg1,arg2,...) + if arg2 then + xwrite(self,arg1,arg2,...) + else + append(self.tbl,arg1) + end +end + +function SW:writef(fmt,...) + self:write(fmt:format(...)) +end + +function SW:value() + return concat(self.tbl) +end + +function SW:__tostring() + return self:value() +end + +function SW:close() -- for compatibility only +end + +function SW:seek() +end + +-- Reader class +local SR = {} +SR.__index = SR + +function SR:_read(fmt) + local i,str = self.i,self.str + local sz = #str + if i > sz then return nil end + local res + if fmt == '*l' or fmt == '*L' then + local idx = str:find('\n',i) or (sz+1) + res = str:sub(i,fmt == '*l' and idx-1 or idx) + self.i = idx+1 + elseif fmt == '*a' then + res = str:sub(i) + self.i = sz + elseif fmt == '*n' then + local _,i2,idx + _,idx = str:find ('%s*%d+',i) + _,i2 = str:find ('^%.%d+',idx+1) + if i2 then idx = i2 end + _,i2 = str:find ('^[eE][%+%-]*%d+',idx+1) + if i2 then idx = i2 end + local val = str:sub(i,idx) + res = tonumber(val) + self.i = idx+1 + elseif type(fmt) == 'number' then + res = str:sub(i,i+fmt-1) + self.i = i + fmt + else + error("bad read format",2) + end + return res +end + +function SR:read(...) + if select('#',...) == 0 then + return self:_read('*l') + else + local res, fmts = {},{...} + for i = 1, #fmts do + res[i] = self:_read(fmts[i]) + end + return unpack(res) + end +end + +function SR:seek(whence,offset) + local base + whence = whence or 'cur' + offset = offset or 0 + if whence == 'set' then + base = 1 + elseif whence == 'cur' then + base = self.i + elseif whence == 'end' then + base = #self.str + end + self.i = base + offset + return self.i +end + +function SR:lines(...) + local n, args = select('#',...) + if n > 0 then + args = {...} + end + return function() + if n == 0 then + return self:_read '*l' + else + return self:read(unpack(args)) + end + end +end + +function SR:close() -- for compatibility only +end + +--- create a file-like object which can be used to construct a string. +-- The resulting object has an extra `value()` method for +-- retrieving the string value. Implements `file:write`, `file:seek`, `file:lines`, +-- plus an extra `writef` method which works like `utils.printf`. +-- @usage f = create(); f:write('hello, dolly\n'); print(f:value()) +function stringio.create() + return setmetatable({tbl={}},SW) +end + +--- create a file-like object for reading from a given string. +-- Implements `file:read`. +-- @string s The input string. +-- @usage fs = open '20 10'; x,y = f:read ('*n','*n'); assert(x == 20 and y == 10) +function stringio.open(s) + return setmetatable({str=s,i=1},SR) +end + +function stringio.lines(s,...) + return stringio.open(s):lines(...) +end + +return stringio diff --git a/Data/Libraries/Penlight/lua/pl/stringx.lua b/Data/Libraries/Penlight/lua/pl/stringx.lua new file mode 100644 index 0000000..6276113 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/stringx.lua @@ -0,0 +1,594 @@ +--- Python-style extended string library. +-- +-- see 3.6.1 of the Python reference. +-- If you want to make these available as string methods, then say +-- `stringx.import()` to bring them into the standard `string` table. +-- +-- See @{03-strings.md|the Guide} +-- +-- Dependencies: `pl.utils` +-- @module pl.stringx +local utils = require 'pl.utils' +local string = string +local find = string.find +local type,setmetatable,ipairs = type,setmetatable,ipairs +local error = error +local gsub = string.gsub +local rep = string.rep +local sub = string.sub +local reverse = string.reverse +local concat = table.concat +local append = table.insert +local escape = utils.escape +local ceil, max = math.ceil, math.max +local assert_arg,usplit = utils.assert_arg,utils.split +local lstrip + +local function assert_string (n,s) + assert_arg(n,s,'string') +end + +local function non_empty(s) + return #s > 0 +end + +local function assert_nonempty_string(n,s) + assert_arg(n,s,'string',non_empty,'must be a non-empty string') +end + +local function makelist(l) + return setmetatable(l, require('pl.List')) +end + +local stringx = {} + +------------------ +-- String Predicates +-- @section predicates + +--- does s only contain alphabetic characters? +-- @string s a string +function stringx.isalpha(s) + assert_string(1,s) + return find(s,'^%a+$') == 1 +end + +--- does s only contain digits? +-- @string s a string +function stringx.isdigit(s) + assert_string(1,s) + return find(s,'^%d+$') == 1 +end + +--- does s only contain alphanumeric characters? +-- @string s a string +function stringx.isalnum(s) + assert_string(1,s) + return find(s,'^%w+$') == 1 +end + +--- does s only contain spaces? +-- @string s a string +function stringx.isspace(s) + assert_string(1,s) + return find(s,'^%s+$') == 1 +end + +--- does s only contain lower case characters? +-- @string s a string +function stringx.islower(s) + assert_string(1,s) + return find(s,'^[%l%s]+$') == 1 +end + +--- does s only contain upper case characters? +-- @string s a string +function stringx.isupper(s) + assert_string(1,s) + return find(s,'^[%u%s]+$') == 1 +end + +local function raw_startswith(s, prefix) + return find(s,prefix,1,true) == 1 +end + +local function raw_endswith(s, suffix) + return #s >= #suffix and find(s, suffix, #s-#suffix+1, true) and true or false +end + +local function test_affixes(s, affixes, fn) + if type(affixes) == 'string' then + return fn(s,affixes) + elseif type(affixes) == 'table' then + for _,affix in ipairs(affixes) do + if fn(s,affix) then return true end + end + return false + else + error(("argument #2 expected a 'string' or a 'table', got a '%s'"):format(type(affixes))) + end +end + +--- does s start with prefix or one of prefixes? +-- @string s a string +-- @param prefix a string or an array of strings +function stringx.startswith(s,prefix) + assert_string(1,s) + return test_affixes(s,prefix,raw_startswith) +end + +--- does s end with suffix or one of suffixes? +-- @string s a string +-- @param suffix a string or an array of strings +function stringx.endswith(s,suffix) + assert_string(1,s) + return test_affixes(s,suffix,raw_endswith) +end + +--- Strings and Lists +-- @section lists + +--- concatenate the strings using this string as a delimiter. +-- Note that the arguments are reversed from `string.concat`. +-- @string s the string +-- @param seq a table of strings or numbers +-- @usage stringx.join(' ', {1,2,3}) == '1 2 3' +function stringx.join(s,seq) + assert_string(1,s) + return concat(seq,s) +end + +--- Split a string into a list of lines. +-- `"\r"`, `"\n"`, and `"\r\n"` are considered line ends. +-- They are not included in the lines unless `keepends` is passed. +-- Terminal line end does not produce an extra line. +-- Splitting an empty string results in an empty list. +-- @string s the string. +-- @bool[opt] keep_ends include line ends. +-- @return List of lines +function stringx.splitlines(s, keep_ends) + assert_string(1, s) + local res = {} + local pos = 1 + while true do + local line_end_pos = find(s, '[\r\n]', pos) + if not line_end_pos then + break + end + + local line_end = sub(s, line_end_pos, line_end_pos) + if line_end == '\r' and sub(s, line_end_pos + 1, line_end_pos + 1) == '\n' then + line_end = '\r\n' + end + + local line = sub(s, pos, line_end_pos - 1) + if keep_ends then + line = line .. line_end + end + append(res, line) + + pos = line_end_pos + #line_end + end + + if pos <= #s then + append(res, sub(s, pos)) + end + return makelist(res) +end + +--- split a string into a list of strings using a delimiter. +-- @function split +-- @string s the string +-- @string[opt] re a delimiter (defaults to whitespace) +-- @int[opt] n maximum number of results +-- @return List +-- @usage #(stringx.split('one two')) == 2 +-- @usage stringx.split('one,two,three', ',') == List{'one','two','three'} +-- @usage stringx.split('one,two,three', ',', 2) == List{'one','two,three'} +function stringx.split(s,re,n) + assert_string(1,s) + local plain = true + if not re then -- default spaces + s = lstrip(s) + plain = false + end + local res = usplit(s,re,plain,n) + if re and re ~= '' and + find(s,re,-#re,true) and + (n or math.huge) > #res then + res[#res+1] = "" + end + return makelist(res) +end + +--- replace all tabs in s with tabsize spaces. If not specified, tabsize defaults to 8. +-- Tab stops will be honored. +-- @string s the string +-- @int tabsize[opt=8] number of spaces to expand each tab +-- @return expanded string +-- @usage stringx.expandtabs('\tone,two,three', 4) == ' one,two,three' +-- @usage stringx.expandtabs(' \tone,two,three', 4) == ' one,two,three' +function stringx.expandtabs(s,tabsize) + assert_string(1,s) + tabsize = tabsize or 8 + return (s:gsub("([^\t\r\n]*)\t", function(before_tab) + return before_tab .. (" "):rep(tabsize - #before_tab % tabsize) + end)) +end + +--- Finding and Replacing +-- @section find + +local function _find_all(s,sub,first,last,allow_overlap) + first = first or 1 + last = last or #s + if sub == '' then return last+1,last-first+1 end + local i1,i2 = find(s,sub,first,true) + local res + local k = 0 + while i1 do + if last and i2 > last then break end + res = i1 + k = k + 1 + if allow_overlap then + i1,i2 = find(s,sub,i1+1,true) + else + i1,i2 = find(s,sub,i2+1,true) + end + end + return res,k +end + +--- find index of first instance of sub in s from the left. +-- @string s the string +-- @string sub substring +-- @int[opt] first first index +-- @int[opt] last last index +-- @return start index, or nil if not found +function stringx.lfind(s,sub,first,last) + assert_string(1,s) + assert_string(2,sub) + local i1, i2 = find(s,sub,first,true) + + if i1 and (not last or i2 <= last) then + return i1 + else + return nil + end +end + +--- find index of first instance of sub in s from the right. +-- @string s the string +-- @string sub substring +-- @int[opt] first first index +-- @int[opt] last last index +-- @return start index, or nil if not found +function stringx.rfind(s,sub,first,last) + assert_string(1,s) + assert_string(2,sub) + return (_find_all(s,sub,first,last,true)) +end + +--- replace up to n instances of old by new in the string s. +-- If n is not present, replace all instances. +-- @string s the string +-- @string old the target substring +-- @string new the substitution +-- @int[opt] n optional maximum number of substitutions +-- @return result string +function stringx.replace(s,old,new,n) + assert_string(1,s) + assert_string(2,old) + assert_string(3,new) + return (gsub(s,escape(old),new:gsub('%%','%%%%'),n)) +end + +--- count all instances of substring in string. +-- @string s the string +-- @string sub substring +-- @bool[opt] allow_overlap allow matches to overlap +-- @usage +-- assert(stringx.count('banana', 'ana') == 1) +-- assert(stringx.count('banana', 'ana', true) == 2) +function stringx.count(s,sub,allow_overlap) + assert_string(1,s) + local _,k = _find_all(s,sub,1,false,allow_overlap) + return k +end + +--- Stripping and Justifying +-- @section strip + +local function _just(s,w,ch,left,right) + local n = #s + if w > n then + if not ch then ch = ' ' end + local f1,f2 + if left and right then + local rn = ceil((w-n)/2) + local ln = w - n - rn + f1 = rep(ch,ln) + f2 = rep(ch,rn) + elseif right then + f1 = rep(ch,w-n) + f2 = '' + else + f2 = rep(ch,w-n) + f1 = '' + end + return f1..s..f2 + else + return s + end +end + +--- left-justify s with width w. +-- @string s the string +-- @int w width of justification +-- @string[opt=' '] ch padding character +-- @usage stringx.ljust('hello', 10, '*') == '*****hello' +function stringx.ljust(s,w,ch) + assert_string(1,s) + assert_arg(2,w,'number') + return _just(s,w,ch,true,false) +end + +--- right-justify s with width w. +-- @string s the string +-- @int w width of justification +-- @string[opt=' '] ch padding character +-- @usage stringx.rjust('hello', 10, '*') == 'hello*****' +function stringx.rjust(s,w,ch) + assert_string(1,s) + assert_arg(2,w,'number') + return _just(s,w,ch,false,true) +end + +--- center-justify s with width w. +-- @string s the string +-- @int w width of justification +-- @string[opt=' '] ch padding character +-- @usage stringx.center('hello', 10, '*') == '**hello***' +function stringx.center(s,w,ch) + assert_string(1,s) + assert_arg(2,w,'number') + return _just(s,w,ch,true,true) +end + +local function _strip(s,left,right,chrs) + if not chrs then + chrs = '%s' + else + chrs = '['..escape(chrs)..']' + end + local f = 1 + local t + if left then + local i1,i2 = find(s,'^'..chrs..'*') + if i2 >= i1 then + f = i2+1 + end + end + if right then + if #s < 200 then + local i1,i2 = find(s,chrs..'*$',f) + if i2 >= i1 then + t = i1-1 + end + else + local rs = reverse(s) + local i1,i2 = find(rs, '^'..chrs..'*') + if i2 >= i1 then + t = -i2-1 + end + end + end + return sub(s,f,t) +end + +--- trim any whitespace on the left of s. +-- @string s the string +-- @string[opt='%s'] chrs default any whitespace character, +-- but can be a string of characters to be trimmed +function stringx.lstrip(s,chrs) + assert_string(1,s) + return _strip(s,true,false,chrs) +end +lstrip = stringx.lstrip + +--- trim any whitespace on the right of s. +-- @string s the string +-- @string[opt='%s'] chrs default any whitespace character, +-- but can be a string of characters to be trimmed +function stringx.rstrip(s,chrs) + assert_string(1,s) + return _strip(s,false,true,chrs) +end + +--- trim any whitespace on both left and right of s. +-- @string s the string +-- @string[opt='%s'] chrs default any whitespace character, +-- but can be a string of characters to be trimmed +function stringx.strip(s,chrs) + assert_string(1,s) + return _strip(s,true,true,chrs) +end + +--- Partioning Strings +-- @section partioning + +--- split a string using a pattern. Note that at least one value will be returned! +-- @string s the string +-- @string[opt='%s'] re a Lua string pattern (defaults to whitespace) +-- @return the parts of the string +-- @usage a,b = line:splitv('=') +-- @see utils.splitv +function stringx.splitv(s,re) + assert_string(1,s) + return utils.splitv(s,re) +end + +-- The partition functions split a string using a delimiter into three parts: +-- the part before, the delimiter itself, and the part afterwards +local function _partition(p,delim,fn) + local i1,i2 = fn(p,delim) + if not i1 or i1 == -1 then + return p,'','' + else + if not i2 then i2 = i1 end + return sub(p,1,i1-1),sub(p,i1,i2),sub(p,i2+1) + end +end + +--- partition the string using first occurance of a delimiter +-- @string s the string +-- @string ch delimiter +-- @return part before ch +-- @return ch +-- @return part after ch +-- @usage {stringx.partition('a,b,c', ','))} == {'a', ',', 'b,c'} +-- @usage {stringx.partition('abc', 'x'))} == {'abc', '', ''} +function stringx.partition(s,ch) + assert_string(1,s) + assert_nonempty_string(2,ch) + return _partition(s,ch,stringx.lfind) +end + +--- partition the string p using last occurance of a delimiter +-- @string s the string +-- @string ch delimiter +-- @return part before ch +-- @return ch +-- @return part after ch +-- @usage {stringx.rpartition('a,b,c', ','))} == {'a,b', ',', 'c'} +-- @usage {stringx.rpartition('abc', 'x'))} == {'', '', 'abc'} +function stringx.rpartition(s,ch) + assert_string(1,s) + assert_nonempty_string(2,ch) + local a,b,c = _partition(s,ch,stringx.rfind) + if a == s then -- no match found + return c,b,a + end + return a,b,c +end + +--- return the 'character' at the index. +-- @string s the string +-- @int idx an index (can be negative) +-- @return a substring of length 1 if successful, empty string otherwise. +function stringx.at(s,idx) + assert_string(1,s) + assert_arg(2,idx,'number') + return sub(s,idx,idx) +end + +--- Miscelaneous +-- @section misc + +--- return an iterator over all lines in a string +-- @string s the string +-- @return an iterator +-- @usage +-- local line_no = 1 +-- for line in stringx.lines(some_text) do +-- print(line_no, line) +-- line_no = line_no + 1 +-- end +function stringx.lines(s) + assert_string(1,s) + if not s:find '\n$' then s = s..'\n' end + return s:gmatch('([^\n]*)\n') +end + +--- inital word letters uppercase ('title case'). +-- Here 'words' mean chunks of non-space characters. +-- @string s the string +-- @return a string with each word's first letter uppercase +-- @usage stringx.title("hello world") == "Hello World") +function stringx.title(s) + assert_string(1,s) + return (s:gsub('(%S)(%S*)',function(f,r) + return f:upper()..r:lower() + end)) +end + +stringx.capitalize = stringx.title + +local ellipsis = '...' +local n_ellipsis = #ellipsis + +--- Return a shortened version of a string. +-- Fits string within w characters. Removed characters are marked with ellipsis. +-- @string s the string +-- @int w the maxinum size allowed +-- @bool tail true if we want to show the end of the string (head otherwise) +-- @usage ('1234567890'):shorten(8) == '12345...' +-- @usage ('1234567890'):shorten(8, true) == '...67890' +-- @usage ('1234567890'):shorten(20) == '1234567890' +function stringx.shorten(s,w,tail) + assert_string(1,s) + if #s > w then + if w < n_ellipsis then return ellipsis:sub(1,w) end + if tail then + local i = #s - w + 1 + n_ellipsis + return ellipsis .. s:sub(i) + else + return s:sub(1,w-n_ellipsis) .. ellipsis + end + end + return s +end + +--- Utility function that finds any patterns that match a long string's an open or close. +-- Note that having this function use the least number of equal signs that is possible is a harder algorithm to come up with. +-- Right now, it simply returns the greatest number of them found. +-- @param s The string +-- @return 'nil' if not found. If found, the maximum number of equal signs found within all matches. +local function has_lquote(s) + local lstring_pat = '([%[%]])(=*)%1' + local equals, new_equals, _ + local finish = 1 + repeat + _, finish, _, new_equals = s:find(lstring_pat, finish) + if new_equals then + equals = max(equals or 0, #new_equals) + end + until not new_equals + + return equals +end + +--- Quote the given string and preserve any control or escape characters, such that reloading the string in Lua returns the same result. +-- @param s The string to be quoted. +-- @return The quoted string. +function stringx.quote_string(s) + assert_string(1,s) + -- Find out if there are any embedded long-quote sequences that may cause issues. + -- This is important when strings are embedded within strings, like when serializing. + -- Append a closing bracket to catch unfinished long-quote sequences at the end of the string. + local equal_signs = has_lquote(s .. "]") + + -- Note that strings containing "\r" can't be quoted using long brackets + -- as Lua lexer converts all newlines to "\n" within long strings. + if (s:find("\n") or equal_signs) and not s:find("\r") then + -- If there is an embedded sequence that matches a long quote, then + -- find the one with the maximum number of = signs and add one to that number. + equal_signs = ("="):rep((equal_signs or -1) + 1) + -- Long strings strip out leading newline. We want to retain that, when quoting. + if s:find("^\n") then s = "\n" .. s end + local lbracket, rbracket = + "[" .. equal_signs .. "[", + "]" .. equal_signs .. "]" + s = lbracket .. s .. rbracket + else + -- Escape funny stuff. Lua 5.1 does not handle "\r" correctly. + s = ("%q"):format(s):gsub("\r", "\\r") + end + return s +end + +function stringx.import() + utils.import(stringx,string) +end + +return stringx diff --git a/Data/Libraries/Penlight/lua/pl/tablex.lua b/Data/Libraries/Penlight/lua/pl/tablex.lua new file mode 100644 index 0000000..81f7395 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/tablex.lua @@ -0,0 +1,999 @@ +--- Extended operations on Lua tables. +-- +-- See @{02-arrays.md.Useful_Operations_on_Tables|the Guide} +-- +-- Dependencies: `pl.utils`, `pl.types` +-- @module pl.tablex +local utils = require ('pl.utils') +local types = require ('pl.types') +local getmetatable,setmetatable,require = getmetatable,setmetatable,require +local tsort,append,remove = table.sort,table.insert,table.remove +local min = math.min +local pairs,type,unpack,select,tostring = pairs,type,utils.unpack,select,tostring +local function_arg = utils.function_arg +local assert_arg = utils.assert_arg + +local tablex = {} + +-- generally, functions that make copies of tables try to preserve the metatable. +-- However, when the source has no obvious type, then we attach appropriate metatables +-- like List, Map, etc to the result. +local function setmeta (res,tbl,pl_class) + local mt = getmetatable(tbl) or pl_class and require('pl.' .. pl_class) + return mt and setmetatable(res, mt) or res +end + +local function makelist(l) + return setmetatable(l, require('pl.List')) +end + +local function makemap(m) + return setmetatable(m, require('pl.Map')) +end + +local function complain (idx,msg) + error(('argument %d is not %s'):format(idx,msg),3) +end + +local function assert_arg_indexable (idx,val) + if not types.is_indexable(val) then + complain(idx,"indexable") + end +end + +local function assert_arg_iterable (idx,val) + if not types.is_iterable(val) then + complain(idx,"iterable") + end +end + +local function assert_arg_writeable (idx,val) + if not types.is_writeable(val) then + complain(idx,"writeable") + end +end + +--- copy a table into another, in-place. +-- @within Copying +-- @tab t1 destination table +-- @tab t2 source (actually any iterable object) +-- @return first table +function tablex.update (t1,t2) + assert_arg_writeable(1,t1) + assert_arg_iterable(2,t2) + for k,v in pairs(t2) do + t1[k] = v + end + return t1 +end + +--- total number of elements in this table. +-- Note that this is distinct from `#t`, which is the number +-- of values in the array part; this value will always +-- be greater or equal. The difference gives the size of +-- the hash part, for practical purposes. Works for any +-- object with a __pairs metamethod. +-- @tab t a table +-- @return the size +function tablex.size (t) + assert_arg_iterable(1,t) + local i = 0 + for k in pairs(t) do i = i + 1 end + return i +end + +--- make a shallow copy of a table +-- @within Copying +-- @tab t an iterable source +-- @return new table +function tablex.copy (t) + assert_arg_iterable(1,t) + local res = {} + for k,v in pairs(t) do + res[k] = v + end + return res +end + +local function cycle_aware_copy(t, cache) + if type(t) ~= 'table' then return t end + if cache[t] then return cache[t] end + assert_arg_iterable(1,t) + local res = {} + cache[t] = res + local mt = getmetatable(t) + for k,v in pairs(t) do + k = cycle_aware_copy(k, cache) + v = cycle_aware_copy(v, cache) + res[k] = v + end + setmetatable(res,mt) + return res +end + +--- make a deep copy of a table, recursively copying all the keys and fields. +-- This supports cycles in tables; cycles will be reproduced in the copy. +-- This will also set the copied table's metatable to that of the original. +-- @within Copying +-- @tab t A table +-- @return new table +function tablex.deepcopy(t) + return cycle_aware_copy(t,{}) +end + +local abs = math.abs + +local function cycle_aware_compare(t1,t2,ignore_mt,eps,cache) + if cache[t1] and cache[t1][t2] then return true end + local ty1 = type(t1) + local ty2 = type(t2) + if ty1 ~= ty2 then return false end + -- non-table types can be directly compared + if ty1 ~= 'table' then + if ty1 == 'number' and eps then return abs(t1-t2) < eps end + return t1 == t2 + end + -- as well as tables which have the metamethod __eq + local mt = getmetatable(t1) + if not ignore_mt and mt and mt.__eq then return t1 == t2 end + for k1 in pairs(t1) do + if t2[k1]==nil then return false end + end + for k2 in pairs(t2) do + if t1[k2]==nil then return false end + end + cache[t1] = cache[t1] or {} + cache[t1][t2] = true + for k1,v1 in pairs(t1) do + local v2 = t2[k1] + if not cycle_aware_compare(v1,v2,ignore_mt,eps,cache) then return false end + end + return true +end + +--- compare two values. +-- if they are tables, then compare their keys and fields recursively. +-- @within Comparing +-- @param t1 A value +-- @param t2 A value +-- @bool[opt] ignore_mt if true, ignore __eq metamethod (default false) +-- @number[opt] eps if defined, then used for any number comparisons +-- @return true or false +function tablex.deepcompare(t1,t2,ignore_mt,eps) + return cycle_aware_compare(t1,t2,ignore_mt,eps,{}) +end + +--- compare two arrays using a predicate. +-- @within Comparing +-- @array t1 an array +-- @array t2 an array +-- @func cmp A comparison function; `bool = cmp(t1_value, t2_value)` +-- @return true or false +-- @usage +-- assert(tablex.compare({ 1, 2, 3 }, { 1, 2, 3 }, "==")) +-- +-- assert(tablex.compare( +-- {1,2,3, hello = "world"}, -- fields are not compared! +-- {1,2,3}, function(v1, v2) return v1 == v2 end) +function tablex.compare (t1,t2,cmp) + assert_arg_indexable(1,t1) + assert_arg_indexable(2,t2) + if #t1 ~= #t2 then return false end + cmp = function_arg(3,cmp) + for k = 1,#t1 do + if not cmp(t1[k],t2[k]) then return false end + end + return true +end + +--- compare two list-like tables using an optional predicate, without regard for element order. +-- @within Comparing +-- @array t1 a list-like table +-- @array t2 a list-like table +-- @param cmp A comparison function (may be nil) +function tablex.compare_no_order (t1,t2,cmp) + assert_arg_indexable(1,t1) + assert_arg_indexable(2,t2) + if cmp then cmp = function_arg(3,cmp) end + if #t1 ~= #t2 then return false end + local visited = {} + for i = 1,#t1 do + local val = t1[i] + local gotcha + for j = 1,#t2 do + if not visited[j] then + local match + if cmp then match = cmp(val,t2[j]) else match = val == t2[j] end + if match then + gotcha = j + break + end + end + end + if not gotcha then return false end + visited[gotcha] = true + end + return true +end + + +--- return the index of a value in a list. +-- Like string.find, there is an optional index to start searching, +-- which can be negative. +-- @within Finding +-- @array t A list-like table +-- @param val A value +-- @int idx index to start; -1 means last element,etc (default 1) +-- @return index of value or nil if not found +-- @usage find({10,20,30},20) == 2 +-- @usage find({'a','b','a','c'},'a',2) == 3 +function tablex.find(t,val,idx) + assert_arg_indexable(1,t) + idx = idx or 1 + if idx < 0 then idx = #t + idx + 1 end + for i = idx,#t do + if t[i] == val then return i end + end + return nil +end + +--- return the index of a value in a list, searching from the end. +-- Like string.find, there is an optional index to start searching, +-- which can be negative. +-- @within Finding +-- @array t A list-like table +-- @param val A value +-- @param idx index to start; -1 means last element,etc (default `#t`) +-- @return index of value or nil if not found +-- @usage rfind({10,10,10},10) == 3 +function tablex.rfind(t,val,idx) + assert_arg_indexable(1,t) + idx = idx or #t + if idx < 0 then idx = #t + idx + 1 end + for i = idx,1,-1 do + if t[i] == val then return i end + end + return nil +end + + +--- return the index (or key) of a value in a table using a comparison function. +-- +-- *NOTE*: the 2nd return value of this function, the value returned +-- by the comparison function, has a limitation that it cannot be `false`. +-- Because if it is, then it indicates the comparison failed, and the +-- function will continue the search. See examples. +-- @within Finding +-- @tab t A table +-- @func cmp A comparison function +-- @param arg an optional second argument to the function +-- @return index of value, or nil if not found +-- @return value returned by comparison function (cannot be `false`!) +-- @usage +-- -- using an operator +-- local lst = { "Rudolph", true, false, 15 } +-- local idx, cmp_result = tablex.rfind(lst, "==", "Rudolph") +-- assert(idx == 1) +-- assert(cmp_result == true) +-- +-- local idx, cmp_result = tablex.rfind(lst, "==", false) +-- assert(idx == 3) +-- assert(cmp_result == true) -- looking up 'false' works! +-- +-- -- using a function returning the value looked up +-- local cmp = function(v1, v2) return v1 == v2 and v2 end +-- local idx, cmp_result = tablex.rfind(lst, cmp, "Rudolph") +-- assert(idx == 1) +-- assert(cmp_result == "Rudolph") -- the value is returned +-- +-- -- NOTE: this fails, since 'false' cannot be returned! +-- local idx, cmp_result = tablex.rfind(lst, cmp, false) +-- assert(idx == nil) -- looking up 'false' failed! +-- assert(cmp_result == nil) +function tablex.find_if(t,cmp,arg) + assert_arg_iterable(1,t) + cmp = function_arg(2,cmp) + for k,v in pairs(t) do + local c = cmp(v,arg) + if c then return k,c end + end + return nil +end + +--- return a list of all values in a table indexed by another list. +-- @tab tbl a table +-- @array idx an index table (a list of keys) +-- @return a list-like table +-- @usage index_by({10,20,30,40},{2,4}) == {20,40} +-- @usage index_by({one=1,two=2,three=3},{'one','three'}) == {1,3} +function tablex.index_by(tbl,idx) + assert_arg_indexable(1,tbl) + assert_arg_indexable(2,idx) + local res = {} + for i = 1,#idx do + res[i] = tbl[idx[i]] + end + return setmeta(res,tbl,'List') +end + +--- apply a function to all values of a table. +-- This returns a table of the results. +-- Any extra arguments are passed to the function. +-- @within MappingAndFiltering +-- @func fun A function that takes at least one argument +-- @tab t A table +-- @param ... optional arguments +-- @usage map(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900,fred=4} +function tablex.map(fun,t,...) + assert_arg_iterable(1,t) + fun = function_arg(1,fun) + local res = {} + for k,v in pairs(t) do + res[k] = fun(v,...) + end + return setmeta(res,t) +end + +--- apply a function to all values of a list. +-- This returns a table of the results. +-- Any extra arguments are passed to the function. +-- @within MappingAndFiltering +-- @func fun A function that takes at least one argument +-- @array t a table (applies to array part) +-- @param ... optional arguments +-- @return a list-like table +-- @usage imap(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900} +function tablex.imap(fun,t,...) + assert_arg_indexable(1,t) + fun = function_arg(1,fun) + local res = {} + for i = 1,#t do + res[i] = fun(t[i],...) or false + end + return setmeta(res,t,'List') +end + +--- apply a named method to values from a table. +-- @within MappingAndFiltering +-- @string name the method name +-- @array t a list-like table +-- @param ... any extra arguments to the method +-- @return a `List` with the results of the method (1st result only) +-- @usage +-- local Car = {} +-- Car.__index = Car +-- function Car.new(car) +-- return setmetatable(car or {}, Car) +-- end +-- Car.speed = 0 +-- function Car:faster(increase) +-- self.speed = self.speed + increase +-- return self.speed +-- end +-- +-- local ferrari = Car.new{ name = "Ferrari" } +-- local lamborghini = Car.new{ name = "Lamborghini", speed = 50 } +-- local cars = { ferrari, lamborghini } +-- +-- assert(ferrari.speed == 0) +-- assert(lamborghini.speed == 50) +-- tablex.map_named_method("faster", cars, 10) +-- assert(ferrari.speed == 10) +-- assert(lamborghini.speed == 60) +function tablex.map_named_method (name,t,...) + utils.assert_string(1,name) + assert_arg_indexable(2,t) + local res = {} + for i = 1,#t do + local val = t[i] + local fun = val[name] + res[i] = fun(val,...) + end + return setmeta(res,t,'List') +end + +--- apply a function to all values of a table, in-place. +-- Any extra arguments are passed to the function. +-- @func fun A function that takes at least one argument +-- @tab t a table +-- @param ... extra arguments passed to `fun` +-- @see tablex.foreach +function tablex.transform (fun,t,...) + assert_arg_iterable(1,t) + fun = function_arg(1,fun) + for k,v in pairs(t) do + t[k] = fun(v,...) + end +end + +--- generate a table of all numbers in a range. +-- This is consistent with a numerical for loop. +-- @int start number +-- @int finish number +-- @int[opt=1] step make this negative for start < finish +function tablex.range (start,finish,step) + local res + step = step or 1 + if start == finish then + res = {start} + elseif (start > finish and step > 0) or (finish > start and step < 0) then + res = {} + else + local k = 1 + res = {} + for i=start,finish,step do res[k]=i; k=k+1 end + end + return makelist(res) +end + +--- apply a function to values from two tables. +-- @within MappingAndFiltering +-- @func fun a function of at least two arguments +-- @tab t1 a table +-- @tab t2 a table +-- @param ... extra arguments +-- @return a table +-- @usage map2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23,m=44} +function tablex.map2 (fun,t1,t2,...) + assert_arg_iterable(1,t1) + assert_arg_iterable(2,t2) + fun = function_arg(1,fun) + local res = {} + for k,v in pairs(t1) do + res[k] = fun(v,t2[k],...) + end + return setmeta(res,t1,'List') +end + +--- apply a function to values from two arrays. +-- The result will be the length of the shortest array. +-- @within MappingAndFiltering +-- @func fun a function of at least two arguments +-- @array t1 a list-like table +-- @array t2 a list-like table +-- @param ... extra arguments +-- @usage imap2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23} +function tablex.imap2 (fun,t1,t2,...) + assert_arg_indexable(2,t1) + assert_arg_indexable(3,t2) + fun = function_arg(1,fun) + local res,n = {},math.min(#t1,#t2) + for i = 1,n do + res[i] = fun(t1[i],t2[i],...) + end + return res +end + +--- 'reduce' a list using a binary function. +-- @func fun a function of two arguments +-- @array t a list-like table +-- @array memo optional initial memo value. Defaults to first value in table. +-- @return the result of the function +-- @usage reduce('+',{1,2,3,4}) == 10 +function tablex.reduce (fun,t,memo) + assert_arg_indexable(2,t) + fun = function_arg(1,fun) + local n = #t + if n == 0 then + return memo + end + local res = memo and fun(memo, t[1]) or t[1] + for i = 2,n do + res = fun(res,t[i]) + end + return res +end + +--- apply a function to all elements of a table. +-- The arguments to the function will be the value, +-- the key and _finally_ any extra arguments passed to this function. +-- Note that the Lua 5.0 function table.foreach passed the _key_ first. +-- @within Iterating +-- @tab t a table +-- @func fun a function on the elements; `function(value, key, ...)` +-- @param ... extra arguments passed to `fun` +-- @see tablex.transform +function tablex.foreach(t,fun,...) + assert_arg_iterable(1,t) + fun = function_arg(2,fun) + for k,v in pairs(t) do + fun(v,k,...) + end +end + +--- apply a function to all elements of a list-like table in order. +-- The arguments to the function will be the value, +-- the index and _finally_ any extra arguments passed to this function +-- @within Iterating +-- @array t a table +-- @func fun a function with at least one argument +-- @param ... optional arguments +function tablex.foreachi(t,fun,...) + assert_arg_indexable(1,t) + fun = function_arg(2,fun) + for i = 1,#t do + fun(t[i],i,...) + end +end + +--- Apply a function to a number of tables. +-- A more general version of map +-- The result is a table containing the result of applying that function to the +-- ith value of each table. Length of output list is the minimum length of all the lists +-- @within MappingAndFiltering +-- @func fun a function of n arguments +-- @tab ... n tables +-- @usage mapn(function(x,y,z) return x+y+z end, {1,2,3},{10,20,30},{100,200,300}) is {111,222,333} +-- @usage mapn(math.max, {1,20,300},{10,2,3},{100,200,100}) is {100,200,300} +-- @param fun A function that takes as many arguments as there are tables +function tablex.mapn(fun,...) + fun = function_arg(1,fun) + local res = {} + local lists = {...} + local minn = 1e40 + for i = 1,#lists do + minn = min(minn,#(lists[i])) + end + for i = 1,minn do + local args,k = {},1 + for j = 1,#lists do + args[k] = lists[j][i] + k = k + 1 + end + res[#res+1] = fun(unpack(args)) + end + return res +end + +--- call the function with the key and value pairs from a table. +-- The function can return a value and a key (note the order!). If both +-- are not nil, then this pair is inserted into the result: if the key already exists, we convert the value for that +-- key into a table and append into it. If only value is not nil, then it is appended to the result. +-- @within MappingAndFiltering +-- @func fun A function which will be passed each key and value as arguments, plus any extra arguments to pairmap. +-- @tab t A table +-- @param ... optional arguments +-- @usage pairmap(function(k,v) return v end,{fred=10,bonzo=20}) is {10,20} _or_ {20,10} +-- @usage pairmap(function(k,v) return {k,v},k end,{one=1,two=2}) is {one={'one',1},two={'two',2}} +function tablex.pairmap(fun,t,...) + assert_arg_iterable(1,t) + fun = function_arg(1,fun) + local res = {} + for k,v in pairs(t) do + local rv,rk = fun(k,v,...) + if rk then + if res[rk] then + if type(res[rk]) == 'table' then + table.insert(res[rk],rv) + else + res[rk] = {res[rk], rv} + end + else + res[rk] = rv + end + else + res[#res+1] = rv + end + end + return res +end + +local function keys_op(i,v) return i end + +--- return all the keys of a table in arbitrary order. +-- @within Extraction +-- @tab t A table +function tablex.keys(t) + assert_arg_iterable(1,t) + return makelist(tablex.pairmap(keys_op,t)) +end + +local function values_op(i,v) return v end + +--- return all the values of the table in arbitrary order +-- @within Extraction +-- @tab t A table +function tablex.values(t) + assert_arg_iterable(1,t) + return makelist(tablex.pairmap(values_op,t)) +end + +local function index_map_op (i,v) return i,v end + +--- create an index map from a list-like table. The original values become keys, +-- and the associated values are the indices into the original list. +-- @array t a list-like table +-- @return a map-like table +function tablex.index_map (t) + assert_arg_indexable(1,t) + return makemap(tablex.pairmap(index_map_op,t)) +end + +local function set_op(i,v) return true,v end + +--- create a set from a list-like table. A set is a table where the original values +-- become keys, and the associated values are all true. +-- @array t a list-like table +-- @return a set (a map-like table) +function tablex.makeset (t) + assert_arg_indexable(1,t) + return setmetatable(tablex.pairmap(set_op,t),require('pl.Set')) +end + +--- combine two tables, either as union or intersection. Corresponds to +-- set operations for sets () but more general. Not particularly +-- useful for list-like tables. +-- @within Merging +-- @tab t1 a table +-- @tab t2 a table +-- @bool dup true for a union, false for an intersection. +-- @usage merge({alice=23,fred=34},{bob=25,fred=34}) is {fred=34} +-- @usage merge({alice=23,fred=34},{bob=25,fred=34},true) is {bob=25,fred=34,alice=23} +-- @see tablex.index_map +function tablex.merge (t1,t2,dup) + assert_arg_iterable(1,t1) + assert_arg_iterable(2,t2) + local res = {} + for k,v in pairs(t1) do + if dup or t2[k] then res[k] = v end + end + if dup then + for k,v in pairs(t2) do + res[k] = v + end + end + return setmeta(res,t1,'Map') +end + +--- the union of two map-like tables. +-- If there are duplicate keys, the second table wins. +-- @tab t1 a table +-- @tab t2 a table +-- @treturn tab +-- @see tablex.merge +function tablex.union(t1, t2) + return tablex.merge(t1, t2, true) +end + +--- the intersection of two map-like tables. +-- @tab t1 a table +-- @tab t2 a table +-- @treturn tab +-- @see tablex.merge +function tablex.intersection(t1, t2) + return tablex.merge(t1, t2, false) +end + +--- a new table which is the difference of two tables. +-- With sets (where the values are all true) this is set difference and +-- symmetric difference depending on the third parameter. +-- @within Merging +-- @tab s1 a map-like table or set +-- @tab s2 a map-like table or set +-- @bool symm symmetric difference (default false) +-- @return a map-like table or set +function tablex.difference (s1,s2,symm) + assert_arg_iterable(1,s1) + assert_arg_iterable(2,s2) + local res = {} + for k,v in pairs(s1) do + if s2[k] == nil then res[k] = v end + end + if symm then + for k,v in pairs(s2) do + if s1[k] == nil then res[k] = v end + end + end + return setmeta(res,s1,'Map') +end + +--- A table where the key/values are the values and value counts of the table. +-- @array t a list-like table +-- @func cmp a function that defines equality (otherwise uses ==) +-- @return a map-like table +-- @see seq.count_map +function tablex.count_map (t,cmp) + assert_arg_indexable(1,t) + local res,mask = {},{} + cmp = function_arg(2,cmp or '==') + local n = #t + for i = 1,#t do + local v = t[i] + if not mask[v] then + mask[v] = true + -- check this value against all other values + res[v] = 1 -- there's at least one instance + for j = i+1,n do + local w = t[j] + local ok = cmp(v,w) + if ok then + res[v] = res[v] + 1 + mask[w] = true + end + end + end + end + return makemap(res) +end + +--- filter an array's values using a predicate function +-- @within MappingAndFiltering +-- @array t a list-like table +-- @func pred a boolean function +-- @param arg optional argument to be passed as second argument of the predicate +function tablex.filter (t,pred,arg) + assert_arg_indexable(1,t) + pred = function_arg(2,pred) + local res,k = {},1 + for i = 1,#t do + local v = t[i] + if pred(v,arg) then + res[k] = v + k = k + 1 + end + end + return setmeta(res,t,'List') +end + +--- return a table where each element is a table of the ith values of an arbitrary +-- number of tables. It is equivalent to a matrix transpose. +-- @within Merging +-- @usage zip({10,20,30},{100,200,300}) is {{10,100},{20,200},{30,300}} +-- @array ... arrays to be zipped +function tablex.zip(...) + return tablex.mapn(function(...) return {...} end,...) +end + +local _copy +function _copy (dest,src,idest,isrc,nsrc,clean_tail) + idest = idest or 1 + isrc = isrc or 1 + local iend + if not nsrc then + nsrc = #src + iend = #src + else + iend = isrc + min(nsrc-1,#src-isrc) + end + if dest == src then -- special case + if idest > isrc and iend >= idest then -- overlapping ranges + src = tablex.sub(src,isrc,nsrc) + isrc = 1; iend = #src + end + end + for i = isrc,iend do + dest[idest] = src[i] + idest = idest + 1 + end + if clean_tail then + tablex.clear(dest,idest) + end + return dest +end + +--- copy an array into another one, clearing `dest` after `idest+nsrc`, if necessary. +-- @within Copying +-- @array dest a list-like table +-- @array src a list-like table +-- @int[opt=1] idest where to start copying values into destination +-- @int[opt=1] isrc where to start copying values from source +-- @int[opt=#src] nsrc number of elements to copy from source +function tablex.icopy (dest,src,idest,isrc,nsrc) + assert_arg_indexable(1,dest) + assert_arg_indexable(2,src) + return _copy(dest,src,idest,isrc,nsrc,true) +end + +--- copy an array into another one. +-- @within Copying +-- @array dest a list-like table +-- @array src a list-like table +-- @int[opt=1] idest where to start copying values into destination +-- @int[opt=1] isrc where to start copying values from source +-- @int[opt=#src] nsrc number of elements to copy from source +function tablex.move (dest,src,idest,isrc,nsrc) + assert_arg_indexable(1,dest) + assert_arg_indexable(2,src) + return _copy(dest,src,idest,isrc,nsrc,false) +end + +function tablex._normalize_slice(self,first,last) + local sz = #self + if not first then first=1 end + if first<0 then first=sz+first+1 end + -- make the range _inclusive_! + if not last then last=sz end + if last < 0 then last=sz+1+last end + return first,last +end + +--- Extract a range from a table, like 'string.sub'. +-- If first or last are negative then they are relative to the end of the list +-- eg. sub(t,-2) gives last 2 entries in a list, and +-- sub(t,-4,-2) gives from -4th to -2nd +-- @within Extraction +-- @array t a list-like table +-- @int first An index +-- @int last An index +-- @return a new List +function tablex.sub(t,first,last) + assert_arg_indexable(1,t) + first,last = tablex._normalize_slice(t,first,last) + local res={} + for i=first,last do append(res,t[i]) end + return setmeta(res,t,'List') +end + +--- set an array range to a value. If it's a function we use the result +-- of applying it to the indices. +-- @array t a list-like table +-- @param val a value +-- @int[opt=1] i1 start range +-- @int[opt=#t] i2 end range +function tablex.set (t,val,i1,i2) + assert_arg_indexable(1,t) + i1,i2 = i1 or 1,i2 or #t + if types.is_callable(val) then + for i = i1,i2 do + t[i] = val(i) + end + else + for i = i1,i2 do + t[i] = val + end + end +end + +--- create a new array of specified size with initial value. +-- @int n size +-- @param val initial value (can be `nil`, but don't expect `#` to work!) +-- @return the table +function tablex.new (n,val) + local res = {} + tablex.set(res,val,1,n) + return res +end + +--- clear out the contents of a table. +-- @array t a list +-- @param istart optional start position +function tablex.clear(t,istart) + istart = istart or 1 + for i = istart,#t do remove(t) end +end + +--- insert values into a table. +-- similar to `table.insert` but inserts values from given table `values`, +-- not the object itself, into table `t` at position `pos`. +-- @within Copying +-- @array t the list +-- @int[opt] position (default is at end) +-- @array values +function tablex.insertvalues(t, ...) + assert_arg(1,t,'table') + local pos, values + if select('#', ...) == 1 then + pos,values = #t+1, ... + else + pos,values = ... + end + if #values > 0 then + for i=#t,pos,-1 do + t[i+#values] = t[i] + end + local offset = 1 - pos + for i=pos,pos+#values-1 do + t[i] = values[i + offset] + end + end + return t +end + +--- remove a range of values from a table. +-- End of range may be negative. +-- @array t a list-like table +-- @int i1 start index +-- @int i2 end index +-- @return the table +function tablex.removevalues (t,i1,i2) + assert_arg(1,t,'table') + i1,i2 = tablex._normalize_slice(t,i1,i2) + for i = i1,i2 do + remove(t,i1) + end + return t +end + +local _find +_find = function (t,value,tables) + for k,v in pairs(t) do + if v == value then return k end + end + for k,v in pairs(t) do + if not tables[v] and type(v) == 'table' then + tables[v] = true + local res = _find(v,value,tables) + if res then + res = tostring(res) + if type(k) ~= 'string' then + return '['..k..']'..res + else + return k..'.'..res + end + end + end + end +end + +--- find a value in a table by recursive search. +-- @within Finding +-- @tab t the table +-- @param value the value +-- @array[opt] exclude any tables to avoid searching +-- @return a fieldspec, e.g. 'a.b' or 'math.sin' +-- @usage search(_G,math.sin,{package.path}) == 'math.sin' +function tablex.search (t,value,exclude) + assert_arg_iterable(1,t) + local tables = {[t]=true} + if exclude then + for _,v in pairs(exclude) do tables[v] = true end + end + return _find(t,value,tables) +end + +--- return an iterator to a table sorted by its keys +-- @within Iterating +-- @tab t the table +-- @func f an optional comparison function (f(x,y) is true if x < y) +-- @usage for k,v in tablex.sort(t) do print(k,v) end +-- @return an iterator to traverse elements sorted by the keys +function tablex.sort(t,f) + local keys = {} + for k in pairs(t) do keys[#keys + 1] = k end + tsort(keys,f) + local i = 0 + return function() + i = i + 1 + return keys[i], t[keys[i]] + end +end + +--- return an iterator to a table sorted by its values +-- @within Iterating +-- @tab t the table +-- @func f an optional comparison function (f(x,y) is true if x < y) +-- @usage for k,v in tablex.sortv(t) do print(k,v) end +-- @return an iterator to traverse elements sorted by the values +function tablex.sortv(t,f) + f = function_arg(2, f or '<') + local keys = {} + for k in pairs(t) do keys[#keys + 1] = k end + tsort(keys,function(x, y) return f(t[x], t[y]) end) + local i = 0 + return function() + i = i + 1 + return keys[i], t[keys[i]] + end +end + +--- modifies a table to be read only. +-- This only offers weak protection. Tables can still be modified with +-- `table.insert` and `rawset`. +-- +-- *NOTE*: for Lua 5.1 length, pairs and ipairs will not work, since the +-- equivalent metamethods are only available in Lua 5.2 and newer. +-- @tab t the table +-- @return the table read only (a proxy). +function tablex.readonly(t) + local mt = { + __index=t, + __newindex=function(t, k, v) error("Attempt to modify read-only table", 2) end, + __pairs=function() return pairs(t) end, + __ipairs=function() return ipairs(t) end, + __len=function() return #t end, + __metatable=false + } + return setmetatable({}, mt) +end + +return tablex diff --git a/Data/Libraries/Penlight/lua/pl/template.lua b/Data/Libraries/Penlight/lua/pl/template.lua new file mode 100644 index 0000000..9961a1b --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/template.lua @@ -0,0 +1,202 @@ +--- A template preprocessor. +-- Originally by [Ricki Lake](http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor) +-- +-- There are two rules: +-- +-- * lines starting with # are Lua +-- * otherwise, `$(expr)` is the result of evaluating `expr` +-- +-- Example: +-- +-- # for i = 1,3 do +-- $(i) Hello, Word! +-- # end +-- ===> +-- 1 Hello, Word! +-- 2 Hello, Word! +-- 3 Hello, Word! +-- +-- Other escape characters can be used, when the defaults conflict +-- with the output language. +-- +-- > for _,n in pairs{'one','two','three'} do +-- static int l_${n} (luaState *state); +-- > end +-- +-- See @{03-strings.md.Another_Style_of_Template|the Guide}. +-- +-- Dependencies: `pl.utils` +-- @module pl.template + +local utils = require 'pl.utils' + +local append,format,strsub,strfind,strgsub = table.insert,string.format,string.sub,string.find,string.gsub + +local APPENDER = "\n__R_size = __R_size + 1; __R_table[__R_size] = " + +local function parseDollarParen(pieces, chunk, exec_pat, newline) + local s = 1 + for term, executed, e in chunk:gmatch(exec_pat) do + executed = '('..strsub(executed,2,-2)..')' + append(pieces, APPENDER..format("%q", strsub(chunk,s, term - 1))) + append(pieces, APPENDER..format("__tostring(%s or '')", executed)) + s = e + end + local r + if newline then + r = format("%q", strgsub(strsub(chunk,s),"\n","")) + else + r = format("%q", strsub(chunk,s)) + end + if r ~= '""' then + append(pieces, APPENDER..r) + end +end + +local function parseHashLines(chunk,inline_escape,brackets,esc,newline) + local exec_pat = "()"..inline_escape.."(%b"..brackets..")()" + + local esc_pat = esc.."+([^\n]*\n?)" + local esc_pat1, esc_pat2 = "^"..esc_pat, "\n"..esc_pat + local pieces, s = {"return function()\nlocal __R_size, __R_table, __tostring = 0, {}, __tostring", n = 1}, 1 + while true do + local _, e, lua = strfind(chunk,esc_pat1, s) + if not e then + local ss + ss, e, lua = strfind(chunk,esc_pat2, s) + parseDollarParen(pieces, strsub(chunk,s, ss), exec_pat, newline) + if not e then break end + end + if strsub(lua, -1, -1) == "\n" then lua = strsub(lua, 1, -2) end + append(pieces, "\n"..lua) + s = e + 1 + end + append(pieces, "\nreturn __R_table\nend") + + -- let's check for a special case where there is nothing to template, but it's + -- just a single static string + local short = false + if (#pieces == 3) and (pieces[2]:find(APPENDER, 1, true) == 1) then + pieces = { "return " .. pieces[2]:sub(#APPENDER+1,-1) } + short = true + end + -- if short == true, the generated function will not return a table of strings, + -- but a single string + return table.concat(pieces), short +end + +local template = {} + +--- expand the template using the specified environment. +-- This function will compile and render the template. For more performant +-- recurring usage use the two step approach by using `compile` and `ct:render`. +-- There are six special fields in the environment table `env` +-- +-- * `_parent`: continue looking up in this table (e.g. `_parent=_G`). +-- * `_brackets`: bracket pair that wraps inline Lua expressions, default is '()'. +-- * `_escape`: character marking Lua lines, default is '#' +-- * `_inline_escape`: character marking inline Lua expression, default is '$'. +-- * `_chunk_name`: chunk name for loaded templates, used if there +-- is an error in Lua code. Default is 'TMP'. +-- * `_debug`: if truthy, the generated code will be printed upon a render error +-- +-- @string str the template string +-- @tab[opt] env the environment +-- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last +-- return value (`source_code`) is only returned if the debug option is used. +function template.substitute(str,env) + env = env or {} + local t, err = template.compile(str, { + chunk_name = rawget(env,"_chunk_name"), + escape = rawget(env,"_escape"), + inline_escape = rawget(env,"_inline_escape"), + inline_brackets = rawget(env,"_brackets"), + newline = nil, + debug = rawget(env,"_debug") + }) + if not t then return t, err end + + return t:render(env, rawget(env,"_parent"), rawget(env,"_debug")) +end + +--- executes the previously compiled template and renders it. +-- @function ct:render +-- @tab[opt] env the environment. +-- @tab[opt] parent continue looking up in this table (e.g. `parent=_G`). +-- @bool[opt] db if thruthy, it will print the code upon a render error +-- (provided the template was compiled with the debug option). +-- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last return value +-- (`source_code`) is only returned if the template was compiled with the debug option. +-- @usage +-- local ct, err = template.compile(my_template) +-- local rendered , err = ct:render(my_env, parent) +local render = function(self, env, parent, db) + env = env or {} + if parent then -- parent is a bit silly, but for backward compatibility retained + setmetatable(env, {__index = parent}) + end + setmetatable(self.env, {__index = env}) + + local res, out = xpcall(self.fn, debug.traceback) + if not res then + if self.code and db then print(self.code) end + return nil, out, self.code + end + return table.concat(out), nil, self.code +end + +--- compiles the template. +-- Returns an object that can repeatedly be rendered without parsing/compiling +-- the template again. +-- The options passed in the `opts` table support the following options: +-- +-- * `chunk_name`: chunk name for loaded templates, used if there +-- is an error in Lua code. Default is 'TMP'. +-- * `escape`: character marking Lua lines, default is '#' +-- * `inline_escape`: character marking inline Lua expression, default is '$'. +-- * `inline_brackets`: bracket pair that wraps inline Lua expressions, default is '()'. +-- * `newline`: string to replace newline characters, default is `nil` (not replacing newlines). +-- * `debug`: if truthy, the generated source code will be retained within the compiled template object, default is `nil`. +-- +-- @string str the template string +-- @tab[opt] opts the compilation options to use +-- @return template object, or `nil + error + source_code` +-- @usage +-- local ct, err = template.compile(my_template) +-- local rendered , err = ct:render(my_env, parent) +function template.compile(str, opts) + opts = opts or {} + local chunk_name = opts.chunk_name or 'TMP' + local escape = opts.escape or '#' + local inline_escape = opts.inline_escape or '$' + local inline_brackets = opts.inline_brackets or '()' + + local code, short = parseHashLines(str,inline_escape,inline_brackets,escape,opts.newline) + local env = { __tostring = tostring } + local fn, err = utils.load(code, chunk_name,'t',env) + if not fn then return nil, err, code end + + if short then + -- the template returns a single constant string, let's optimize for that + local constant_string = fn() + return { + fn = fn(), + env = env, + render = function(self) -- additional params can be ignored + -- skip the metatable magic and error handling in the render + -- function above for this special case + return constant_string, nil, self.code + end, + code = opts.debug and code or nil, + } + end + + return { + fn = fn(), + env = env, + render = render, + code = opts.debug and code or nil, + } +end + +return template diff --git a/Data/Libraries/Penlight/lua/pl/test.lua b/Data/Libraries/Penlight/lua/pl/test.lua new file mode 100644 index 0000000..694bbc5 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/test.lua @@ -0,0 +1,164 @@ +--- Useful test utilities. +-- +-- test.asserteq({1,2},{1,2}) -- can compare tables +-- test.asserteq(1.2,1.19,0.02) -- compare FP numbers within precision +-- T = test.tuple -- used for comparing multiple results +-- test.asserteq(T(string.find(" me","me")),T(2,3)) +-- +-- Dependencies: `pl.utils`, `pl.tablex`, `pl.pretty`, `pl.path`, `debug` +-- @module pl.test + +local tablex = require 'pl.tablex' +local utils = require 'pl.utils' +local pretty = require 'pl.pretty' +local path = require 'pl.path' +local type,unpack,pack = type,utils.unpack,utils.pack +local clock = os.clock +local debug = require 'debug' +local io = io + +local function dump(x) + if type(x) == 'table' and not (getmetatable(x) and getmetatable(x).__tostring) then + return pretty.write(x,' ',true) + elseif type(x) == 'string' then + return '"'..x..'"' + else + return tostring(x) + end +end + +local test = {} + +---- error handling for test results. +-- By default, this writes to stderr and exits the program. +-- Re-define this function to raise an error and/or redirect output +function test.error_handler(file,line,got_text, needed_text,msg) + local err = io.stderr + err:write(path.basename(file)..':'..line..': assertion failed\n') + err:write("got:\t",got_text,'\n') + err:write("needed:\t",needed_text,'\n') + utils.quit(1,msg or "these values were not equal") +end + +local function complain (x,y,msg,where) + local i = debug.getinfo(3 + (where or 0)) + test.error_handler(i.short_src,i.currentline,dump(x),dump(y),msg) +end + +--- general test complain message. +-- Useful for composing new test functions (see tests/tablex.lua for an example) +-- @param x a value +-- @param y value to compare first value against +-- @param msg message +-- @param where extra level offset for errors +-- @function complain +test.complain = complain + +--- like assert, except takes two arguments that must be equal and can be tables. +-- If they are plain tables, it will use tablex.deepcompare. +-- @param x any value +-- @param y a value equal to x +-- @param eps an optional tolerance for numerical comparisons +-- @param where extra level offset +function test.asserteq (x,y,eps,where) + local res = x == y + if not res then + res = tablex.deepcompare(x,y,true,eps) + end + if not res then + complain(x,y,nil,where) + end +end + +--- assert that the first string matches the second. +-- @param s1 a string +-- @param s2 a string +-- @param where extra level offset +function test.assertmatch (s1,s2,where) + if not s1:match(s2) then + complain (s1,s2,"these strings did not match",where) + end +end + +--- assert that the function raises a particular error. +-- @param fn a function or a table of the form {function,arg1,...} +-- @param e a string to match the error against +-- @param where extra level offset +function test.assertraise(fn,e,where) + local ok, err + if type(fn) == 'table' then + ok, err = pcall(unpack(fn)) + else + ok, err = pcall(fn) + end + if ok or err:match(e)==nil then + complain (err,e,"these errors did not match",where) + end +end + +--- a version of asserteq that takes two pairs of values. +-- <code>x1==y1 and x2==y2</code> must be true. Useful for functions that naturally +-- return two values. +-- @param x1 any value +-- @param x2 any value +-- @param y1 any value +-- @param y2 any value +-- @param where extra level offset +function test.asserteq2 (x1,x2,y1,y2,where) + if x1 ~= y1 then complain(x1,y1,nil,where) end + if x2 ~= y2 then complain(x2,y2,nil,where) end +end + +-- tuple type -- + +local tuple_mt = { + unpack = unpack +} +tuple_mt.__index = tuple_mt + +function tuple_mt.__tostring(self) + local ts = {} + for i=1, self.n do + local s = self[i] + ts[i] = type(s) == 'string' and ('%q'):format(s) or tostring(s) + end + return 'tuple(' .. table.concat(ts, ', ') .. ')' +end + +function tuple_mt.__eq(a, b) + if a.n ~= b.n then return false end + for i=1, a.n do + if a[i] ~= b[i] then return false end + end + return true +end + +function tuple_mt.__len(self) + return self.n +end + +--- encode an arbitrary argument list as a tuple. +-- This can be used to compare to other argument lists, which is +-- very useful for testing functions which return a number of values. +-- Unlike regular array-like tables ('sequences') they may contain nils. +-- Tuples understand equality and know how to print themselves out. +-- The # operator is defined to be the size, irrespecive of any nils, +-- and there is an `unpack` method. +-- @usage asserteq(tuple( ('ab'):find 'a'), tuple(1,1)) +function test.tuple(...) + return setmetatable(pack(...), tuple_mt) +end + +--- Time a function. Call the function a given number of times, and report the number of seconds taken, +-- together with a message. Any extra arguments will be passed to the function. +-- @string msg a descriptive message +-- @int n number of times to call the function +-- @func fun the function +-- @param ... optional arguments to fun +function test.timer(msg,n,fun,...) + local start = clock() + for i = 1,n do fun(...) end + utils.printf("%s: took %7.2f sec\n",msg,clock()-start) +end + +return test diff --git a/Data/Libraries/Penlight/lua/pl/text.lua b/Data/Libraries/Penlight/lua/pl/text.lua new file mode 100644 index 0000000..e626e86 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/text.lua @@ -0,0 +1,246 @@ +--- Text processing utilities. +-- +-- This provides a Template class (modeled after the same from the Python +-- libraries, see string.Template). It also provides similar functions to those +-- found in the textwrap module. +-- +-- See @{03-strings.md.String_Templates|the Guide}. +-- +-- Calling `text.format_operator()` overloads the % operator for strings to give Python/Ruby style formated output. +-- This is extended to also do template-like substitution for map-like data. +-- +-- > require 'pl.text'.format_operator() +-- > = '%s = %5.3f' % {'PI',math.pi} +-- PI = 3.142 +-- > = '$name = $value' % {name='dog',value='Pluto'} +-- dog = Pluto +-- +-- Dependencies: `pl.utils`, `pl.types` +-- @module pl.text + +local gsub = string.gsub +local concat,append = table.concat,table.insert +local utils = require 'pl.utils' +local bind1,usplit,assert_arg = utils.bind1,utils.split,utils.assert_arg +local is_callable = require 'pl.types'.is_callable +local unpack = utils.unpack + +local text = {} + + +local function makelist(l) + return setmetatable(l, require('pl.List')) +end + +local function lstrip(str) return (str:gsub('^%s+','')) end +local function strip(str) return (lstrip(str):gsub('%s+$','')) end +local function split(s,delim) return makelist(usplit(s,delim)) end + +local function imap(f,t,...) + local res = {} + for i = 1,#t do res[i] = f(t[i],...) end + return res +end + +local function _indent (s,sp) + local sl = split(s,'\n') + return concat(imap(bind1('..',sp),sl),'\n')..'\n' +end + +--- indent a multiline string. +-- @param s the string +-- @param n the size of the indent +-- @param ch the character to use when indenting (default ' ') +-- @return indented string +function text.indent (s,n,ch) + assert_arg(1,s,'string') + assert_arg(2,n,'number') + return _indent(s,string.rep(ch or ' ',n)) +end + +--- dedent a multiline string by removing any initial indent. +-- useful when working with [[..]] strings. +-- @param s the string +-- @return a string with initial indent zero. +function text.dedent (s) + assert_arg(1,s,'string') + local sl = split(s,'\n') + local _,i2 = (#sl>0 and sl[1] or ''):find('^%s*') + sl = imap(string.sub,sl,i2+1) + return concat(sl,'\n')..'\n' +end + +--- format a paragraph into lines so that they fit into a line width. +-- It will not break long words, so lines can be over the length +-- to that extent. +-- @param s the string +-- @param width the margin width, default 70 +-- @return a list of lines (List object) +-- @see pl.List +function text.wrap (s,width) + assert_arg(1,s,'string') + width = width or 70 + s = s:gsub('\n',' ') + local i,nxt = 1 + local lines,line = {} + while i < #s do + nxt = i+width + if s:find("[%w']",nxt) then -- inside a word + nxt = s:find('%W',nxt+1) -- so find word boundary + end + line = s:sub(i,nxt) + i = i + #line + append(lines,strip(line)) + end + return makelist(lines) +end + +--- format a paragraph so that it fits into a line width. +-- @param s the string +-- @param width the margin width, default 70 +-- @return a string +-- @see wrap +function text.fill (s,width) + return concat(text.wrap(s,width),'\n') .. '\n' +end + +local Template = {} +text.Template = Template +Template.__index = Template +setmetatable(Template, { + __call = function(obj,tmpl) + return Template.new(tmpl) + end}) + +function Template.new(tmpl) + assert_arg(1,tmpl,'string') + local res = {} + res.tmpl = tmpl + setmetatable(res,Template) + return res +end + +local function _substitute(s,tbl,safe) + local subst + if is_callable(tbl) then + subst = tbl + else + function subst(f) + local s = tbl[f] + if not s then + if safe then + return f + else + error("not present in table "..f) + end + else + return s + end + end + end + local res = gsub(s,'%${([%w_]+)}',subst) + return (gsub(res,'%$([%w_]+)',subst)) +end + +--- substitute values into a template, throwing an error. +-- This will throw an error if no name is found. +-- @param tbl a table of name-value pairs. +function Template:substitute(tbl) + assert_arg(1,tbl,'table') + return _substitute(self.tmpl,tbl,false) +end + +--- substitute values into a template. +-- This version just passes unknown names through. +-- @param tbl a table of name-value pairs. +function Template:safe_substitute(tbl) + assert_arg(1,tbl,'table') + return _substitute(self.tmpl,tbl,true) +end + +--- substitute values into a template, preserving indentation. <br> +-- If the value is a multiline string _or_ a template, it will insert +-- the lines at the correct indentation. <br> +-- Furthermore, if a template, then that template will be subsituted +-- using the same table. +-- @param tbl a table of name-value pairs. +function Template:indent_substitute(tbl) + assert_arg(1,tbl,'table') + if not self.strings then + self.strings = split(self.tmpl,'\n') + end + -- the idea is to substitute line by line, grabbing any spaces as + -- well as the $var. If the value to be substituted contains newlines, + -- then we split that into lines and adjust the indent before inserting. + local function subst(line) + return line:gsub('(%s*)%$([%w_]+)',function(sp,f) + local subtmpl + local s = tbl[f] + if not s then error("not present in table "..f) end + if getmetatable(s) == Template then + subtmpl = s + s = s.tmpl + else + s = tostring(s) + end + if s:find '\n' then + s = _indent(s,sp) + end + if subtmpl then return _substitute(s,tbl) + else return s + end + end) + end + local lines = imap(subst,self.strings) + return concat(lines,'\n')..'\n' +end + +------- Python-style formatting operator ------ +-- (see <a href="http://lua-users.org/wiki/StringInterpolation">the lua-users wiki</a>) -- + +function text.format_operator() + + local format = string.format + + -- a more forgiving version of string.format, which applies + -- tostring() to any value with a %s format. + local function formatx (fmt,...) + local args = {...} + local i = 1 + for p in fmt:gmatch('%%.') do + if p == '%s' and type(args[i]) ~= 'string' then + args[i] = tostring(args[i]) + end + i = i + 1 + end + return format(fmt,unpack(args)) + end + + local function basic_subst(s,t) + return (s:gsub('%$([%w_]+)',t)) + end + + -- Note this goes further than the original, and will allow these cases: + -- 1. a single value + -- 2. a list of values + -- 3. a map of var=value pairs + -- 4. a function, as in gsub + -- For the second two cases, it uses $-variable substituion. + getmetatable("").__mod = function(a, b) + if b == nil then + return a + elseif type(b) == "table" and getmetatable(b) == nil then + if #b == 0 then -- assume a map-like table + return _substitute(a,b,true) + else + return formatx(a,unpack(b)) + end + elseif type(b) == 'function' then + return basic_subst(a,b) + else + return formatx(a,b) + end + end +end + +return text diff --git a/Data/Libraries/Penlight/lua/pl/types.lua b/Data/Libraries/Penlight/lua/pl/types.lua new file mode 100644 index 0000000..35b0ccb --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/types.lua @@ -0,0 +1,183 @@ +---- Dealing with Detailed Type Information + +-- Dependencies `pl.utils` +-- @module pl.types + +local utils = require 'pl.utils' +local math_ceil = math.ceil +local assert_arg = utils.assert_arg +local types = {} + +--- is the object either a function or a callable object?. +-- @param obj Object to check. +function types.is_callable (obj) + return type(obj) == 'function' or getmetatable(obj) and getmetatable(obj).__call and true +end + +--- is the object of the specified type?. +-- If the type is a string, then use type, otherwise compare with metatable. +-- +-- NOTE: this function is imported from `utils.is_type`. +-- @param obj An object to check +-- @param tp The expected type +-- @function is_type +-- @see utils.is_type +types.is_type = utils.is_type + +local fileMT = getmetatable(io.stdout) + +--- a string representation of a type. +-- For tables and userdata with metatables, we assume that the metatable has a `_name` +-- field. If the field is not present it will return 'unknown table' or +-- 'unknown userdata'. +-- Lua file objects return the type 'file'. +-- @param obj an object +-- @return a string like 'number', 'table', 'file' or 'List' +function types.type (obj) + local t = type(obj) + if t == 'table' or t == 'userdata' then + local mt = getmetatable(obj) + if mt == fileMT then + return 'file' + elseif mt == nil then + return t + else + -- TODO: the "unknown" is weird, it should just return the type + return mt._name or "unknown "..t + end + else + return t + end +end + +--- is this number an integer? +-- @param x a number +-- @raise error if x is not a number +-- @return boolean +function types.is_integer (x) + return math_ceil(x)==x +end + +--- Check if the object is "empty". +-- An object is considered empty if it is: +-- +-- - `nil` +-- - a table without any items (key-value pairs or indexes) +-- - a string with no content ("") +-- - not a nil/table/string +-- @param o The object to check if it is empty. +-- @param ignore_spaces If the object is a string and this is true the string is +-- considered empty if it only contains spaces. +-- @return `true` if the object is empty, otherwise a falsy value. +function types.is_empty(o, ignore_spaces) + if o == nil then + return true + elseif type(o) == "table" then + return next(o) == nil + elseif type(o) == "string" then + return o == "" or (not not ignore_spaces and (not not o:find("^%s+$"))) + else + return true + end +end + +local function check_meta (val) + if type(val) == 'table' then return true end + return getmetatable(val) +end + +--- is an object 'array-like'? +-- An object is array like if: +-- +-- - it is a table, or +-- - it has a metatable with `__len` and `__index` methods +-- +-- NOTE: since `__len` is 5.2+, on 5.1 is usually returns `false` for userdata +-- @param val any value. +-- @return `true` if the object is array-like, otherwise a falsy value. +function types.is_indexable (val) + local mt = check_meta(val) + if mt == true then return true end + return mt and mt.__len and mt.__index and true +end + +--- can an object be iterated over with `pairs`? +-- An object is iterable if: +-- +-- - it is a table, or +-- - it has a metatable with a `__pairs` meta method +-- +-- NOTE: since `__pairs` is 5.2+, on 5.1 is usually returns `false` for userdata +-- @param val any value. +-- @return `true` if the object is iterable, otherwise a falsy value. +function types.is_iterable (val) + local mt = check_meta(val) + if mt == true then return true end + return mt and mt.__pairs and true +end + +--- can an object accept new key/pair values? +-- An object is iterable if: +-- +-- - it is a table, or +-- - it has a metatable with a `__newindex` meta method +-- +-- @param val any value. +-- @return `true` if the object is writeable, otherwise a falsy value. +function types.is_writeable (val) + local mt = check_meta(val) + if mt == true then return true end + return mt and mt.__newindex and true +end + +-- Strings that should evaluate to true. -- TODO: add on/off ??? +local trues = { yes=true, y=true, ["true"]=true, t=true, ["1"]=true } +-- Conditions types should evaluate to true. +local true_types = { + boolean=function(o, true_strs, check_objs) return o end, + string=function(o, true_strs, check_objs) + o = o:lower() + if trues[o] then + return true + end + -- Check alternative user provided strings. + for _,v in ipairs(true_strs or {}) do + if type(v) == "string" and o == v:lower() then + return true + end + end + return false + end, + number=function(o, true_strs, check_objs) return o ~= 0 end, + table=function(o, true_strs, check_objs) if check_objs and next(o) ~= nil then return true end return false end +} +--- Convert to a boolean value. +-- True values are: +-- +-- * boolean: true. +-- * string: 'yes', 'y', 'true', 't', '1' or additional strings specified by `true_strs`. +-- * number: Any non-zero value. +-- * table: Is not empty and `check_objs` is true. +-- * everything else: Is not `nil` and `check_objs` is true. +-- +-- @param o The object to evaluate. +-- @param[opt] true_strs optional Additional strings that when matched should evaluate to true. Comparison is case insensitive. +-- This should be a List of strings. E.g. "ja" to support German. +-- @param[opt] check_objs True if objects should be evaluated. +-- @return true if the input evaluates to true, otherwise false. +function types.to_bool(o, true_strs, check_objs) + local true_func + if true_strs then + assert_arg(2, true_strs, "table") + end + true_func = true_types[type(o)] + if true_func then + return true_func(o, true_strs, check_objs) + elseif check_objs and o ~= nil then + return true + end + return false +end + + +return types diff --git a/Data/Libraries/Penlight/lua/pl/url.lua b/Data/Libraries/Penlight/lua/pl/url.lua new file mode 100644 index 0000000..8c7cfeb --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/url.lua @@ -0,0 +1,51 @@ +--- Python-style URL quoting library. +-- +-- @module pl.url + +local url = {} + +local function quote_char(c) + return string.format("%%%02X", string.byte(c)) +end + +--- Quote the url, replacing special characters using the '%xx' escape. +-- @string s the string +-- @bool quote_plus Also escape slashes and replace spaces by plus signs. +-- @return The quoted string, or if `s` wasn't a string, just plain unaltered `s`. +function url.quote(s, quote_plus) + if type(s) ~= "string" then + return s + end + + s = s:gsub("\n", "\r\n") + s = s:gsub("([^A-Za-z0-9 %-_%./])", quote_char) + if quote_plus then + s = s:gsub(" ", "+") + s = s:gsub("/", quote_char) + else + s = s:gsub(" ", "%%20") + end + + return s +end + +local function unquote_char(h) + return string.char(tonumber(h, 16)) +end + +--- Unquote the url, replacing '%xx' escapes and plus signs. +-- @string s the string +-- @return The unquoted string, or if `s` wasn't a string, just plain unaltered `s`. +function url.unquote(s) + if type(s) ~= "string" then + return s + end + + s = s:gsub("+", " ") + s = s:gsub("%%(%x%x)", unquote_char) + s = s:gsub("\r\n", "\n") + + return s +end + +return url diff --git a/Data/Libraries/Penlight/lua/pl/utils.lua b/Data/Libraries/Penlight/lua/pl/utils.lua new file mode 100644 index 0000000..66edda6 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/utils.lua @@ -0,0 +1,777 @@ +--- Generally useful routines. +-- See @{01-introduction.md.Generally_useful_functions|the Guide}. +-- +-- Dependencies: `pl.compat`, all exported fields and functions from +-- `pl.compat` are also available in this module. +-- +-- @module pl.utils +local format = string.format +local compat = require 'pl.compat' +local stdout = io.stdout +local append = table.insert +local _unpack = table.unpack -- always injected by 'compat' + +local is_windows = compat.is_windows +local err_mode = 'default' +local raise +local operators +local _function_factories = {} + + +local utils = { _VERSION = "1.11.0" } +for k, v in pairs(compat) do utils[k] = v end + +--- Some standard patterns +-- @table patterns +utils.patterns = { + FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*', -- floating point number + INTEGER = '[+%-%d]%d*', -- integer number + IDEN = '[%a_][%w_]*', -- identifier + FILE = '[%a%.\\][:%][%w%._%-\\]*', -- file +} + + +--- Standard meta-tables as used by other Penlight modules +-- @table stdmt +-- @field List the List metatable +-- @field Map the Map metatable +-- @field Set the Set metatable +-- @field MultiMap the MultiMap metatable +utils.stdmt = { + List = {_name='List'}, + Map = {_name='Map'}, + Set = {_name='Set'}, + MultiMap = {_name='MultiMap'}, +} + + +--- pack an argument list into a table. +-- @param ... any arguments +-- @return a table with field `n` set to the length +-- @function utils.pack +-- @see compat.pack +-- @see utils.npairs +-- @see utils.unpack +utils.pack = table.pack -- added here to be symmetrical with unpack + +--- unpack a table and return its contents. +-- +-- NOTE: this implementation differs from the Lua implementation in the way +-- that this one DOES honor the `n` field in the table `t`, such that it is 'nil-safe'. +-- @param t table to unpack +-- @param[opt] i index from which to start unpacking, defaults to 1 +-- @param[opt] t index of the last element to unpack, defaults to `t.n` or `#t` +-- @return multiple return values from the table +-- @function utils.unpack +-- @see compat.unpack +-- @see utils.pack +-- @see utils.npairs +-- @usage +-- local t = table.pack(nil, nil, nil, 4) +-- local a, b, c, d = table.unpack(t) -- this `unpack` is NOT nil-safe, so d == nil +-- +-- local a, b, c, d = utils.unpack(t) -- this is nil-safe, so d == 4 +function utils.unpack(t, i, j) + return _unpack(t, i or 1, j or t.n or #t) +end + +--- print an arbitrary number of arguments using a format. +-- Output will be sent to `stdout`. +-- @param fmt The format (see `string.format`) +-- @param ... Extra arguments for format +function utils.printf(fmt, ...) + utils.assert_string(1, fmt) + utils.fprintf(stdout, fmt, ...) +end + +--- write an arbitrary number of arguments to a file using a format. +-- @param f File handle to write to. +-- @param fmt The format (see `string.format`). +-- @param ... Extra arguments for format +function utils.fprintf(f,fmt,...) + utils.assert_string(2,fmt) + f:write(format(fmt,...)) +end + +do + local function import_symbol(T,k,v,libname) + local key = rawget(T,k) + -- warn about collisions! + if key and k ~= '_M' and k ~= '_NAME' and k ~= '_PACKAGE' and k ~= '_VERSION' then + utils.fprintf(io.stderr,"warning: '%s.%s' will not override existing symbol\n",libname,k) + return + end + rawset(T,k,v) + end + + local function lookup_lib(T,t) + for k,v in pairs(T) do + if v == t then return k end + end + return '?' + end + + local already_imported = {} + + --- take a table and 'inject' it into the local namespace. + -- @param t The table (table), or module name (string), defaults to this `utils` module table + -- @param T An optional destination table (defaults to callers environment) + function utils.import(t,T) + T = T or _G + t = t or utils + if type(t) == 'string' then + t = require (t) + end + local libname = lookup_lib(T,t) + if already_imported[t] then return end + already_imported[t] = libname + for k,v in pairs(t) do + import_symbol(T,k,v,libname) + end + end +end + +--- return either of two values, depending on a condition. +-- @param cond A condition +-- @param value1 Value returned if cond is truthy +-- @param value2 Value returned if cond is falsy +function utils.choose(cond, value1, value2) + return cond and value1 or value2 +end + +--- convert an array of values to strings. +-- @param t a list-like table +-- @param[opt] temp (table) buffer to use, otherwise allocate +-- @param[opt] tostr custom tostring function, called with (value,index). Defaults to `tostring`. +-- @return the converted buffer +function utils.array_tostring (t,temp,tostr) + temp, tostr = temp or {}, tostr or tostring + for i = 1,#t do + temp[i] = tostr(t[i],i) + end + return temp +end + + + +--- is the object of the specified type? +-- If the type is a string, then use type, otherwise compare with metatable +-- @param obj An object to check +-- @param tp String of what type it should be +-- @return boolean +-- @usage utils.is_type("hello world", "string") --> true +-- -- or check metatable +-- local my_mt = {} +-- local my_obj = setmetatable(my_obj, my_mt) +-- utils.is_type(my_obj, my_mt) --> true +function utils.is_type (obj,tp) + if type(tp) == 'string' then return type(obj) == tp end + local mt = getmetatable(obj) + return tp == mt +end + + + +--- an iterator with indices, similar to `ipairs`, but with a range. +-- This is a nil-safe index based iterator that will return `nil` when there +-- is a hole in a list. To be safe ensure that table `t.n` contains the length. +-- @tparam table t the table to iterate over +-- @tparam[opt=1] integer i_start start index +-- @tparam[opt=t.n or #t] integer i_end end index +-- @tparam[opt=1] integer step step size +-- @treturn integer index +-- @treturn any value at index (which can be `nil`!) +-- @see utils.pack +-- @see utils.unpack +-- @usage +-- local t = utils.pack(nil, 123, nil) -- adds an `n` field when packing +-- +-- for i, v in utils.npairs(t, 2) do -- start at index 2 +-- t[i] = tostring(t[i]) +-- end +-- +-- -- t = { n = 3, [2] = "123", [3] = "nil" } +function utils.npairs(t, i_start, i_end, step) + step = step or 1 + if step == 0 then + error("iterator step-size cannot be 0", 2) + end + local i = (i_start or 1) - step + i_end = i_end or t.n or #t + if step < 0 then + return function() + i = i + step + if i < i_end then + return nil + end + return i, t[i] + end + + else + return function() + i = i + step + if i > i_end then + return nil + end + return i, t[i] + end + end +end + + + +--- Error handling +-- @section Error-handling + +--- assert that the given argument is in fact of the correct type. +-- @param n argument index +-- @param val the value +-- @param tp the type +-- @param verify an optional verification function +-- @param msg an optional custom message +-- @param lev optional stack position for trace, default 2 +-- @return the validated value +-- @raise if `val` is not the correct type +-- @usage +-- local param1 = assert_arg(1,"hello",'table') --> error: argument 1 expected a 'table', got a 'string' +-- local param4 = assert_arg(4,'!@#$%^&*','string',path.isdir,'not a directory') +-- --> error: argument 4: '!@#$%^&*' not a directory +function utils.assert_arg (n,val,tp,verify,msg,lev) + if type(val) ~= tp then + error(("argument %d expected a '%s', got a '%s'"):format(n,tp,type(val)),lev or 2) + end + if verify and not verify(val) then + error(("argument %d: '%s' %s"):format(n,val,msg),lev or 2) + end + return val +end + +--- process a function argument. +-- This is used throughout Penlight and defines what is meant by a function: +-- Something that is callable, or an operator string as defined by <code>pl.operator</code>, +-- such as '>' or '#'. If a function factory has been registered for the type, it will +-- be called to get the function. +-- @param idx argument index +-- @param f a function, operator string, or callable object +-- @param msg optional error message +-- @return a callable +-- @raise if idx is not a number or if f is not callable +function utils.function_arg (idx,f,msg) + utils.assert_arg(1,idx,'number') + local tp = type(f) + if tp == 'function' then return f end -- no worries! + -- ok, a string can correspond to an operator (like '==') + if tp == 'string' then + if not operators then operators = require 'pl.operator'.optable end + local fn = operators[f] + if fn then return fn end + local fn, err = utils.string_lambda(f) + if not fn then error(err..': '..f) end + return fn + elseif tp == 'table' or tp == 'userdata' then + local mt = getmetatable(f) + if not mt then error('not a callable object',2) end + local ff = _function_factories[mt] + if not ff then + if not mt.__call then error('not a callable object',2) end + return f + else + return ff(f) -- we have a function factory for this type! + end + end + if not msg then msg = " must be callable" end + if idx > 0 then + error("argument "..idx..": "..msg,2) + else + error(msg,2) + end +end + + +--- assert the common case that the argument is a string. +-- @param n argument index +-- @param val a value that must be a string +-- @return the validated value +-- @raise val must be a string +-- @usage +-- local val = 42 +-- local param2 = utils.assert_string(2, val) --> error: argument 2 expected a 'string', got a 'number' +function utils.assert_string (n, val) + return utils.assert_arg(n,val,'string',nil,nil,3) +end + +--- control the error strategy used by Penlight. +-- This is a global setting that controls how `utils.raise` behaves: +-- +-- - 'default': return `nil + error` (this is the default) +-- - 'error': throw a Lua error +-- - 'quit': exit the program +-- +-- @param mode either 'default', 'quit' or 'error' +-- @see utils.raise +function utils.on_error (mode) + mode = tostring(mode) + if ({['default'] = 1, ['quit'] = 2, ['error'] = 3})[mode] then + err_mode = mode + else + -- fail loudly + local err = "Bad argument expected string; 'default', 'quit', or 'error'. Got '"..tostring(mode).."'" + if err_mode == 'default' then + error(err, 2) -- even in 'default' mode fail loud in this case + end + raise(err) + end +end + +--- used by Penlight functions to return errors. Its global behaviour is controlled +-- by `utils.on_error`. +-- To use this function you MUST use it in conjunction with `return`, since it might +-- return `nil + error`. +-- @param err the error string. +-- @see utils.on_error +-- @usage +-- if some_condition then +-- return utils.raise("some condition was not met") -- MUST use 'return'! +-- end +function utils.raise (err) + if err_mode == 'default' then + return nil, err + elseif err_mode == 'quit' then + return utils.quit(err) + else + error(err, 2) + end +end +raise = utils.raise + + + +--- File handling +-- @section files + +--- return the contents of a file as a string +-- @param filename The file path +-- @param is_bin open in binary mode +-- @return file contents +function utils.readfile(filename,is_bin) + local mode = is_bin and 'b' or '' + utils.assert_string(1,filename) + local f,open_err = io.open(filename,'r'..mode) + if not f then return raise (open_err) end + local res,read_err = f:read('*a') + f:close() + if not res then + -- Errors in io.open have "filename: " prefix, + -- error in file:read don't, add it. + return raise (filename..": "..read_err) + end + return res +end + +--- write a string to a file +-- @param filename The file path +-- @param str The string +-- @param is_bin open in binary mode +-- @return true or nil +-- @return error message +-- @raise error if filename or str aren't strings +function utils.writefile(filename,str,is_bin) + local mode = is_bin and 'b' or '' + utils.assert_string(1,filename) + utils.assert_string(2,str) + local f,err = io.open(filename,'w'..mode) + if not f then return raise(err) end + local ok, write_err = f:write(str) + f:close() + if not ok then + -- Errors in io.open have "filename: " prefix, + -- error in file:write don't, add it. + return raise (filename..": "..write_err) + end + return true +end + +--- return the contents of a file as a list of lines +-- @param filename The file path +-- @return file contents as a table +-- @raise error if filename is not a string +function utils.readlines(filename) + utils.assert_string(1,filename) + local f,err = io.open(filename,'r') + if not f then return raise(err) end + local res = {} + for line in f:lines() do + append(res,line) + end + f:close() + return res +end + +--- OS functions +-- @section OS-functions + +--- execute a shell command and return the output. +-- This function redirects the output to tempfiles and returns the content of those files. +-- @param cmd a shell command +-- @param bin boolean, if true, read output as binary file +-- @return true if successful +-- @return actual return code +-- @return stdout output (string) +-- @return errout output (string) +function utils.executeex(cmd, bin) + local outfile = os.tmpname() + local errfile = os.tmpname() + + if is_windows and not outfile:find(':') then + outfile = os.getenv('TEMP')..outfile + errfile = os.getenv('TEMP')..errfile + end + cmd = cmd .. " > " .. utils.quote_arg(outfile) .. " 2> " .. utils.quote_arg(errfile) + + local success, retcode = utils.execute(cmd) + local outcontent = utils.readfile(outfile, bin) + local errcontent = utils.readfile(errfile, bin) + os.remove(outfile) + os.remove(errfile) + return success, retcode, (outcontent or ""), (errcontent or "") +end + +--- Quote and escape an argument of a command. +-- Quotes a single (or list of) argument(s) of a command to be passed +-- to `os.execute`, `pl.utils.execute` or `pl.utils.executeex`. +-- @param argument (string or table/list) the argument to quote. If a list then +-- all arguments in the list will be returned as a single string quoted. +-- @return quoted and escaped argument. +-- @usage +-- local options = utils.quote_arg { +-- "-lluacov", +-- "-e", +-- "utils = print(require('pl.utils')._VERSION", +-- } +-- -- returns: -lluacov -e 'utils = print(require('\''pl.utils'\'')._VERSION' +function utils.quote_arg(argument) + if type(argument) == "table" then + -- encode an entire table + local r = {} + for i, arg in ipairs(argument) do + r[i] = utils.quote_arg(arg) + end + + return table.concat(r, " ") + end + -- only a single argument + if is_windows then + if argument == "" or argument:find('[ \f\t\v]') then + -- Need to quote the argument. + -- Quotes need to be escaped with backslashes; + -- additionally, backslashes before a quote, escaped or not, + -- need to be doubled. + -- See documentation for CommandLineToArgvW Windows function. + argument = '"' .. argument:gsub([[(\*)"]], [[%1%1\"]]):gsub([[\+$]], "%0%0") .. '"' + end + + -- os.execute() uses system() C function, which on Windows passes command + -- to cmd.exe. Escape its special characters. + return (argument:gsub('["^<>!|&%%]', "^%0")) + else + if argument == "" or argument:find('[^a-zA-Z0-9_@%+=:,./-]') then + -- To quote arguments on posix-like systems use single quotes. + -- To represent an embedded single quote close quoted string ('), + -- add escaped quote (\'), open quoted string again ('). + argument = "'" .. argument:gsub("'", [['\'']]) .. "'" + end + + return argument + end +end + +--- error out of this program gracefully. +-- @param[opt] code The exit code, defaults to -`1` if omitted +-- @param msg The exit message will be sent to `stderr` (will be formatted with the extra parameters) +-- @param ... extra arguments for message's format' +-- @see utils.fprintf +-- @usage utils.quit(-1, "Error '%s' happened", "42") +-- -- is equivalent to +-- utils.quit("Error '%s' happened", "42") --> Error '42' happened +function utils.quit(code, msg, ...) + if type(code) == 'string' then + utils.fprintf(io.stderr, code, msg, ...) + io.stderr:write('\n') + code = -1 -- TODO: this is odd, see the test. Which returns 255 as exit code + elseif msg then + utils.fprintf(io.stderr, msg, ...) + io.stderr:write('\n') + end + os.exit(code, true) +end + + +--- String functions +-- @section string-functions + +--- escape any Lua 'magic' characters in a string +-- @param s The input string +function utils.escape(s) + utils.assert_string(1,s) + return (s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1')) +end + +--- split a string into a list of strings separated by a delimiter. +-- @param s The input string +-- @param re optional A Lua string pattern; defaults to '%s+' +-- @param plain optional If truthy don't use Lua patterns +-- @param n optional maximum number of elements (if there are more, the last will remian un-split) +-- @return a list-like table +-- @raise error if s is not a string +-- @see splitv +function utils.split(s,re,plain,n) + utils.assert_string(1,s) + local find,sub,append = string.find, string.sub, table.insert + local i1,ls = 1,{} + if not re then re = '%s+' end + if re == '' then return {s} end + while true do + local i2,i3 = find(s,re,i1,plain) + if not i2 then + local last = sub(s,i1) + if last ~= '' then append(ls,last) end + if #ls == 1 and ls[1] == '' then + return {} + else + return ls + end + end + append(ls,sub(s,i1,i2-1)) + if n and #ls == n then + ls[#ls] = sub(s,i1) + return ls + end + i1 = i3+1 + end +end + +--- split a string into a number of return values. +-- Identical to `split` but returns multiple sub-strings instead of +-- a single list of sub-strings. +-- @param s the string +-- @param re A Lua string pattern; defaults to '%s+' +-- @param plain don't use Lua patterns +-- @param n optional maximum number of splits +-- @return n values +-- @usage first,next = splitv('user=jane=doe','=', false, 2) +-- assert(first == "user") +-- assert(next == "jane=doe") +-- @see split +function utils.splitv (s,re, plain, n) + return _unpack(utils.split(s,re, plain, n)) +end + + +--- Functional +-- @section functional + + +--- 'memoize' a function (cache returned value for next call). +-- This is useful if you have a function which is relatively expensive, +-- but you don't know in advance what values will be required, so +-- building a table upfront is wasteful/impossible. +-- @param func a function of at least one argument +-- @return a function with at least one argument, which is used as the key. +function utils.memoize(func) + local cache = {} + return function(k) + local res = cache[k] + if res == nil then + res = func(k) + cache[k] = res + end + return res + end +end + + +--- associate a function factory with a type. +-- A function factory takes an object of the given type and +-- returns a function for evaluating it +-- @tab mt metatable +-- @func fun a callable that returns a function +function utils.add_function_factory (mt,fun) + _function_factories[mt] = fun +end + +local function _string_lambda(f) + if f:find '^|' or f:find '_' then + local args,body = f:match '|([^|]*)|(.+)' + if f:find '_' then + args = '_' + body = f + else + if not args then return raise 'bad string lambda' end + end + local fstr = 'return function('..args..') return '..body..' end' + local fn,err = utils.load(fstr) + if not fn then return raise(err) end + fn = fn() + return fn + else + return raise 'not a string lambda' + end +end + + +--- an anonymous function as a string. This string is either of the form +-- '|args| expression' or is a function of one argument, '_' +-- @param lf function as a string +-- @return a function +-- @function utils.string_lambda +-- @usage +-- string_lambda '|x|x+1' (2) == 3 +-- string_lambda '_+1' (2) == 3 +utils.string_lambda = utils.memoize(_string_lambda) + + +--- bind the first argument of the function to a value. +-- @param fn a function of at least two values (may be an operator string) +-- @param p a value +-- @return a function such that f(x) is fn(p,x) +-- @raise same as @{function_arg} +-- @see func.bind1 +-- @usage local function f(msg, name) +-- print(msg .. " " .. name) +-- end +-- +-- local hello = utils.bind1(f, "Hello") +-- +-- print(hello("world")) --> "Hello world" +-- print(hello("sunshine")) --> "Hello sunshine" +function utils.bind1 (fn,p) + fn = utils.function_arg(1,fn) + return function(...) return fn(p,...) end +end + + +--- bind the second argument of the function to a value. +-- @param fn a function of at least two values (may be an operator string) +-- @param p a value +-- @return a function such that f(x) is fn(x,p) +-- @raise same as @{function_arg} +-- @usage local function f(a, b, c) +-- print(a .. " " .. b .. " " .. c) +-- end +-- +-- local hello = utils.bind1(f, "world") +-- +-- print(hello("Hello", "!")) --> "Hello world !" +-- print(hello("Bye", "?")) --> "Bye world ?" +function utils.bind2 (fn,p) + fn = utils.function_arg(1,fn) + return function(x,...) return fn(x,p,...) end +end + + + + +--- Deprecation +-- @section deprecation + +do + -- the default implementation + local deprecation_func = function(msg, trace) + if trace then + warn(msg, "\n", trace) -- luacheck: ignore + else + warn(msg) -- luacheck: ignore + end + end + + --- Sets a deprecation warning function. + -- An application can override this function to support proper output of + -- deprecation warnings. The warnings can be generated from libraries or + -- functions by calling `utils.raise_deprecation`. The default function + -- will write to the 'warn' system (introduced in Lua 5.4, or the compatibility + -- function from the `compat` module for earlier versions). + -- + -- Note: only applications should set/change this function, libraries should not. + -- @param func a callback with signature: `function(msg, trace)` both arguments are strings, the latter being optional. + -- @see utils.raise_deprecation + -- @usage + -- -- write to the Nginx logs with OpenResty + -- utils.set_deprecation_func(function(msg, trace) + -- ngx.log(ngx.WARN, msg, (trace and (" " .. trace) or nil)) + -- end) + -- + -- -- disable deprecation warnings + -- utils.set_deprecation_func() + function utils.set_deprecation_func(func) + if func == nil then + deprecation_func = function() end + else + utils.assert_arg(1, func, "function") + deprecation_func = func + end + end + + --- raises a deprecation warning. + -- For options see the usage example below. + -- + -- Note: the `opts.deprecated_after` field is the last version in which + -- a feature or option was NOT YET deprecated! Because when writing the code it + -- is quite often not known in what version the code will land. But the last + -- released version is usually known. + -- @param opts options table + -- @see utils.set_deprecation_func + -- @usage + -- warn("@on") -- enable Lua warnings, they are usually off by default + -- + -- function stringx.islower(str) + -- raise_deprecation { + -- source = "Penlight " .. utils._VERSION, -- optional + -- message = "function 'islower' was renamed to 'is_lower'", -- required + -- version_removed = "2.0.0", -- optional + -- deprecated_after = "1.2.3", -- optional + -- no_trace = true, -- optional + -- } + -- return stringx.is_lower(str) + -- end + -- -- output: "[Penlight 1.9.2] function 'islower' was renamed to 'is_lower' (deprecated after 1.2.3, scheduled for removal in 2.0.0)" + function utils.raise_deprecation(opts) + utils.assert_arg(1, opts, "table") + if type(opts.message) ~= "string" then + error("field 'message' of the options table must be a string", 2) + end + local trace + if not opts.no_trace then + trace = debug.traceback("", 2):match("[\n%s]*(.-)$") + end + local msg + if opts.deprecated_after and opts.version_removed then + msg = (" (deprecated after %s, scheduled for removal in %s)"):format( + tostring(opts.deprecated_after), tostring(opts.version_removed)) + elseif opts.deprecated_after then + msg = (" (deprecated after %s)"):format(tostring(opts.deprecated_after)) + elseif opts.version_removed then + msg = (" (scheduled for removal in %s)"):format(tostring(opts.version_removed)) + else + msg = "" + end + + msg = opts.message .. msg + + if opts.source then + msg = "[" .. opts.source .."] " .. msg + else + if msg:sub(1,1) == "@" then + -- in Lua 5.4 "@" prefixed messages are control messages to the warn system + error("message cannot start with '@'", 2) + end + end + + deprecation_func(msg, trace) + end + +end + + +return utils + + diff --git a/Data/Libraries/Penlight/lua/pl/xml.lua b/Data/Libraries/Penlight/lua/pl/xml.lua new file mode 100644 index 0000000..4650a32 --- /dev/null +++ b/Data/Libraries/Penlight/lua/pl/xml.lua @@ -0,0 +1,776 @@ +--- XML LOM Utilities. +-- +-- This implements some useful things on [LOM](http://matthewwild.co.uk/projects/luaexpat/lom.html) documents, such as returned by `lxp.lom.parse`. +-- In particular, it can convert LOM back into XML text, with optional pretty-printing control. +-- It is s based on stanza.lua from [Prosody](http://hg.prosody.im/trunk/file/4621c92d2368/util/stanza.lua) +-- +-- > d = xml.parse "<nodes><node id='1'>alice</node></nodes>" +-- > = d +-- <nodes><node id='1'>alice</node></nodes> +-- > = xml.tostring(d,'',' ') +-- <nodes> +-- <node id='1'>alice</node> +-- </nodes> +-- +-- Can be used as a lightweight one-stop-shop for simple XML processing; a simple XML parser is included +-- but the default is to use `lxp.lom` if it can be found. +-- <pre> +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain-- +-- classic Lua XML parser by Roberto Ierusalimschy. +-- modified to output LOM format. +-- http://lua-users.org/wiki/LuaXml +-- </pre> +-- See @{06-data.md.XML|the Guide} +-- +-- Dependencies: `pl.utils` +-- +-- Soft Dependencies: `lxp.lom` (fallback is to use basic Lua parser) +-- @module pl.xml + +local utils = require 'pl.utils' +local split = utils.split; +local t_insert = table.insert; +local t_concat = table.concat; +local t_remove = table.remove; +local s_match = string.match; +local tostring = tostring; +local setmetatable = setmetatable; +local getmetatable = getmetatable; +local pairs = pairs; +local ipairs = ipairs; +local type = type; +local next = next; +local print = print; +local unpack = utils.unpack; +local s_gsub = string.gsub; +local s_find = string.find; +local pcall,require,io = pcall,require,io + +local _M = {} +local Doc = { __type = "doc" }; +Doc.__index = Doc; + +--- create a new document node. +-- @param tag the tag name +-- @param attr optional attributes (table of name-value pairs) +function _M.new(tag, attr) + local doc = { tag = tag, attr = attr or {}, last_add = {}}; + return setmetatable(doc, Doc); +end + +--- parse an XML document. By default, this uses lxp.lom.parse, but +-- falls back to basic_parse, or if use_basic is true +-- @param text_or_file file or string representation +-- @param is_file whether text_or_file is a file name or not +-- @param use_basic do a basic parse +-- @return a parsed LOM document with the document metatatables set +-- @return nil, error the error can either be a file error or a parse error +function _M.parse(text_or_file, is_file, use_basic) + local parser,status,lom + if use_basic then parser = _M.basic_parse + else + status,lom = pcall(require,'lxp.lom') + if not status then parser = _M.basic_parse else parser = lom.parse end + end + if is_file then + local f,err = io.open(text_or_file) + if not f then return nil,err end + text_or_file = f:read '*a' + f:close() + end + local doc,err = parser(text_or_file) + if not doc then return nil,err end + if lom then + _M.walk(doc,false,function(_,d) + setmetatable(d,Doc) + end) + end + return doc +end + +---- convenient function to add a document node, This updates the last inserted position. +-- @param tag a tag name +-- @param attrs optional set of attributes (name-string pairs) +function Doc:addtag(tag, attrs) + local s = _M.new(tag, attrs); + (self.last_add[#self.last_add] or self):add_direct_child(s); + t_insert(self.last_add, s); + return self; +end + +--- convenient function to add a text node. This updates the last inserted position. +-- @param text a string +function Doc:text(text) + (self.last_add[#self.last_add] or self):add_direct_child(text); + return self; +end + +---- go up one level in a document +function Doc:up() + t_remove(self.last_add); + return self; +end + +function Doc:reset() + local last_add = self.last_add; + for i = 1,#last_add do + last_add[i] = nil; + end + return self; +end + +--- append a child to a document directly. +-- @param child a child node (either text or a document) +function Doc:add_direct_child(child) + t_insert(self, child); +end + +--- append a child to a document at the last element added +-- @param child a child node (either text or a document) +function Doc:add_child(child) + (self.last_add[#self.last_add] or self):add_direct_child(child); + return self; +end + +--accessing attributes: useful not to have to expose implementation (attr) +--but also can allow attr to be nil in any future optimizations + +--- set attributes of a document node. +-- @param t a table containing attribute/value pairs +function Doc:set_attribs (t) + for k,v in pairs(t) do + self.attr[k] = v + end +end + +--- set a single attribute of a document node. +-- @param a attribute +-- @param v its value +function Doc:set_attrib(a,v) + self.attr[a] = v +end + +--- access the attributes of a document node. +function Doc:get_attribs() + return self.attr +end + +local function is_text(s) return type(s) == 'string' end + +--- function to create an element with a given tag name and a set of children. +-- @param tag a tag name +-- @param items either text or a table where the hash part is the attributes and the list part is the children. +function _M.elem(tag,items) + local s = _M.new(tag) + if is_text(items) then items = {items} end + if _M.is_tag(items) then + t_insert(s,items) + elseif type(items) == 'table' then + for k,v in pairs(items) do + if is_text(k) then + s.attr[k] = v + t_insert(s.attr,k) + else + s[k] = v + end + end + end + return s +end + +--- given a list of names, return a number of element constructors. +-- @param list a list of names, or a comma-separated string. +-- @usage local parent,children = doc.tags 'parent,children' <br> +-- doc = parent {child 'one', child 'two'} +function _M.tags(list) + local ctors = {} + if is_text(list) then list = split(list,'%s*,%s*') end + for _,tag in ipairs(list) do + local ctor = function(items) return _M.elem(tag,items) end + t_insert(ctors,ctor) + end + return unpack(ctors) +end + +local templ_cache = {} + +local function template_cache (templ) + if is_text(templ) then + if templ_cache[templ] then + templ = templ_cache[templ] + else + local str,err = templ + templ,err = _M.parse(str,false,true) + if not templ then return nil,err end + templ_cache[str] = templ + end + elseif not _M.is_tag(templ) then + return nil, "template is not a document" + end + return templ +end + +local function is_data(data) + return #data == 0 or type(data[1]) ~= 'table' +end + +local function prepare_data(data) + -- a hack for ensuring that $1 maps to first element of data, etc. + -- Either this or could change the gsub call just below. + for i,v in ipairs(data) do + data[tostring(i)] = v + end +end + +--- create a substituted copy of a document, +-- @param templ may be a document or a string representation which will be parsed and cached +-- @param data a table of name-value pairs or a list of such tables +-- @return an XML document +function Doc.subst(templ, data) + local err + if type(data) ~= 'table' or not next(data) then return nil, "data must be a non-empty table" end + if is_data(data) then + prepare_data(data) + end + templ,err = template_cache(templ) + if err then return nil, err end + local function _subst(item) + return _M.clone(templ,function(s) + return s:gsub('%$(%w+)',item) + end) + end + if is_data(data) then return _subst(data) end + local list = {} + for _,item in ipairs(data) do + prepare_data(item) + t_insert(list,_subst(item)) + end + if data.tag then + list = _M.elem(data.tag,list) + end + return list +end + + +--- get the first child with a given tag name. +-- @param tag the tag name +function Doc:child_with_name(tag) + for _, child in ipairs(self) do + if child.tag == tag then return child; end + end +end + +local _children_with_name +function _children_with_name(self,tag,list,recurse) + for _, child in ipairs(self) do if type(child) == 'table' then + if child.tag == tag then t_insert(list,child) end + if recurse then _children_with_name(child,tag,list,recurse) end + end end +end + +--- get all elements in a document that have a given tag. +-- @param tag a tag name +-- @param dont_recurse optionally only return the immediate children with this tag name +-- @return a list of elements +function Doc:get_elements_with_name(tag,dont_recurse) + local res = {} + _children_with_name(self,tag,res,not dont_recurse) + return res +end + +-- iterate over all children of a document node, including text nodes. +function Doc:children() + local i = 0; + return function (a) + i = i + 1 + return a[i]; + end, self, i; +end + +-- return the first child element of a node, if it exists. +function Doc:first_childtag() + if #self == 0 then return end + for _,t in ipairs(self) do + if type(t) == 'table' then return t end + end +end + +function Doc:matching_tags(tag, xmlns) + xmlns = xmlns or self.attr.xmlns; + local tags = self; + local start_i, max_i, v = 1, #tags; + return function () + for i=start_i,max_i do + v = tags[i]; + if (not tag or v.tag == tag) + and (not xmlns or xmlns == v.attr.xmlns) then + start_i = i+1; + return v; + end + end + end, tags, start_i; +end + +--- iterate over all child elements of a document node. +function Doc:childtags() + local i = 0; + return function (a) + local v + repeat + i = i + 1 + v = self[i] + if v and type(v) == 'table' then return v; end + until not v + end, self[1], i; +end + +--- visit child element of a node and call a function, possibility modifying the document. +-- @param callback a function passed the node (text or element). If it returns nil, that node will be removed. +-- If it returns a value, that will replace the current node. +function Doc:maptags(callback) + local is_tag = _M.is_tag + local i = 1; + while i <= #self do + if is_tag(self[i]) then + local ret = callback(self[i]); + if ret == nil then + t_remove(self, i); + else + self[i] = ret; + i = i + 1; + end + end + end + return self; +end + +local xml_escape +do + local escape_table = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" }; + function xml_escape(str) return (s_gsub(str, "['&<>\"]", escape_table)); end + _M.xml_escape = xml_escape; +end + +-- pretty printing +-- if indent, then put each new tag on its own line +-- if attr_indent, put each new attribute on its own line +local function _dostring(t, buf, self, xml_escape, parentns, idn, indent, attr_indent) + local nsid = 0; + local tag = t.tag + local lf,alf = ""," " + if indent then lf = '\n'..idn end + if attr_indent then alf = '\n'..idn..attr_indent end + t_insert(buf, lf.."<"..tag); + local function write_attr(k,v) + if s_find(k, "\1", 1, true) then + local ns, attrk = s_match(k, "^([^\1]*)\1?(.*)$"); + nsid = nsid + 1; + t_insert(buf, " xmlns:ns"..nsid.."='"..xml_escape(ns).."' ".."ns"..nsid..":"..attrk.."='"..xml_escape(v).."'"); + elseif not(k == "xmlns" and v == parentns) then + t_insert(buf, alf..k.."='"..xml_escape(v).."'"); + end + end + -- it's useful for testing to have predictable attribute ordering, if available + if #t.attr > 0 then + for _,k in ipairs(t.attr) do + write_attr(k,t.attr[k]) + end + else + for k, v in pairs(t.attr) do + write_attr(k,v) + end + end + local len,has_children = #t; + if len == 0 then + local out = "/>" + if attr_indent then out = '\n'..idn..out end + t_insert(buf, out); + else + t_insert(buf, ">"); + for n=1,len do + local child = t[n]; + if child.tag then + self(child, buf, self, xml_escape, t.attr.xmlns,idn and idn..indent, indent, attr_indent ); + has_children = true + else -- text element + t_insert(buf, xml_escape(child)); + end + end + t_insert(buf, (has_children and lf or '').."</"..tag..">"); + end +end + +---- pretty-print an XML document +--- @param t an XML document +--- @param idn an initial indent (indents are all strings) +--- @param indent an indent for each level +--- @param attr_indent if given, indent each attribute pair and put on a separate line +--- @param xml force prefacing with default or custom <?xml...> +--- @return a string representation +function _M.tostring(t,idn,indent, attr_indent, xml) + local buf = {}; + if xml then + if type(xml) == "string" then + buf[1] = xml + else + buf[1] = "<?xml version='1.0'?>" + end + end + _dostring(t, buf, _dostring, xml_escape, nil,idn,indent, attr_indent); + return t_concat(buf); +end + +Doc.__tostring = _M.tostring + +--- get the full text value of an element +function Doc:get_text() + local res = {} + for i,el in ipairs(self) do + if is_text(el) then t_insert(res,el) end + end + return t_concat(res); +end + +--- make a copy of a document +-- @param doc the original document +-- @param strsubst an optional function for handling string copying which could do substitution, etc. +function _M.clone(doc, strsubst) + local lookup_table = {}; + local function _copy(object,kind,parent) + if type(object) ~= "table" then + if strsubst and is_text(object) then return strsubst(object,kind,parent) + else return object + end + elseif lookup_table[object] then + return lookup_table[object] + end + local new_table = {}; + lookup_table[object] = new_table + local tag = object.tag + new_table.tag = _copy(tag,'*TAG',parent) + if object.attr then + local res = {} + for attr,value in pairs(object.attr) do + res[attr] = _copy(value,attr,object) + end + new_table.attr = res + end + for index = 1,#object do + local v = _copy(object[index],'*TEXT',object) + t_insert(new_table,v) + end + return setmetatable(new_table, getmetatable(object)) + end + + return _copy(doc) +end + +Doc.filter = _M.clone -- also available as method + +--- compare two documents. +-- @param t1 any value +-- @param t2 any value +function _M.compare(t1,t2) + local ty1 = type(t1) + local ty2 = type(t2) + if ty1 ~= ty2 then return false, 'type mismatch' end + if ty1 == 'string' then + return t1 == t2 and true or 'text '..t1..' ~= text '..t2 + end + if ty1 ~= 'table' or ty2 ~= 'table' then return false, 'not a document' end + if t1.tag ~= t2.tag then return false, 'tag '..t1.tag..' ~= tag '..t2.tag end + if #t1 ~= #t2 then return false, 'size '..#t1..' ~= size '..#t2..' for tag '..t1.tag end + -- compare attributes + for k,v in pairs(t1.attr) do + if t2.attr[k] ~= v then return false, 'mismatch attrib' end + end + for k,v in pairs(t2.attr) do + if t1.attr[k] ~= v then return false, 'mismatch attrib' end + end + -- compare children + for i = 1,#t1 do + local yes,err = _M.compare(t1[i],t2[i]) + if not yes then return err end + end + return true +end + +--- is this value a document element? +-- @param d any value +function _M.is_tag(d) + return type(d) == 'table' and is_text(d.tag) +end + +--- call the desired function recursively over the document. +-- @param doc the document +-- @param depth_first visit child notes first, then the current node +-- @param operation a function which will receive the current tag name and current node. +function _M.walk (doc, depth_first, operation) + if not depth_first then operation(doc.tag,doc) end + for _,d in ipairs(doc) do + if _M.is_tag(d) then + _M.walk(d,depth_first,operation) + end + end + if depth_first then operation(doc.tag,doc) end +end + +local html_empty_elements = { --lists all HTML empty (void) elements + br = true, + img = true, + meta = true, + frame = true, + area = true, + hr = true, + base = true, + col = true, + link = true, + input = true, + option = true, + param = true, + isindex = true, + embed = true, +} + +local escapes = { quot = "\"", apos = "'", lt = "<", gt = ">", amp = "&" } +local function unescape(str) return (str:gsub( "&(%a+);", escapes)); end + +--- Parse a well-formed HTML file as a string. +-- Tags are case-insenstive, DOCTYPE is ignored, and empty elements can be .. empty. +-- @param s the HTML +function _M.parsehtml (s) + return _M.basic_parse(s,false,true) +end + +--- Parse a simple XML document using a pure Lua parser based on Robero Ierusalimschy's original version. +-- @param s the XML document to be parsed. +-- @param all_text if true, preserves all whitespace. Otherwise only text containing non-whitespace is included. +-- @param html if true, uses relaxed HTML rules for parsing +function _M.basic_parse(s,all_text,html) + local t_insert,t_remove = table.insert,table.remove + local s_find,s_sub = string.find,string.sub + local stack = {} + local top = {} + + local function parseargs(s) + local arg = {} + s:gsub("([%w:%-_]+)%s*=%s*([\"'])(.-)%2", function (w, _, a) + if html then w = w:lower() end + arg[w] = unescape(a) + end) + if html then + s:gsub("([%w:%-_]+)%s*=%s*([^\"']+)%s*", function (w, a) + w = w:lower() + arg[w] = unescape(a) + end) + end + return arg + end + + t_insert(stack, top) + local ni,c,label,xarg, empty, _, istart + local i = 1 + local j + -- we're not interested in <?xml version="1.0"?> + _,istart = s_find(s,'^%s*<%?[^%?]+%?>%s*') + if not istart then -- or <!DOCTYPE ...> + _,istart = s_find(s,'^%s*<!DOCTYPE.->%s*') + end + if istart then i = istart+1 end + while true do + ni,j,c,label,xarg, empty = s_find(s, "<([%/!]?)([%w:%-_]+)(.-)(%/?)>", i) + if not ni then break end + if c == "!" then -- comment + -- case where there's no space inside comment + if not (label:match '%-%-$' and xarg == '') then + if xarg:match '%-%-$' then -- we've grabbed it all + j = j - 2 + end + -- match end of comment + _,j = s_find(s, "-->", j, true) + end + else + local text = s_sub(s, i, ni-1) + if html then + label = label:lower() + if html_empty_elements[label] then empty = "/" end + end + if all_text or not s_find(text, "^%s*$") then + t_insert(top, unescape(text)) + end + if empty == "/" then -- empty element tag + t_insert(top, setmetatable({tag=label, attr=parseargs(xarg), empty=1},Doc)) + elseif c == "" then -- start tag + top = setmetatable({tag=label, attr=parseargs(xarg)},Doc) + t_insert(stack, top) -- new level + else -- end tag + local toclose = t_remove(stack) -- remove top + top = stack[#stack] + if #stack < 1 then + error("nothing to close with "..label..':'..text) + end + if toclose.tag ~= label then + error("trying to close "..toclose.tag.." with "..label.." "..text) + end + t_insert(top, toclose) + end + end + i = j+1 + end + local text = s_sub(s, i) + if all_text or not s_find(text, "^%s*$") then + t_insert(stack[#stack], unescape(text)) + end + if #stack > 1 then + error("unclosed "..stack[#stack].tag) + end + local res = stack[1] + return is_text(res[1]) and res[2] or res[1] +end + +local function empty(attr) return not attr or not next(attr) end +local function is_element(d) return type(d) == 'table' and d.tag ~= nil end + +-- returns the key,value pair from a table if it has exactly one entry +local function has_one_element(t) + local key,value = next(t) + if next(t,key) ~= nil then return false end + return key,value +end + +local function append_capture(res,tbl) + if not empty(tbl) then -- no point in capturing empty tables... + local key + if tbl._ then -- if $_ was set then it is meant as the top-level key for the captured table + key = tbl._ + tbl._ = nil + if empty(tbl) then return end + end + -- a table with only one pair {[0]=value} shall be reduced to that value + local numkey,val = has_one_element(tbl) + if numkey == 0 then tbl = val end + if key then + res[key] = tbl + else -- otherwise, we append the captured table + t_insert(res,tbl) + end + end +end + +local function make_number(pat) + if pat:find '^%d+$' then -- $1 etc means use this as an array location + pat = tonumber(pat) + end + return pat +end + +local function capture_attrib(res,pat,value) + pat = make_number(pat:sub(2)) + res[pat] = value + return true +end + +local match +function match(d,pat,res,keep_going) + local ret = true + if d == nil then d = '' end --return false end + -- attribute string matching is straight equality, except if the pattern is a $ capture, + -- which always succeeds. + if is_text(d) then + if not is_text(pat) then return false end + if _M.debug then print(d,pat) end + if pat:find '^%$' then + return capture_attrib(res,pat,d) + else + return d == pat + end + else + if _M.debug then print(d.tag,pat.tag) end + -- this is an element node. For a match to succeed, the attributes must + -- match as well. + -- a tagname in the pattern ending with '-' is a wildcard and matches like an attribute + local tagpat = pat.tag:match '^(.-)%-$' + if tagpat then + tagpat = make_number(tagpat) + res[tagpat] = d.tag + end + if d.tag == pat.tag or tagpat then + + if not empty(pat.attr) then + if empty(d.attr) then ret = false + else + for prop,pval in pairs(pat.attr) do + local dval = d.attr[prop] + if not match(dval,pval,res) then ret = false; break end + end + end + end + -- the pattern may have child nodes. We match partially, so that {P1,P2} shall match {X,P1,X,X,P2,..} + if ret and #pat > 0 then + local i,j = 1,1 + local function next_elem() + j = j + 1 -- next child element of data + if is_text(d[j]) then j = j + 1 end + return j <= #d + end + repeat + local p = pat[i] + -- repeated {{<...>}} patterns shall match one or more elements + -- so e.g. {P+} will match {X,X,P,P,X,P,X,X,X} + if is_element(p) and p.repeated then + local found + repeat + local tbl = {} + ret = match(d[j],p,tbl,false) + if ret then + found = false --true + append_capture(res,tbl) + end + until not next_elem() or (found and not ret) + i = i + 1 + else + ret = match(d[j],p,res,false) + if ret then i = i + 1 end + end + until not next_elem() or i > #pat -- run out of elements or patterns to match + -- if every element in our pattern matched ok, then it's been a successful match + if i > #pat then return true end + end + if ret then return true end + else + ret = false + end + -- keep going anyway - look at the children! + if keep_going then + for child in d:childtags() do + ret = match(child,pat,res,keep_going) + if ret then break end + end + end + end + return ret +end + +function Doc:match(pat) + local err + pat,err = template_cache(pat) + if not pat then return nil, err end + _M.walk(pat,false,function(_,d) + if is_text(d[1]) and is_element(d[2]) and is_text(d[3]) and + d[1]:find '%s*{{' and d[3]:find '}}%s*' then + t_remove(d,1) + t_remove(d,2) + d[1].repeated = true + end + end) + + local res = {} + local ret = match(self,pat,res,true) + return res,ret +end + + +return _M + diff --git a/Data/Libraries/Penlight/penlight-dev-1.rockspec b/Data/Libraries/Penlight/penlight-dev-1.rockspec new file mode 100644 index 0000000..343bfcb --- /dev/null +++ b/Data/Libraries/Penlight/penlight-dev-1.rockspec @@ -0,0 +1,89 @@ +local package_name = "penlight" +local package_version = "dev" +local rockspec_revision = "1" +local github_account_name = "lunarmodules" +local github_repo_name = package_name +local git_checkout = package_version == "dev" and "master" or package_version + + +rockspec_format = "3.0" +package = package_name +version = package_version .. "-" .. rockspec_revision + +source = { + url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git", + branch = git_checkout +} + +description = { + summary = "Lua utility libraries loosely based on the Python standard libraries", + detailed = [[ + Penlight is a set of pure Lua libraries focusing on input data handling + (such as reading configuration files), functional programming + (such as map, reduce, placeholder expressions,etc), and OS path management. + Much of the functionality is inspired by the Python standard libraries. + ]], + license = "MIT/X11", + homepage = "https://"..github_account_name..".github.io/"..github_repo_name, + issues_url = "https://github.com/"..github_account_name.."/"..github_repo_name.."/issues", + maintainer = "thijs@thijsschreijer.nl", +} + +dependencies = { + "lua >= 5.1", + "luafilesystem" +} + +test_dependencies = { + "busted", +} + +test = { + type = "busted", +} + +build = { + type = "builtin", + modules = { + ["pl"] = "lua/pl/init.lua", + ["pl.strict"] = "lua/pl/strict.lua", + ["pl.dir"] = "lua/pl/dir.lua", + ["pl.operator"] = "lua/pl/operator.lua", + ["pl.input"] = "lua/pl/input.lua", + ["pl.config"] = "lua/pl/config.lua", + ["pl.seq"] = "lua/pl/seq.lua", + ["pl.stringio"] = "lua/pl/stringio.lua", + ["pl.text"] = "lua/pl/text.lua", + ["pl.test"] = "lua/pl/test.lua", + ["pl.tablex"] = "lua/pl/tablex.lua", + ["pl.app"] = "lua/pl/app.lua", + ["pl.stringx"] = "lua/pl/stringx.lua", + ["pl.lexer"] = "lua/pl/lexer.lua", + ["pl.utils"] = "lua/pl/utils.lua", + ["pl.compat"] = "lua/pl/compat.lua", + ["pl.sip"] = "lua/pl/sip.lua", + ["pl.permute"] = "lua/pl/permute.lua", + ["pl.pretty"] = "lua/pl/pretty.lua", + ["pl.class"] = "lua/pl/class.lua", + ["pl.List"] = "lua/pl/List.lua", + ["pl.data"] = "lua/pl/data.lua", + ["pl.Date"] = "lua/pl/Date.lua", + ["pl.luabalanced"] = "lua/pl/luabalanced.lua", + ["pl.comprehension"] = "lua/pl/comprehension.lua", + ["pl.path"] = "lua/pl/path.lua", + ["pl.array2d"] = "lua/pl/array2d.lua", + ["pl.func"] = "lua/pl/func.lua", + ["pl.lapp"] = "lua/pl/lapp.lua", + ["pl.file"] = "lua/pl/file.lua", + ['pl.template'] = "lua/pl/template.lua", + ["pl.Map"] = "lua/pl/Map.lua", + ["pl.MultiMap"] = "lua/pl/MultiMap.lua", + ["pl.OrderedMap"] = "lua/pl/OrderedMap.lua", + ["pl.Set"] = "lua/pl/Set.lua", + ["pl.xml"] = "lua/pl/xml.lua", + ["pl.url"] = "lua/pl/url.lua", + ["pl.types"] = "lua/pl/types.lua", + ["pl.import_into"] = "lua/pl/import_into.lua" + }, + copy_directories = {"docs", "tests"} +} diff --git a/Data/Libraries/Penlight/rockspecs/penlight-1.10.0-1.rockspec b/Data/Libraries/Penlight/rockspecs/penlight-1.10.0-1.rockspec new file mode 100644 index 0000000..6bcc6ab --- /dev/null +++ b/Data/Libraries/Penlight/rockspecs/penlight-1.10.0-1.rockspec @@ -0,0 +1,78 @@ +local package_name = "penlight" +local package_version = "1.10.0" +local rockspec_revision = "1" +local github_account_name = "lunarmodules" +local github_repo_name = package_name +local git_checkout = package_version == "dev" and "master" or package_version + + +package = package_name +version = package_version .. "-" .. rockspec_revision + +source = { + url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git", + branch = git_checkout +} + +description = { + summary = "Lua utility libraries loosely based on the Python standard libraries", + homepage = "https://"..github_account_name..".github.io/"..github_repo_name, + license = "MIT/X11", + maintainer = "thijs@thijsschreijer.nl", + detailed = [[ +Penlight is a set of pure Lua libraries for making it easier to work with common tasks like +iterating over directories, reading configuration files and the like. Provides functional operations +on tables and sequences. +]] +} + +dependencies = { + "luafilesystem", +} + +build = { + type = "builtin", + modules = { + ["pl.strict"] = "lua/pl/strict.lua", + ["pl.dir"] = "lua/pl/dir.lua", + ["pl.operator"] = "lua/pl/operator.lua", + ["pl.input"] = "lua/pl/input.lua", + ["pl.config"] = "lua/pl/config.lua", + ["pl.compat"] = "lua/pl/config.lua", + ["pl.seq"] = "lua/pl/seq.lua", + ["pl.stringio"] = "lua/pl/stringio.lua", + ["pl.text"] = "lua/pl/text.lua", + ["pl.test"] = "lua/pl/test.lua", + ["pl.tablex"] = "lua/pl/tablex.lua", + ["pl.app"] = "lua/pl/app.lua", + ["pl.stringx"] = "lua/pl/stringx.lua", + ["pl.lexer"] = "lua/pl/lexer.lua", + ["pl.utils"] = "lua/pl/utils.lua", + ["pl.sip"] = "lua/pl/sip.lua", + ["pl.permute"] = "lua/pl/permute.lua", + ["pl.pretty"] = "lua/pl/pretty.lua", + ["pl.class"] = "lua/pl/class.lua", + ["pl.List"] = "lua/pl/List.lua", + ["pl.data"] = "lua/pl/data.lua", + ["pl.Date"] = "lua/pl/Date.lua", + ["pl.init"] = "lua/pl/init.lua", + ["pl.luabalanced"] = "lua/pl/luabalanced.lua", + ["pl.comprehension"] = "lua/pl/comprehension.lua", + ["pl.path"] = "lua/pl/path.lua", + ["pl.array2d"] = "lua/pl/array2d.lua", + ["pl.func"] = "lua/pl/func.lua", + ["pl.lapp"] = "lua/pl/lapp.lua", + ["pl.file"] = "lua/pl/file.lua", + ['pl.template'] = "lua/pl/template.lua", + ["pl.Map"] = "lua/pl/Map.lua", + ["pl.MultiMap"] = "lua/pl/MultiMap.lua", + ["pl.OrderedMap"] = "lua/pl/OrderedMap.lua", + ["pl.Set"] = "lua/pl/Set.lua", + ["pl.xml"] = "lua/pl/xml.lua", + ["pl.url"] = "lua/pl/url.lua", + ["pl.import_into"] = "lua/pl/import_into.lua", + ["pl.types"] = "lua/pl/types.lua", + }, + copy_directories = {"docs", "tests"} +} + diff --git a/Data/Libraries/Penlight/rockspecs/penlight-1.11.0-1.rockspec b/Data/Libraries/Penlight/rockspecs/penlight-1.11.0-1.rockspec new file mode 100644 index 0000000..253f8e6 --- /dev/null +++ b/Data/Libraries/Penlight/rockspecs/penlight-1.11.0-1.rockspec @@ -0,0 +1,78 @@ +local package_name = "penlight" +local package_version = "1.11.0" +local rockspec_revision = "1" +local github_account_name = "lunarmodules" +local github_repo_name = package_name +local git_checkout = package_version == "dev" and "master" or package_version + + +package = package_name +version = package_version .. "-" .. rockspec_revision + +source = { + url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git", + branch = git_checkout +} + +description = { + summary = "Lua utility libraries loosely based on the Python standard libraries", + homepage = "https://"..github_account_name..".github.io/"..github_repo_name, + license = "MIT/X11", + maintainer = "thijs@thijsschreijer.nl", + detailed = [[ +Penlight is a set of pure Lua libraries for making it easier to work with common tasks like +iterating over directories, reading configuration files and the like. Provides functional operations +on tables and sequences. +]] +} + +dependencies = { + "luafilesystem", +} + +build = { + type = "builtin", + modules = { + ["pl.strict"] = "lua/pl/strict.lua", + ["pl.dir"] = "lua/pl/dir.lua", + ["pl.operator"] = "lua/pl/operator.lua", + ["pl.input"] = "lua/pl/input.lua", + ["pl.config"] = "lua/pl/config.lua", + ["pl.compat"] = "lua/pl/config.lua", + ["pl.seq"] = "lua/pl/seq.lua", + ["pl.stringio"] = "lua/pl/stringio.lua", + ["pl.text"] = "lua/pl/text.lua", + ["pl.test"] = "lua/pl/test.lua", + ["pl.tablex"] = "lua/pl/tablex.lua", + ["pl.app"] = "lua/pl/app.lua", + ["pl.stringx"] = "lua/pl/stringx.lua", + ["pl.lexer"] = "lua/pl/lexer.lua", + ["pl.utils"] = "lua/pl/utils.lua", + ["pl.sip"] = "lua/pl/sip.lua", + ["pl.permute"] = "lua/pl/permute.lua", + ["pl.pretty"] = "lua/pl/pretty.lua", + ["pl.class"] = "lua/pl/class.lua", + ["pl.List"] = "lua/pl/List.lua", + ["pl.data"] = "lua/pl/data.lua", + ["pl.Date"] = "lua/pl/Date.lua", + ["pl.init"] = "lua/pl/init.lua", + ["pl.luabalanced"] = "lua/pl/luabalanced.lua", + ["pl.comprehension"] = "lua/pl/comprehension.lua", + ["pl.path"] = "lua/pl/path.lua", + ["pl.array2d"] = "lua/pl/array2d.lua", + ["pl.func"] = "lua/pl/func.lua", + ["pl.lapp"] = "lua/pl/lapp.lua", + ["pl.file"] = "lua/pl/file.lua", + ['pl.template'] = "lua/pl/template.lua", + ["pl.Map"] = "lua/pl/Map.lua", + ["pl.MultiMap"] = "lua/pl/MultiMap.lua", + ["pl.OrderedMap"] = "lua/pl/OrderedMap.lua", + ["pl.Set"] = "lua/pl/Set.lua", + ["pl.xml"] = "lua/pl/xml.lua", + ["pl.url"] = "lua/pl/url.lua", + ["pl.import_into"] = "lua/pl/import_into.lua", + ["pl.types"] = "lua/pl/types.lua", + }, + copy_directories = {"docs", "tests"} +} + diff --git a/Data/Libraries/Penlight/rockspecs/penlight-1.6.0-1.rockspec b/Data/Libraries/Penlight/rockspecs/penlight-1.6.0-1.rockspec new file mode 100644 index 0000000..760cbf5 --- /dev/null +++ b/Data/Libraries/Penlight/rockspecs/penlight-1.6.0-1.rockspec @@ -0,0 +1,70 @@ +package = "penlight" +version = "1.6.0-1" + +source = { + url = "git://github.com/Tieske/Penlight.git", + branch = "1.6.0" +} + +description = { + summary = "Lua utility libraries loosely based on the Python standard libraries", + homepage = "http://tieske.github.io/Penlight", + license = "MIT/X11", + maintainer = "thijs@thijsschreijer.nl", + detailed = [[ +Penlight is a set of pure Lua libraries for making it easier to work with common tasks like +iterating over directories, reading configuration files and the like. Provides functional operations +on tables and sequences. +]] +} + +dependencies = { + "luafilesystem", +} + +build = { + type = "builtin", + modules = { + ["pl.strict"] = "lua/pl/strict.lua", + ["pl.dir"] = "lua/pl/dir.lua", + ["pl.operator"] = "lua/pl/operator.lua", + ["pl.input"] = "lua/pl/input.lua", + ["pl.config"] = "lua/pl/config.lua", + ["pl.compat"] = "lua/pl/config.lua", + ["pl.seq"] = "lua/pl/seq.lua", + ["pl.stringio"] = "lua/pl/stringio.lua", + ["pl.text"] = "lua/pl/text.lua", + ["pl.test"] = "lua/pl/test.lua", + ["pl.tablex"] = "lua/pl/tablex.lua", + ["pl.app"] = "lua/pl/app.lua", + ["pl.stringx"] = "lua/pl/stringx.lua", + ["pl.lexer"] = "lua/pl/lexer.lua", + ["pl.utils"] = "lua/pl/utils.lua", + ["pl.sip"] = "lua/pl/sip.lua", + ["pl.permute"] = "lua/pl/permute.lua", + ["pl.pretty"] = "lua/pl/pretty.lua", + ["pl.class"] = "lua/pl/class.lua", + ["pl.List"] = "lua/pl/List.lua", + ["pl.data"] = "lua/pl/data.lua", + ["pl.Date"] = "lua/pl/Date.lua", + ["pl.init"] = "lua/pl/init.lua", + ["pl.luabalanced"] = "lua/pl/luabalanced.lua", + ["pl.comprehension"] = "lua/pl/comprehension.lua", + ["pl.path"] = "lua/pl/path.lua", + ["pl.array2d"] = "lua/pl/array2d.lua", + ["pl.func"] = "lua/pl/func.lua", + ["pl.lapp"] = "lua/pl/lapp.lua", + ["pl.file"] = "lua/pl/file.lua", + ['pl.template'] = "lua/pl/template.lua", + ["pl.Map"] = "lua/pl/Map.lua", + ["pl.MultiMap"] = "lua/pl/MultiMap.lua", + ["pl.OrderedMap"] = "lua/pl/OrderedMap.lua", + ["pl.Set"] = "lua/pl/Set.lua", + ["pl.xml"] = "lua/pl/xml.lua", + ["pl.url"] = "lua/pl/url.lua", + ["pl.import_into"] = "lua/pl/import_into.lua", + ["pl.types"] = "lua/pl/types.lua", + }, + copy_directories = {"docs", "tests"} +} + diff --git a/Data/Libraries/Penlight/rockspecs/penlight-1.7.0-1.rockspec b/Data/Libraries/Penlight/rockspecs/penlight-1.7.0-1.rockspec new file mode 100644 index 0000000..9b19f39 --- /dev/null +++ b/Data/Libraries/Penlight/rockspecs/penlight-1.7.0-1.rockspec @@ -0,0 +1,70 @@ +package = "penlight" +version = "1.7.0-1" + +source = { + url = "git://github.com/Tieske/Penlight.git", + branch = "1.7.0" +} + +description = { + summary = "Lua utility libraries loosely based on the Python standard libraries", + homepage = "http://tieske.github.io/Penlight", + license = "MIT/X11", + maintainer = "thijs@thijsschreijer.nl", + detailed = [[ +Penlight is a set of pure Lua libraries for making it easier to work with common tasks like +iterating over directories, reading configuration files and the like. Provides functional operations +on tables and sequences. +]] +} + +dependencies = { + "luafilesystem", +} + +build = { + type = "builtin", + modules = { + ["pl.strict"] = "lua/pl/strict.lua", + ["pl.dir"] = "lua/pl/dir.lua", + ["pl.operator"] = "lua/pl/operator.lua", + ["pl.input"] = "lua/pl/input.lua", + ["pl.config"] = "lua/pl/config.lua", + ["pl.compat"] = "lua/pl/config.lua", + ["pl.seq"] = "lua/pl/seq.lua", + ["pl.stringio"] = "lua/pl/stringio.lua", + ["pl.text"] = "lua/pl/text.lua", + ["pl.test"] = "lua/pl/test.lua", + ["pl.tablex"] = "lua/pl/tablex.lua", + ["pl.app"] = "lua/pl/app.lua", + ["pl.stringx"] = "lua/pl/stringx.lua", + ["pl.lexer"] = "lua/pl/lexer.lua", + ["pl.utils"] = "lua/pl/utils.lua", + ["pl.sip"] = "lua/pl/sip.lua", + ["pl.permute"] = "lua/pl/permute.lua", + ["pl.pretty"] = "lua/pl/pretty.lua", + ["pl.class"] = "lua/pl/class.lua", + ["pl.List"] = "lua/pl/List.lua", + ["pl.data"] = "lua/pl/data.lua", + ["pl.Date"] = "lua/pl/Date.lua", + ["pl.init"] = "lua/pl/init.lua", + ["pl.luabalanced"] = "lua/pl/luabalanced.lua", + ["pl.comprehension"] = "lua/pl/comprehension.lua", + ["pl.path"] = "lua/pl/path.lua", + ["pl.array2d"] = "lua/pl/array2d.lua", + ["pl.func"] = "lua/pl/func.lua", + ["pl.lapp"] = "lua/pl/lapp.lua", + ["pl.file"] = "lua/pl/file.lua", + ['pl.template'] = "lua/pl/template.lua", + ["pl.Map"] = "lua/pl/Map.lua", + ["pl.MultiMap"] = "lua/pl/MultiMap.lua", + ["pl.OrderedMap"] = "lua/pl/OrderedMap.lua", + ["pl.Set"] = "lua/pl/Set.lua", + ["pl.xml"] = "lua/pl/xml.lua", + ["pl.url"] = "lua/pl/url.lua", + ["pl.import_into"] = "lua/pl/import_into.lua", + ["pl.types"] = "lua/pl/types.lua", + }, + copy_directories = {"docs", "tests"} +} + diff --git a/Data/Libraries/Penlight/rockspecs/penlight-1.8.0-1.rockspec b/Data/Libraries/Penlight/rockspecs/penlight-1.8.0-1.rockspec new file mode 100644 index 0000000..7ee49d3 --- /dev/null +++ b/Data/Libraries/Penlight/rockspecs/penlight-1.8.0-1.rockspec @@ -0,0 +1,70 @@ +package = "penlight" +version = "1.8.0-1" + +source = { + url = "git://github.com/Tieske/Penlight.git", + branch = "1.8.0" +} + +description = { + summary = "Lua utility libraries loosely based on the Python standard libraries", + homepage = "http://tieske.github.io/Penlight", + license = "MIT/X11", + maintainer = "thijs@thijsschreijer.nl", + detailed = [[ +Penlight is a set of pure Lua libraries for making it easier to work with common tasks like +iterating over directories, reading configuration files and the like. Provides functional operations +on tables and sequences. +]] +} + +dependencies = { + "luafilesystem", +} + +build = { + type = "builtin", + modules = { + ["pl.strict"] = "lua/pl/strict.lua", + ["pl.dir"] = "lua/pl/dir.lua", + ["pl.operator"] = "lua/pl/operator.lua", + ["pl.input"] = "lua/pl/input.lua", + ["pl.config"] = "lua/pl/config.lua", + ["pl.compat"] = "lua/pl/config.lua", + ["pl.seq"] = "lua/pl/seq.lua", + ["pl.stringio"] = "lua/pl/stringio.lua", + ["pl.text"] = "lua/pl/text.lua", + ["pl.test"] = "lua/pl/test.lua", + ["pl.tablex"] = "lua/pl/tablex.lua", + ["pl.app"] = "lua/pl/app.lua", + ["pl.stringx"] = "lua/pl/stringx.lua", + ["pl.lexer"] = "lua/pl/lexer.lua", + ["pl.utils"] = "lua/pl/utils.lua", + ["pl.sip"] = "lua/pl/sip.lua", + ["pl.permute"] = "lua/pl/permute.lua", + ["pl.pretty"] = "lua/pl/pretty.lua", + ["pl.class"] = "lua/pl/class.lua", + ["pl.List"] = "lua/pl/List.lua", + ["pl.data"] = "lua/pl/data.lua", + ["pl.Date"] = "lua/pl/Date.lua", + ["pl.init"] = "lua/pl/init.lua", + ["pl.luabalanced"] = "lua/pl/luabalanced.lua", + ["pl.comprehension"] = "lua/pl/comprehension.lua", + ["pl.path"] = "lua/pl/path.lua", + ["pl.array2d"] = "lua/pl/array2d.lua", + ["pl.func"] = "lua/pl/func.lua", + ["pl.lapp"] = "lua/pl/lapp.lua", + ["pl.file"] = "lua/pl/file.lua", + ['pl.template'] = "lua/pl/template.lua", + ["pl.Map"] = "lua/pl/Map.lua", + ["pl.MultiMap"] = "lua/pl/MultiMap.lua", + ["pl.OrderedMap"] = "lua/pl/OrderedMap.lua", + ["pl.Set"] = "lua/pl/Set.lua", + ["pl.xml"] = "lua/pl/xml.lua", + ["pl.url"] = "lua/pl/url.lua", + ["pl.import_into"] = "lua/pl/import_into.lua", + ["pl.types"] = "lua/pl/types.lua", + }, + copy_directories = {"docs", "tests"} +} + diff --git a/Data/Libraries/Penlight/rockspecs/penlight-1.8.1-1.rockspec b/Data/Libraries/Penlight/rockspecs/penlight-1.8.1-1.rockspec new file mode 100644 index 0000000..45919d1 --- /dev/null +++ b/Data/Libraries/Penlight/rockspecs/penlight-1.8.1-1.rockspec @@ -0,0 +1,70 @@ +package = "penlight" +version = "1.8.1-1" + +source = { + url = "git://github.com/lunarmodules/Penlight.git", + tag = "1.8.1" +} + +description = { + summary = "Lua utility libraries loosely based on the Python standard libraries", + homepage = "https://lunarmodules.github.io/Penlight", + license = "MIT/X11", + maintainer = "thijs@thijsschreijer.nl", + detailed = [[ +Penlight is a set of pure Lua libraries for making it easier to work with common tasks like +iterating over directories, reading configuration files and the like. Provides functional operations +on tables and sequences. +]] +} + +dependencies = { + "luafilesystem", +} + +build = { + type = "builtin", + modules = { + ["pl.strict"] = "lua/pl/strict.lua", + ["pl.dir"] = "lua/pl/dir.lua", + ["pl.operator"] = "lua/pl/operator.lua", + ["pl.input"] = "lua/pl/input.lua", + ["pl.config"] = "lua/pl/config.lua", + ["pl.compat"] = "lua/pl/config.lua", + ["pl.seq"] = "lua/pl/seq.lua", + ["pl.stringio"] = "lua/pl/stringio.lua", + ["pl.text"] = "lua/pl/text.lua", + ["pl.test"] = "lua/pl/test.lua", + ["pl.tablex"] = "lua/pl/tablex.lua", + ["pl.app"] = "lua/pl/app.lua", + ["pl.stringx"] = "lua/pl/stringx.lua", + ["pl.lexer"] = "lua/pl/lexer.lua", + ["pl.utils"] = "lua/pl/utils.lua", + ["pl.sip"] = "lua/pl/sip.lua", + ["pl.permute"] = "lua/pl/permute.lua", + ["pl.pretty"] = "lua/pl/pretty.lua", + ["pl.class"] = "lua/pl/class.lua", + ["pl.List"] = "lua/pl/List.lua", + ["pl.data"] = "lua/pl/data.lua", + ["pl.Date"] = "lua/pl/Date.lua", + ["pl.init"] = "lua/pl/init.lua", + ["pl.luabalanced"] = "lua/pl/luabalanced.lua", + ["pl.comprehension"] = "lua/pl/comprehension.lua", + ["pl.path"] = "lua/pl/path.lua", + ["pl.array2d"] = "lua/pl/array2d.lua", + ["pl.func"] = "lua/pl/func.lua", + ["pl.lapp"] = "lua/pl/lapp.lua", + ["pl.file"] = "lua/pl/file.lua", + ['pl.template'] = "lua/pl/template.lua", + ["pl.Map"] = "lua/pl/Map.lua", + ["pl.MultiMap"] = "lua/pl/MultiMap.lua", + ["pl.OrderedMap"] = "lua/pl/OrderedMap.lua", + ["pl.Set"] = "lua/pl/Set.lua", + ["pl.xml"] = "lua/pl/xml.lua", + ["pl.url"] = "lua/pl/url.lua", + ["pl.import_into"] = "lua/pl/import_into.lua", + ["pl.types"] = "lua/pl/types.lua", + }, + copy_directories = {"docs", "tests"} +} + diff --git a/Data/Libraries/Penlight/rockspecs/penlight-1.9.1-1.rockspec b/Data/Libraries/Penlight/rockspecs/penlight-1.9.1-1.rockspec new file mode 100644 index 0000000..e546a16 --- /dev/null +++ b/Data/Libraries/Penlight/rockspecs/penlight-1.9.1-1.rockspec @@ -0,0 +1,70 @@ +package = "penlight" +version = "1.9.1-1" + +source = { + url = "git://github.com/lunarmodules/Penlight.git", + tag = "1.9.1" +} + +description = { + summary = "Lua utility libraries loosely based on the Python standard libraries", + homepage = "https://lunarmodules.github.io/Penlight", + license = "MIT/X11", + maintainer = "thijs@thijsschreijer.nl", + detailed = [[ +Penlight is a set of pure Lua libraries for making it easier to work with common tasks like +iterating over directories, reading configuration files and the like. Provides functional operations +on tables and sequences. +]] +} + +dependencies = { + "luafilesystem", +} + +build = { + type = "builtin", + modules = { + ["pl.strict"] = "lua/pl/strict.lua", + ["pl.dir"] = "lua/pl/dir.lua", + ["pl.operator"] = "lua/pl/operator.lua", + ["pl.input"] = "lua/pl/input.lua", + ["pl.config"] = "lua/pl/config.lua", + ["pl.compat"] = "lua/pl/config.lua", + ["pl.seq"] = "lua/pl/seq.lua", + ["pl.stringio"] = "lua/pl/stringio.lua", + ["pl.text"] = "lua/pl/text.lua", + ["pl.test"] = "lua/pl/test.lua", + ["pl.tablex"] = "lua/pl/tablex.lua", + ["pl.app"] = "lua/pl/app.lua", + ["pl.stringx"] = "lua/pl/stringx.lua", + ["pl.lexer"] = "lua/pl/lexer.lua", + ["pl.utils"] = "lua/pl/utils.lua", + ["pl.sip"] = "lua/pl/sip.lua", + ["pl.permute"] = "lua/pl/permute.lua", + ["pl.pretty"] = "lua/pl/pretty.lua", + ["pl.class"] = "lua/pl/class.lua", + ["pl.List"] = "lua/pl/List.lua", + ["pl.data"] = "lua/pl/data.lua", + ["pl.Date"] = "lua/pl/Date.lua", + ["pl.init"] = "lua/pl/init.lua", + ["pl.luabalanced"] = "lua/pl/luabalanced.lua", + ["pl.comprehension"] = "lua/pl/comprehension.lua", + ["pl.path"] = "lua/pl/path.lua", + ["pl.array2d"] = "lua/pl/array2d.lua", + ["pl.func"] = "lua/pl/func.lua", + ["pl.lapp"] = "lua/pl/lapp.lua", + ["pl.file"] = "lua/pl/file.lua", + ['pl.template'] = "lua/pl/template.lua", + ["pl.Map"] = "lua/pl/Map.lua", + ["pl.MultiMap"] = "lua/pl/MultiMap.lua", + ["pl.OrderedMap"] = "lua/pl/OrderedMap.lua", + ["pl.Set"] = "lua/pl/Set.lua", + ["pl.xml"] = "lua/pl/xml.lua", + ["pl.url"] = "lua/pl/url.lua", + ["pl.import_into"] = "lua/pl/import_into.lua", + ["pl.types"] = "lua/pl/types.lua", + }, + copy_directories = {"docs", "tests"} +} + diff --git a/Data/Libraries/Penlight/rockspecs/penlight-1.9.2-1.rockspec b/Data/Libraries/Penlight/rockspecs/penlight-1.9.2-1.rockspec new file mode 100644 index 0000000..5145b38 --- /dev/null +++ b/Data/Libraries/Penlight/rockspecs/penlight-1.9.2-1.rockspec @@ -0,0 +1,70 @@ +package = "penlight" +version = "1.9.2-1" + +source = { + url = "git://github.com/lunarmodules/Penlight.git", + tag = "1.9.2" +} + +description = { + summary = "Lua utility libraries loosely based on the Python standard libraries", + homepage = "https://lunarmodules.github.io/Penlight", + license = "MIT/X11", + maintainer = "thijs@thijsschreijer.nl", + detailed = [[ +Penlight is a set of pure Lua libraries for making it easier to work with common tasks like +iterating over directories, reading configuration files and the like. Provides functional operations +on tables and sequences. +]] +} + +dependencies = { + "luafilesystem", +} + +build = { + type = "builtin", + modules = { + ["pl.strict"] = "lua/pl/strict.lua", + ["pl.dir"] = "lua/pl/dir.lua", + ["pl.operator"] = "lua/pl/operator.lua", + ["pl.input"] = "lua/pl/input.lua", + ["pl.config"] = "lua/pl/config.lua", + ["pl.compat"] = "lua/pl/config.lua", + ["pl.seq"] = "lua/pl/seq.lua", + ["pl.stringio"] = "lua/pl/stringio.lua", + ["pl.text"] = "lua/pl/text.lua", + ["pl.test"] = "lua/pl/test.lua", + ["pl.tablex"] = "lua/pl/tablex.lua", + ["pl.app"] = "lua/pl/app.lua", + ["pl.stringx"] = "lua/pl/stringx.lua", + ["pl.lexer"] = "lua/pl/lexer.lua", + ["pl.utils"] = "lua/pl/utils.lua", + ["pl.sip"] = "lua/pl/sip.lua", + ["pl.permute"] = "lua/pl/permute.lua", + ["pl.pretty"] = "lua/pl/pretty.lua", + ["pl.class"] = "lua/pl/class.lua", + ["pl.List"] = "lua/pl/List.lua", + ["pl.data"] = "lua/pl/data.lua", + ["pl.Date"] = "lua/pl/Date.lua", + ["pl.init"] = "lua/pl/init.lua", + ["pl.luabalanced"] = "lua/pl/luabalanced.lua", + ["pl.comprehension"] = "lua/pl/comprehension.lua", + ["pl.path"] = "lua/pl/path.lua", + ["pl.array2d"] = "lua/pl/array2d.lua", + ["pl.func"] = "lua/pl/func.lua", + ["pl.lapp"] = "lua/pl/lapp.lua", + ["pl.file"] = "lua/pl/file.lua", + ['pl.template'] = "lua/pl/template.lua", + ["pl.Map"] = "lua/pl/Map.lua", + ["pl.MultiMap"] = "lua/pl/MultiMap.lua", + ["pl.OrderedMap"] = "lua/pl/OrderedMap.lua", + ["pl.Set"] = "lua/pl/Set.lua", + ["pl.xml"] = "lua/pl/xml.lua", + ["pl.url"] = "lua/pl/url.lua", + ["pl.import_into"] = "lua/pl/import_into.lua", + ["pl.types"] = "lua/pl/types.lua", + }, + copy_directories = {"docs", "tests"} +} + diff --git a/Data/Libraries/Penlight/run.lua b/Data/Libraries/Penlight/run.lua new file mode 100644 index 0000000..3d637e7 --- /dev/null +++ b/Data/Libraries/Penlight/run.lua @@ -0,0 +1,66 @@ +-- Running tests and/or examples. +local lfs = require "lfs" + +local directories = {} +local luacov = false + +for _, argument in ipairs(arg) do + if argument == "--help" then + print("Usage: lua run.lua [--luacov] [<dir>]...") + os.exit(0) + elseif argument == "--luacov" then + luacov = true + else + table.insert(directories, argument) + end +end + +if #directories == 0 then + directories = {"tests", "examples"} +end + +local lua = "lua" +local i = -1 +while arg[i] do + lua = arg[i] + i = i - 1 +end + +if luacov then + lua = lua .. " -lluacov" +end + +local dir_sep = package.config:sub(1, 1) +local quote = dir_sep == "/" and "'" or '"' +local pl_src = "lua/?.lua;lua/?/init.lua" +lua = lua .. " -e " .. quote .. "package.path=[[" .. pl_src .. ";]]..package.path" .. quote + +local function run_directory(dir) + local files = {} + for path in lfs.dir(dir) do + local full_path = dir .. dir_sep .. path + if path:find("%.lua$") and lfs.attributes(full_path, "mode") == "file" then + table.insert(files, full_path) + end + end + table.sort(files) + + for _, file in ipairs(files) do + local cmd = lua .. " " .. file + print("Running " .. file) + local code1, _, code2 = os.execute(cmd) + local code = type(code1) == "number" and code1 or code2 + + if code ~= 0 then + print(("Running %s failed with code %d"):format(file, code)) + os.exit(1) + end + end +end + +for _, dir in ipairs(directories) do + print("Running files in " .. dir) + run_directory(dir) +end + +print("Run completed successfully") diff --git a/Data/Libraries/Penlight/spec/app_spec.lua b/Data/Libraries/Penlight/spec/app_spec.lua new file mode 100644 index 0000000..3b1f290 --- /dev/null +++ b/Data/Libraries/Penlight/spec/app_spec.lua @@ -0,0 +1,27 @@ +local app = require("pl.app") + +describe("pl.app.lua", function () + + local invocation = app.lua() + + it("should pick up the arguments used to run this test", function () + assert.is.truthy(invocation:match("lua.+package.+busted")) + end) + + it("should be reusable to invoke Lua", function () + assert.is.truthy(os.execute(app.lua()..' -e "n=1;os.exit(n-1)"')) + end) + +end) + +describe("pl.app.platform", function () + + -- TODO: Find a reliable alternate way to determine platform to check that + -- this is returning the right answer, not just any old answer. + it("should at least return a valid platform", function () + local platforms = { Linux = true, OSX = true, Windows = true } + local detected = app.platform() + assert.is.truthy(platforms[detected]) + end) + +end) diff --git a/Data/Libraries/Penlight/spec/array2d_spec.lua b/Data/Libraries/Penlight/spec/array2d_spec.lua new file mode 100644 index 0000000..3fd0085 --- /dev/null +++ b/Data/Libraries/Penlight/spec/array2d_spec.lua @@ -0,0 +1,557 @@ +local array2d = require("pl.array2d") + +describe("pl.array2d", function() + + describe("new()", function() + it("creates an empty 2d array", function() + assert.same({{},{},{}}, array2d.new(3,3,nil)) + end) + + it("creates a value-filled 2d array", function() + assert.same({{99,99,99}, + {99,99,99}, + {99,99,99}}, array2d.new(3,3,99)) + end) + + it("creates a function-filled 2d array", function() + assert.same({{2,3,4}, + {3,4,5}, + {4,5,6}}, array2d.new(3,3,function(i,j) return i+j end)) + end) + end) + + describe("size()", function() + it("returns array size", function() + local a = array2d.new(3,5,99) + assert.same({3,5}, {array2d.size(a)}) + end) + + it("returns 0 columns for nil arrays", function() + local a = array2d.new(3,5,nil) + assert.same({3,0}, {array2d.size(a)}) + end) + end) + + describe("column()", function() + it("returns a column copy", function() + local a = {{1,2}, + {3,4}, + {5,6}} + assert.same({1,3,5}, array2d.column(a,1)) + assert.same({2,4,6}, array2d.column(a,2)) + end) + end) + + describe("row()", function() + it("returns a row copy", function() + local a = {{1,2}, + {3,4}, + {5,6}} + assert.same({1,2}, array2d.row(a,1)) + -- next test: need to remove the metatable to prevent comparison by + -- metamethods in Lua 5.3 and 5.4 + assert.not_equal(a[1], setmetatable(array2d.row(a,1),nil)) + assert.same({3,4}, array2d.row(a,2)) + assert.same({5,6}, array2d.row(a,3)) + end) + end) + + describe("map()", function() + it("maps a function on an array", function() + local a1 = array2d.new(2,3,function(i,j) return i+j end) + local a2 = array2d.map(function(a,b) return a .. b end, a1, "x") + assert.same({{"2x","3x","4x"}, + {"3x","4x","5x"}}, a2) + end) + end) + + describe("reduce_rows()", function() + it("reduces rows", function() + local a = {{ 1, 2, 3, 4}, + { 10, 20, 30, 40}, + { 100, 200, 300, 400}, + {1000,2000,3000,4000}} + assert.same({10,100,1000,10000},array2d.reduce_rows('+',a)) + end) + end) + + describe("reduce_cols()", function() + it("reduces columns", function() + local a = {{ 1, 2, 3, 4}, + { 10, 20, 30, 40}, + { 100, 200, 300, 400}, + {1000,2000,3000,4000}} + assert.same({1111,2222,3333,4444},array2d.reduce_cols('+',a)) + end) + end) + + describe("reduce2()", function() + it("recuces array to scalar", function() + local a = {{1,10}, + {2,10}, + {3,10}} + assert.same(60, array2d.reduce2('+','*',a)) + end) + end) + + describe("map2()", function() + it("maps over 2 arrays", function() + local b = {{10,20}, + {30,40}} + local a = {{1,2}, + {3,4}} + -- 2 2d arrays + assert.same({{11,22},{33,44}}, array2d.map2('+',2,2,a,b)) + -- 1d, 2d + assert.same({{11,102},{13,104}}, array2d.map2('+',1,2,{10,100},a)) + -- 2d, 1d + assert.same({{1,-2},{3,-4}},array2d.map2('*',2,1,a,{1,-1})) + end) + end) + + describe("product()", function() + it("creates a product array", function() + local a = array2d.product('..',{1,2,3},{'a','b','c'}) + assert.same({{'1a','2a','3a'},{'1b','2b','3b'},{'1c','2c','3c'}}, a) + + local a = array2d.product('{}',{1,2},{'a','b','c'}) + assert.same({{{1,'a'},{2,'a'}},{{1,'b'},{2,'b'}},{{1,'c'},{2,'c'}}}, a) + end) + end) + + describe("flatten()", function() + it("flattens a 2darray", function() + local a = {{1,2}, + {3,4}, + {5,6}} + assert.same( {1,2,3,4,5,6}, array2d.flatten(a)) + end) + + it("keeps a nil-array 'square'", function() + local a = {{ 1,2}, + {nil,4}, + {nil,6}} + assert.same( {1,2,nil,4,nil,6}, array2d.flatten(a)) + end) + end) + + describe("reshape()", function() + it("reshapes array in new nr of rows", function() + local a = {{ 1, 2, 3}, + { 4, 5, 6}, + { 7, 8, 9}, + {10,11,12}} + local b = array2d.reshape(a, 2, false) + assert.same({{ 1, 2, 3, 4, 5, 6}, + { 7, 8, 9,10,11,12}}, b) + local c = array2d.reshape(b, 4, false) + assert.same(a, c) + end) + it("reshapes array in new nr of rows, column order", function() + local a = {{ 1, 2, 3}, + { 4, 5, 6}, + { 7, 8, 9}} + local b = array2d.reshape(a, 3, true) + assert.same({{ 1, 4, 7}, + { 2, 5, 8}, + { 3, 6, 9}}, b) + end) + end) + + describe("transpose()", function() + it("transposes a 2d array", function() + local a = {{ 1, 2, 3}, + { 4, 5, 6}, + { 7, 8, 9}} + local b = array2d.transpose(a) + assert.same({{ 1, 4, 7}, + { 2, 5, 8}, + { 3, 6, 9}}, b) + + local a = {{ 1, 2, 3, 4, 5}, + { 6, 7, 8, 9, 10}} + local b = array2d.transpose(a) + assert.same({{ 1, 6}, + { 2, 7}, + { 3, 8}, + { 4, 9}, + { 5,10}}, b) + end) + end) + + describe("swap_rows()", function() + it("swaps 2 rows, in-place", function() + local a = {{1,2}, + {3,4}, + {5,6}} + local b = array2d.swap_rows(a, 1, 3) + assert.same({{5,6}, + {3,4}, + {1,2}}, b) + assert.equal(a, b) + end) + end) + + describe("swap_cols()", function() + it("swaps 2 columns, in-place", function() + local a = {{1,2,3}, + {4,5,6}, + {7,8,9}} + local b = array2d.swap_cols(a, 1, 3) + assert.same({{3,2,1}, + {6,5,4}, + {9,8,7}}, b) + assert.equal(a, b) + end) + end) + + describe("extract_rows()", function() + it("extracts rows", function() + local a = {{ 1, 2, 3}, + { 4, 5, 6}, + { 7, 8, 9}, + {10,11,12}} + local b = array2d.extract_rows(a, {1, 3}) + assert.same({{1,2,3}, + {7,8,9}}, b) + end) + end) + + describe("extract_cols()", function() + it("extracts colums", function() + local a = {{ 1, 2, 3}, + { 4, 5, 6}, + { 7, 8, 9}, + {10,11,12}} + local b = array2d.extract_cols(a, {1, 2}) + assert.same({{ 1, 2}, + { 4, 5}, + { 7, 8}, + {10,11}}, b) + end) + end) + + describe("remove_row()", function() + it("removes a row", function() + local a = {{ 1, 2, 3}, + { 4, 5, 6}, + { 7, 8, 9}, + {10,11,12}} + array2d.remove_row(a, 2) + assert.same({{ 1, 2, 3}, + { 7, 8, 9}, + {10,11,12}}, a) + end) + end) + + describe("remove_col()", function() + it("removes a colum", function() + local a = {{ 1, 2, 3}, + { 4, 5, 6}, + { 7, 8, 9}, + {10,11,12}} + array2d.remove_col(a, 2) + assert.same({{ 1, 3}, + { 4, 6}, + { 7, 9}, + {10,12}}, a) + end) + end) + + describe("parse_range()", function() + it("parses A1:B2 format", function() + assert.same({4,11,7,12},{array2d.parse_range("K4:L7")}) + assert.same({4,28,7,54},{array2d.parse_range("AB4:BB7")}) + -- test Col R since it might be mixed up with RxCx format + assert.same({4,18,7,18},{array2d.parse_range("R4:R7")}) + end) + + it("parses A1 format", function() + assert.same({4,11},{array2d.parse_range("K4")}) + -- test Col R since it might be mixed up with RxCx format + assert.same({4,18},{array2d.parse_range("R4")}) + end) + + it("parses R1C1:R2C2 format", function() + assert.same({4,11,7,12},{array2d.parse_range("R4C11:R7C12")}) + end) + + it("parses R1C1 format", function() + assert.same({4,11},{array2d.parse_range("R4C11")}) + end) + end) + + describe("range()", function() + it("returns a range", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local b = array2d.range(a, "B3:C4") + assert.same({{ 8, 9}, + {11,12}}, b) + end) + end) + + describe("default_range()", function() + it("returns the default range", function() + local a = array2d.new(4,6,1) + assert.same({1,1,4,6}, {array2d.default_range(a, nil, nil, nil, nil)}) + end) + + it("accepts negative indices", function() + local a = array2d.new(4,6,1) + assert.same({2,2,3,5}, {array2d.default_range(a, -3, -5, -2, -2)}) + end) + + it("corrects out of bounds indices", function() + local a = array2d.new(4,6,1) + assert.same({1,1,4,6}, {array2d.default_range(a, -100, -100, 100, 100)}) + end) + end) + + describe("slice()", function() + it("returns a slice", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local b = array2d.slice(a,3,2,4,3) + assert.same({{ 8, 9}, + {11,12}}, b) + end) + + it("returns a single row if rows are equal", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local b = array2d.slice(a,4,1,4,3) + assert.same({10,11,12}, b) + end) + + it("returns a single column if columns are equal", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local b = array2d.slice(a,1,3,4,3) + assert.same({3,6,9,12}, b) + end) + + it("returns a single value if rows and columns are equal", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local b = array2d.slice(a,2,2,2,2) + assert.same(5, b) + end) + end) + + describe("set()", function() + it("sets a range to a value", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + array2d.set(a,0,2,2,3,3) + assert.same({{1 ,2 ,3}, + {4 ,0 ,0}, + {7 ,0 ,0}, + {10,11,12}}, a) + end) + + it("sets a range to a function value", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local x = 10 + local args = {} + local f = function(r,c) + args[#args+1] = {r,c} + x = x + 1 + return x + end + array2d.set(a,f,3,1,4,3) + assert.same({{1 ,2 ,3}, + {4 ,5 ,6}, + {11,12,13}, + {14,15,16}}, a) + -- validate args used to call the function + assert.same({{3,1},{3,2},{3,3},{4,1},{4,2},{4,3}}, args) + end) + end) + + describe("write()", function() + it("writes array to a file", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local f = setmetatable({}, { + __index = { + write = function(self,str) + self[#self+1] = str + end + } + }) + array2d.write(a,f,"(%s)") + f = table.concat(f) + assert.equal([[(1)(2)(3) +(4)(5)(6) +(7)(8)(9) +(10)(11)(12) +]],f) + end) + + it("writes partial array to a file", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local f = setmetatable({}, { + __index = { + write = function(self,str) + self[#self+1] = str + end + } + }) + array2d.write(a,f,"(%s)", 1,1,2,2) + f = table.concat(f) + assert.equal([[(1)(2) +(4)(5) +]],f) + end) + end) + + describe("forall()", function() + it("runs all value and row functions", function() + local r = {} + local t = 0 + local fval = function(row, j) t = t + row[j] end + local frow = function(i) r[#r+1] = t; t = 0 end + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + array2d.forall(a, fval, frow) + assert.same({6, 15, 24, 33}, r) + r = {} + array2d.forall(a, fval, frow, 2,2,4,3) + assert.same({11, 17, 23}, r) + end) + + end) + + describe("move()", function() + it("moves block to destination aray", function() + local a = array2d.new(4,4,0) + local b = array2d.new(3,3,1) + array2d.move(a,2,2,b) + assert.same({{0,0,0,0}, + {0,1,1,1}, + {0,1,1,1}, + {0,1,1,1}}, a) + end) + end) + + describe("iter()", function() + it("iterates all values", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local r = {} + for v, i, j in array2d.iter(a) do + r[#r+1] = v + assert.is_nil(i) + assert.is_nil(j) + end + assert.same({1,2,3,4,5,6,7,8,9,10,11,12}, r) + end) + + it("iterates all values and indices", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local r = {} + local ri = {} + local rj = {} + for i, j, v in array2d.iter(a,true) do + r[#r+1] = v + ri[#ri+1] = i + rj[#rj+1] = j + end + assert.same({1,2,3,4,5,6,7,8,9,10,11,12}, r) + assert.same({1,1,1,2,2,2,3,3,3,4,4,4}, ri) + assert.same({1,2,3,1,2,3,1,2,3,1,2,3}, rj) + end) + + it("iterates all values of a 2d array part", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local r = {} + for v, i, j in array2d.iter(a,false,2,2,4,3) do + r[#r+1] = v + assert.is_nil(i) + assert.is_nil(j) + end + assert.same({5,6,8,9,11,12}, r) + end) + + it("iterates all values and indices of a 2d array part", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local r = {} + local ri = {} + local rj = {} + for i, j, v in array2d.iter(a,true,2,2,4,3) do + r[#r+1] = v + ri[#ri+1] = i + rj[#rj+1] = j + end + assert.same({5,6,8,9,11,12}, r) + assert.same({2,2,3,3,4,4}, ri) + assert.same({2,3,2,3,2,3}, rj) + end) + end) + + describe("columns()", function() + it("iterates all columns", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local r = {} + for col, idx in array2d.columns(a) do + r[#r+1] = col + col.idx = idx + end + assert.same({{1,4,7,10, idx=1},{2,5,8,11, idx=2},{3,6,9,12, idx=3}}, r) + end) + end) + + describe("rows()", function() + it("iterates all columns", function() + local a = {{1 ,2 ,3}, + {4 ,5 ,6}, + {7 ,8 ,9}, + {10,11,12}} + local r = {} + for row, idx in array2d.rows(a) do + r[#r+1] = row + row.idx = idx + end + assert.same({{1,2,3, idx=1},{4,5,6, idx=2}, + {7,8,9, idx=3},{10,11,12, idx=4}}, r) + end) + end) + +end) diff --git a/Data/Libraries/Penlight/spec/date_spec.lua b/Data/Libraries/Penlight/spec/date_spec.lua new file mode 100644 index 0000000..1032de2 --- /dev/null +++ b/Data/Libraries/Penlight/spec/date_spec.lua @@ -0,0 +1,48 @@ +local Date = require("pl.Date") + +describe("pl.Date", function () + + describe("function", function () + + describe("Format()", function () + + it("should output parsable inputs", function () + local function assert_date_format(expected, format) + local df = Date.Format(format) + local d = df:parse(expected) + assert.is.equal(expected, df:tostring(d)) + end + assert_date_format('02/04/10', 'dd/mm/yy') + assert_date_format('04/02/2010', 'mm/dd/yyyy') + assert_date_format('2011-02-20', 'yyyy-mm-dd') + assert_date_format('20070320', 'yyyymmdd') + assert_date_format('23:10', 'HH:MM') + end) + + it("should parse 'slack' fields", function () + local df = Date.Format("m/d/yy") + -- TODO: Re-enable when issue #359 fixed + -- assert.is.equal('01/05/99', df:tostring(df:parse('1/5/99'))) + assert.is.equal('01/05/01', df:tostring(df:parse('1/5/01'))) + assert.is.equal('01/05/32', df:tostring(df:parse('1/5/32'))) + end) + + end) + + end) + + describe("meta method", function () + + describe("__tostring()", function () + + it("should be suitable for serialization", function () + local df = Date.Format() + local du = df:parse("2008-07-05") + assert.is.equal(du, du:toUTC()) + end) + + end) + + end) + +end) diff --git a/Data/Libraries/Penlight/spec/multimap_spec.lua b/Data/Libraries/Penlight/spec/multimap_spec.lua new file mode 100644 index 0000000..a83d150 --- /dev/null +++ b/Data/Libraries/Penlight/spec/multimap_spec.lua @@ -0,0 +1,14 @@ +local MultiMap = require("pl.MultiMap") + +describe("pl.MultiMap", function () + + it("should hold multiple values per key", function () + local map = MultiMap() + map:set('foo', 1) + map:set('bar', 3) + map:set('foo', 2) + local expected = { foo = { 1, 2 }, bar = { 3 } } + assert.is.same(expected, map) + end) + +end) diff --git a/Data/Libraries/Penlight/spec/permute_spec.lua b/Data/Libraries/Penlight/spec/permute_spec.lua new file mode 100644 index 0000000..49143c0 --- /dev/null +++ b/Data/Libraries/Penlight/spec/permute_spec.lua @@ -0,0 +1,213 @@ +local permute = require("pl.permute") +local tcopy = require("pl.tablex").copy +local utils = require("pl.utils") + +describe("pl.permute", function() + + describe("order_iter", function() + + it("returns all order combinations", function() + local result = {} + for list in permute.order_iter({"one", "two", "three"}) do + result[#result+1] = tcopy(list) + end + assert.same({ + [1] = { + [1] = 'two', + [2] = 'three', + [3] = 'one' }, + [2] = { + [1] = 'three', + [2] = 'two', + [3] = 'one' }, + [3] = { + [1] = 'three', + [2] = 'one', + [3] = 'two' }, + [4] = { + [1] = 'one', + [2] = 'three', + [3] = 'two' }, + [5] = { + [1] = 'two', + [2] = 'one', + [3] = 'three' }, + [6] = { + [1] = 'one', + [2] = 'two', + [3] = 'three' } }, result) + end) + + + it("returns nil on empty list", function() + local result = {} + for list in permute.order_iter({}) do + result[#result+1] = tcopy(list) + end + assert.equal(0, #result) + end) + + end) + + + + describe("order_table", function() + + it("returns all order combinations", function() + local result = permute.order_table({"one", "two", "three"}) + assert.same({ + [1] = { + [1] = 'two', + [2] = 'three', + [3] = 'one' }, + [2] = { + [1] = 'three', + [2] = 'two', + [3] = 'one' }, + [3] = { + [1] = 'three', + [2] = 'one', + [3] = 'two' }, + [4] = { + [1] = 'one', + [2] = 'three', + [3] = 'two' }, + [5] = { + [1] = 'two', + [2] = 'one', + [3] = 'three' }, + [6] = { + [1] = 'one', + [2] = 'two', + [3] = 'three' } }, result) + end) + + + it("returns empty table on empty input list", function() + local result = permute.order_table({}) + assert.same({}, result) + end) + + end) + + + + describe("list_iter", function() + + it("returns all combinations from sub-lists", function() + local result = {} + local strs = {"one", "two", "three"} + local ints = { 1,2,3 } + local bools = { true, false } + for count, str, int, bool in permute.list_iter(strs, ints, bools) do + result[#result+1] = {count, str, int, bool} + end + assert.same({ + [1] = {1, 'one', 1, true }, + [2] = {2, 'two', 1, true }, + [3] = {3, 'three', 1, true }, + [4] = {4, 'one', 2, true }, + [5] = {5, 'two', 2, true }, + [6] = {6, 'three', 2, true }, + [7] = {7, 'one', 3, true }, + [8] = {8, 'two', 3, true }, + [9] = {9, 'three', 3, true }, + [10] = {10, 'one', 1, false }, + [11] = {11, 'two', 1, false }, + [12] = {12, 'three', 1, false }, + [13] = {13, 'one', 2, false }, + [14] = {14, 'two', 2, false }, + [15] = {15, 'three', 2, false }, + [16] = {16, 'one', 3, false }, + [17] = {17, 'two', 3, false }, + [18] = {18, 'three', 3, false }, + }, result) + end) + + + it("is nil-safe, given 'n' is set", function() + local result = {} + local bools = utils.pack(nil, true, false) + local strs = utils.pack("one", "two", nil) + for count, bool, str in permute.list_iter(bools, strs) do + result[#result+1] = {count, bool, str} + end + assert.same({ + [1] = {1, nil, 'one' }, + [2] = {2, true, 'one' }, + [3] = {3, false, 'one' }, + [4] = {4, nil, 'two' }, + [5] = {5, true, 'two' }, + [6] = {6, false, 'two' }, + [7] = {7, nil, nil }, + [8] = {8, true, nil }, + [9] = {9, false, nil }, + }, result) + end) + + + it("returns nil on empty list", function() + local count = 0 + for list in permute.list_iter({}) do + count = count + 1 + end + assert.equal(0, count) + end) + + end) + + + + describe("list_table", function() + + it("returns all combinations from sub-lists", function() + local strs = {"one", "two", "three"} + local ints = { 1,2,3 } + local bools = { true, false } + assert.same({ + [1] = {'one', 1, true, n = 3 }, + [2] = {'two', 1, true, n = 3 }, + [3] = {'three', 1, true, n = 3 }, + [4] = {'one', 2, true, n = 3 }, + [5] = {'two', 2, true, n = 3 }, + [6] = {'three', 2, true, n = 3 }, + [7] = {'one', 3, true, n = 3 }, + [8] = {'two', 3, true, n = 3 }, + [9] = {'three', 3, true, n = 3 }, + [10] = {'one', 1, false, n = 3 }, + [11] = {'two', 1, false, n = 3 }, + [12] = {'three', 1, false, n = 3 }, + [13] = {'one', 2, false, n = 3 }, + [14] = {'two', 2, false, n = 3 }, + [15] = {'three', 2, false, n = 3 }, + [16] = {'one', 3, false, n = 3 }, + [17] = {'two', 3, false, n = 3 }, + [18] = {'three', 3, false, n = 3 }, + }, permute.list_table(strs, ints, bools)) + end) + + + it("is nil-safe, given 'n' is set", function() + local bools = utils.pack(nil, true, false) + local strs = utils.pack("one", "two", nil) + assert.same({ + [1] = {nil, 'one', n = 2 }, + [2] = {true, 'one', n = 2 }, + [3] = {false, 'one', n = 2 }, + [4] = {nil, 'two', n = 2 }, + [5] = {true, 'two', n = 2 }, + [6] = {false, 'two', n = 2 }, + [7] = {nil, nil, n = 2 }, + [8] = {true, nil, n = 2 }, + [9] = {false, nil, n = 2 }, + }, permute.list_table(bools, strs)) + end) + + + it("returns nil on empty list", function() + assert.same({}, permute.list_table({})) + end) + + end) + +end) diff --git a/Data/Libraries/Penlight/spec/pretty_spec.lua b/Data/Libraries/Penlight/spec/pretty_spec.lua new file mode 100644 index 0000000..85e3770 --- /dev/null +++ b/Data/Libraries/Penlight/spec/pretty_spec.lua @@ -0,0 +1,40 @@ +local pretty = require("pl.pretty") + +describe("pl.pretty.number", function () + + it("should format memory", function () + local function assert_memory (expected, input) + assert.is.equal(expected, pretty.number(input, "M")) + end + assert_memory("123B", 123) + assert_memory("1.2KiB", 1234) + assert_memory("10.0KiB", 10*1024) + assert_memory("1.0MiB", 1024*1024) + assert_memory("1.0GiB", 1024*1024*1024) + end) + + it("should format postfixes", function () + local function assert_postfix(expected, input) + assert.is.equal(expected, pretty.number(input, "N", 2)) + end + assert_postfix("123", 123) + assert_postfix("1.23K", 1234) + assert_postfix("10.24K", 10*1024) + assert_postfix("1.05M", 1024*1024) + assert_postfix("1.07B", 1024*1024*1024) + end) + + it("should format postfixes", function () + local function assert_separator(expected, input) + assert.is.equal(expected, pretty.number(input, "T")) + end + assert_separator('123', 123) + assert_separator('1,234', 1234) + assert_separator('12,345', 12345) + assert_separator('123,456', 123456) + assert_separator('1,234,567', 1234567) + assert_separator('12,345,678', 12345678) + end) + + +end) diff --git a/Data/Libraries/Penlight/spec/set_spec.lua b/Data/Libraries/Penlight/spec/set_spec.lua new file mode 100644 index 0000000..02febca --- /dev/null +++ b/Data/Libraries/Penlight/spec/set_spec.lua @@ -0,0 +1,84 @@ +local Set = require("pl.Set") + +describe("pl.Set", function () + + local s = Set() + local s1_2 = Set({ 1, 2 }) + local s1_2_3 = Set({ 1, 2, 3 }) + local s1_3 = Set({ 1, 3 }) + local s2 = Set({ 2 }) + local s2_1 = Set({ 2, 1 }) + local s2_3 = Set({ 2, 3 }) + local s3 = Set({ 3 }) + local sm = Set({ "foo", "bar" }) + + it("should produce a set object", function () + assert.is.same({ true, true }, s1_2) + end) + + it("should produce identical sets for any ordered input", function () + assert.is.same(s1_2, s2_1) + end) + + describe("should have an operator for", function () + + it("union", function () + assert.is.same(s1_2_3, s1_2 + s3) + assert.is.same(s1_2_3, s1_2 + 3) + end) + + it("intersection", function () + assert.is.same(s2, s1_2 * s2_3) + end) + + it("difference", function () + assert.is.same(s2_1, s1_2_3 - s3) + assert.is.same(s2_3, s1_2_3 - 1) + end) + + it("symmetric difference", function () + assert.is.same(s1_3, s1_2 ^ s2_3) + end) + + it("tostring", function () + -- Cannot test multi-entry sets because of non-deterministic key order + assert.is.same('[2]', tostring(s2)) + end) + + end) + + describe("should provide functions", function () + + it("isempty", function () + assert.is.truthy(Set.isempty(s)) + assert.is.falsy(Set.isempty(s3)) + end) + + it("set", function () + local m = Set() + Set.set(m, 'foo', true) + m.bar = true + assert.is.same(m, sm) + assert.is_not.same(m, s1_2) + end) + + end) + + describe("should have a comparison operator for", function () + + it("supersets/subsets than", function () + assert.is.truthy(s1_2 > s2) + assert.is.falsy(s1_3 > s2) + assert.is.falsy(s1_2 > s2_3) + assert.is.truthy(s1_2 < s1_2_3) + assert.is.falsy(s1_2_3 < s1_2) + end) + + it("equality", function () + assert.is.truthy(s1_2 == s2_1) + assert.is.falsy(s1_2 == s2_3) + end) + + end) + +end) diff --git a/Data/Libraries/Penlight/spec/text_spec.lua b/Data/Libraries/Penlight/spec/text_spec.lua new file mode 100644 index 0000000..cd42f06 --- /dev/null +++ b/Data/Libraries/Penlight/spec/text_spec.lua @@ -0,0 +1,11 @@ +local text = require("pl.text") + +describe("pl.text.Template", function () + + it("replaces placeholders", function () + local tempalte = text.Template("${here} is the $answer") + local out = tempalte:substitute({ here = 'one', answer = 'two' }) + assert.is.equal('one is the two', out) + end) + +end) diff --git a/Data/Libraries/Penlight/spec/utils-deprecate_spec.lua b/Data/Libraries/Penlight/spec/utils-deprecate_spec.lua new file mode 100644 index 0000000..1a6e352 --- /dev/null +++ b/Data/Libraries/Penlight/spec/utils-deprecate_spec.lua @@ -0,0 +1,128 @@ +local utils = require("pl.utils") + +describe("pl.utils", function () + + local old_fn, last_msg, last_trace + + before_each(function() + old_fn = function() end + last_msg = nil + last_trace = nil + utils.set_deprecation_func(function(msg, trace) + last_msg = msg + last_trace = trace + end) + end) + + + after_each(function() + utils.deprecation_warning = old_fn + end) + + + + describe("set_deprecation_func", function () + + it("accepts nil as callback", function() + assert.has.no.error(function() + utils.set_deprecation_func() + end) + end) + + + it("accepts function as callback", function() + assert.has.no.error(function() + utils.set_deprecation_func(function() end) + end) + end) + + + it("fails on non-functions", function() + assert.has.error(function() + utils.set_deprecation_func("not a function") + end, "argument 1 expected a 'function', got a 'string'") + end) + + end) + + + + describe("raise_deprecation", function () + + it("requires the opts table", function() + assert.has.error(function() utils.raise_deprecation(nil) end, + "argument 1 expected a 'table', got a 'nil'") + end) + + + it("requires the opts.message field", function() + assert.has.error(function() utils.raise_deprecation({}) end, + "field 'message' of the options table must be a string") + end) + + + it("should output the message", function () + utils.raise_deprecation { + message = "hello world" + } + assert.equal("hello world", last_msg) + end) + + + it("should output the deprecated version", function () + utils.raise_deprecation { + message = "hello world", + deprecated_after = "2.0.0", + } + assert.equal("hello world (deprecated after 2.0.0)", last_msg) + end) + + + it("should output the removal version", function () + utils.raise_deprecation { + message = "hello world", + version_removed = "3.0.0", + } + assert.equal("hello world (scheduled for removal in 3.0.0)", last_msg) + end) + + + it("should output the deprecated and removal versions", function () + utils.raise_deprecation { + message = "hello world", + deprecated_after = "2.0.0", + version_removed = "3.0.0", + } + assert.equal("hello world (deprecated after 2.0.0, scheduled for removal in 3.0.0)", last_msg) + end) + + + it("should output the application/module name", function () + utils.raise_deprecation { + source = "MyApp 1.2.3", + message = "hello world", + deprecated_after = "2.0.0", + version_removed = "3.0.0", + } + assert.equal("[MyApp 1.2.3] hello world (deprecated after 2.0.0, scheduled for removal in 3.0.0)", last_msg) + end) + + + it("should add a stracktrace", function () + local function my_function_name() + utils.raise_deprecation { + source = "MyApp 1.2.3", + message = "hello world", + deprecated_after = "2.0.0", + version_removed = "3.0.0", + } + end + my_function_name() + + assert.Not.match("raise_deprecation", last_trace) + assert.match("my_function_name", last_trace) + end) + + end) + +end) diff --git a/Data/Libraries/Penlight/spec/utils-npairs_spec.lua b/Data/Libraries/Penlight/spec/utils-npairs_spec.lua new file mode 100644 index 0000000..7e3c5d4 --- /dev/null +++ b/Data/Libraries/Penlight/spec/utils-npairs_spec.lua @@ -0,0 +1,105 @@ +local utils = require("pl.utils") + +describe("pl.utils", function () + + describe("npairs", function () + local npairs = utils.npairs + + it("start index defaults to 1", function() + local t1 = { 1, 2, 3 } + local t2 = {} + for i, v in npairs(t1, nil, 2) do t2[i] = v end + assert.are.same({ 1, 2 }, t2) + end) + + + it("end index defaults to `t.n`", function() + local t1 = { n = 2, 1, 2, 3 } + local t2 = {} + for i, v in npairs(t1) do t2[i] = v end + assert.are.same({1, 2}, t2) + end) + + + it("step size defaults to 1", function() + local t1 = { 1, 2, 3 } + local t2 = {} + for i, v in npairs(t1) do t2[i] = v end + assert.are.same({1, 2, 3}, t2) + end) + + + it("step size cannot be 0", function() + local t1 = { 1, 2, 3 } + assert.has.error(function() + npairs(t1, nil, nil, 0) + end, "iterator step-size cannot be 0") + end) + + + it("end index defaults to `#t` if there is no `t.n`", function() + local t1 = { 1, 2, 3 } + local t2 = {} + for i, v in npairs(t1) do t2[i] = v end + assert.are.same({1, 2, 3}, t2) + end) + + + it("returns nothing if start index is beyond end index", function() + local t1 = { 1, 2, 3 } + local t2 = {} + for i, v in npairs(t1, 5, 3) do t2[i] = v end + assert.are.same({}, t2) + end) + + + it("returns nothing if start index is beyond end index, with negative step size", function() + local t1 = { 1, 2, 3 } + local t2 = {} + for i, v in npairs(t1, 3, 1, -1) do t2[#t2+1] = v end + assert.are.same({ 3, 2, 1}, t2) + end) + + + it("returns 1 key/value if end == start index", function() + local t1 = { 1, 2, 3 } + local t2 = {} + for i, v in npairs(t1, 2, 2) do t2[i] = v end + assert.are.same({ [2] = 2 }, t2) + end) + + + it("returns negative to positive ranges", function() + local t1 = { [-5] = -5, [-4] = -4, [-3] = -3, [-2] = -2, [-1] = -1, [0] = 0, 1, 2, 3 } + local t2 = {} + for i, v in npairs(t1, -4, 1) do t2[i] = v end + assert.are.same({ [-4] = -4, [-3] = -3, [-2] = -2, [-1] = -1, [0] = 0, 1 }, t2) + end) + + + it("returns nil values with the range", function() + local t1 = { n = 3 } + local t2 = {} + for i, v in npairs(t1) do t2[i] = tostring(v) end + assert.are.same({ "nil", "nil", "nil" }, t2) + end) + + + it("honours positive step size", function() + local t1 = { [-5] = -5, [-4] = -4, [-3] = -3, [-2] = -2, [-1] = -1, [0] = 0, 1, 2, 3 } + local t2 = {} + for i, v in npairs(t1, -4, 1, 2) do t2[#t2+1] = v end + assert.are.same({ -4, -2, 0}, t2) + end) + + + it("honours negative step size", function() + local t1 = { [-5] = -5, [-4] = -4, [-3] = -3, [-2] = -2, [-1] = -1, [0] = 0, 1, 2, 3 } + local t2 = {} + for i, v in npairs(t1, 0, -5, -2) do t2[#t2+1] = v end + assert.are.same({ 0, -2, -4 }, t2) + end) + + end) + +end) diff --git a/Data/Libraries/Penlight/tests/lua/animal.lua b/Data/Libraries/Penlight/tests/lua/animal.lua new file mode 100644 index 0000000..9366db9 --- /dev/null +++ b/Data/Libraries/Penlight/tests/lua/animal.lua @@ -0,0 +1,54 @@ +-- Module containing classes +local class = require 'pl.class' +local utils = require 'pl.utils' +local error = error +if utils.lua51 then + module 'animal' +else + _ENV = {} +end + +class.Animal() + +function Animal:_init(name) + self.name = name +end + +function Animal:__tostring() + return self.name..': '..self:speak() +end + +class.Dog(Animal) + +function Dog:speak() + return 'bark' +end + +class.Cat(Animal) + +function Cat:_init(name,breed) + self:super(name) -- must init base! + self.breed = breed +end + +function Cat:speak() + return 'meow' +end + +-- you may declare the methods in-line like so; +-- note the meaning of `_base`! +class.Lion { + _base = Cat; + speak = function(self) + return 'roar' + end +} + +-- a class may handle unknown methods with `catch`: +Lion:catch(function(self,name) + return function() error("no such method "..name,2) end +end) + +if not utils.lua51 then + return _ENV +end diff --git a/Data/Libraries/Penlight/tests/lua/bar.lua b/Data/Libraries/Penlight/tests/lua/bar.lua new file mode 100644 index 0000000..191cb70 --- /dev/null +++ b/Data/Libraries/Penlight/tests/lua/bar.lua @@ -0,0 +1,10 @@ +--- test module for demonstrating app.require_here() +local bar = {} + +function bar.name () + return 'bar' +end + +return bar + + diff --git a/Data/Libraries/Penlight/tests/lua/foo/args.lua b/Data/Libraries/Penlight/tests/lua/foo/args.lua new file mode 100644 index 0000000..8a53130 --- /dev/null +++ b/Data/Libraries/Penlight/tests/lua/foo/args.lua @@ -0,0 +1,9 @@ +--- test module for demonstrating app.require_here() +local args = {} + +function args.answer () + return 42 +end + +return args + diff --git a/Data/Libraries/Penlight/tests/lua/mod52.lua b/Data/Libraries/Penlight/tests/lua/mod52.lua new file mode 100644 index 0000000..3dd4e46 --- /dev/null +++ b/Data/Libraries/Penlight/tests/lua/mod52.lua @@ -0,0 +1,30 @@ +local test = require 'pl.test' +local LUA_VERSION = _VERSION +print(LUA_VERSION) + +-- if STRICT is true, then M is distinct from _ENV, and ONLY contains +-- the exported functions! + +local _ENV,M = require 'pl.import_into' (rawget(_G,'STRICT')) + +function answer () + -- of course, you don't have the usual global environment available + -- so define it as a local up above, or use utils.import(_G). + + local versioned_errors = { + ["1"] = "attempt to call global 'print'", + ["2"] = "attempt to call global 'print'", + ["3"] = "attempt to call a nil value", + ["4"] = "a nil value", + } + local expected = versioned_errors[LUA_VERSION:match("Lua 5.(%d)")] + test.assertraise(function() + print 'hello' + end, expected) + + -- but all the Penlight modules are available + return pretty.write(utils.split '10 20 30', '') +end + +return M + diff --git a/Data/Libraries/Penlight/tests/lua/mymod.lua b/Data/Libraries/Penlight/tests/lua/mymod.lua new file mode 100644 index 0000000..e7d73a7 --- /dev/null +++ b/Data/Libraries/Penlight/tests/lua/mymod.lua @@ -0,0 +1,19 @@ +local strict = require 'pl.strict' +local test = require 'pl.test' +local M = strict.module (...) + +function M.answer () + Boo = false -- fine, it's a declared global + -- in strict mode, you cannot assign to globals if you aren't in main + test.assertraise(function() + Foo = true + end," assign to undeclared global 'Foo'") + return 42 +end + +function M.question () + return 'what is the answer to Life, the Universe and Everything?' +end + +return M + diff --git a/Data/Libraries/Penlight/tests/test-__vector.lua b/Data/Libraries/Penlight/tests/test-__vector.lua new file mode 100644 index 0000000..e040564 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-__vector.lua @@ -0,0 +1,123 @@ +---- deriving specialized classes from List +-- illustrating covariance of List methods +local test = require 'pl.test' +local class = require 'pl.class' +local types = require 'pl.types' +local operator = require 'pl.operator' +local List = require 'pl.List' + +local asserteq = test.asserteq + +class.Vector(List) + + +function Vector.range (x1,x2,delta) + return Vector(List.range(x1,x2,delta)) +end + +local function vbinop (op,v1,v2,scalar) + if not Vector:class_of(v1) then + v2, v1 = v1, v2 + end + if type(v2) ~= 'table' then + return v1:map(op,v2) + else + if scalar then error("operation not permitted on two vectors",3) end + if #v1 ~= #v2 then error("vectors have different lengths",3) end + return v1:map2(op,v2) + end +end + +function Vector.__add (v1,v2) + return vbinop(operator.add,v1,v2) +end + +function Vector.__sub (v1,v2) + return vbinop(operator.sub,v1,v2) +end + +function Vector.__mul (v1,v2) + return vbinop(operator.mul,v1,v2,true) +end + +function Vector.__div (v1,v2) + return vbinop(operator.div,v1,v2,true) +end + +function Vector.__unm (v) + return v:map(operator.unm) +end + +Vector:catch(List.default_map_with(math)) + +v = Vector() + +assert(v:is_a(Vector)) +assert(Vector:class_of(v)) + +v:append(10) +v:append(20) +asserteq(1+v,v+1) + +-- covariance: the inherited Vector.map returns a Vector +asserteq(List{1,2} + v:map '2*_',{21,42}) + +u = Vector{1,2} + +asserteq(v + u,{11,22}) +asserteq(v - u,{9,18}) +asserteq (v - 1, {9,19}) +asserteq(2 * v, {20,40}) +-- print(v * v) -- throws error: not permitted +-- print(v + Vector{1,2,3}) -- throws error: different lengths +asserteq(2*v + u, {21,42}) +asserteq(-v, {-10,-20}) + +-- Vector.slice returns the Right Thing due to covariance +asserteq( + Vector.range(0,1,0.1):slice(1,3)+1, + {1,1.1,1.2}, + 1e-8) + +u:transform '_+1' +asserteq(u,{2,3}) + +u = Vector.range(0,1,0.1) +asserteq( + u:map(math.sin), + {0,0.0998,0.1986,0.2955,0.3894,0.4794,0.5646,0.6442,0.7173,0.7833,0.8414}, +0.001) + +-- unknown Vector methods are assumed to be math.* functions +asserteq(Vector{-1,2,3,-4}:abs(),Vector.range(1,4)) + +local R = Vector.range + +-- concatenating two Vectors returns another vector (covariance again) +-- note the operator precedence here... +asserteq(( + R(0,1,0.1)..R(1.2,2,0.2)) + 1, + {1,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2,2.2,2.4,2.6,2.8,3}, + 1e-8) + + +class.Strings(List) + +Strings:catch(List.default_map_with(string)) + +ls = Strings{'one','two','three'} +asserteq(ls:upper(),{'ONE','TWO','THREE'}) +asserteq(ls:sub(1,2),{'on','tw','th'}) + +-- all map operations on specialized lists +-- results in another list of that type! This isn't necessarily +-- what you want. +local sizes = ls:map '#' +asserteq(sizes, {3,3,5}) +asserteq(types.type(sizes),'Strings') +asserteq(sizes:is_a(Strings),true) +sizes = Vector:cast(sizes) +asserteq(types.type(sizes),'Vector') +asserteq(sizes+1,{4,4,6}) + + diff --git a/Data/Libraries/Penlight/tests/test-app.lua b/Data/Libraries/Penlight/tests/test-app.lua new file mode 100644 index 0000000..980cb16 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-app.lua @@ -0,0 +1,280 @@ +local app = require "pl.app" +local utils = require "pl.utils" +local path = require "pl.path" +local asserteq = require 'pl.test'.asserteq + +local quote = utils.quote_arg + +local _, cmd = app.lua() +cmd = cmd .. " " .. quote({"-e", "package.path=[[./lua/?.lua;./lua/?/init.lua;]]..package.path"}) + +local function run_script(s, fname) + local tmpname = path.tmpname() + if fname then + tmpname = path.join(path.dirname(tmpname), fname) + end + assert(utils.writefile(tmpname, s)) + local success, code, stdout, stderr = utils.executeex(cmd.." "..tmpname) + os.remove(tmpname) + return success, code, stdout, stderr +end + +do -- app.script_name + + local success, code, stdout, stderr = run_script([[ + print(require("pl.app").script_name()) + ]], + "justsomescriptname.lua") + asserteq(stderr, "") + asserteq(stdout:match("(justsome.+)$"), "justsomescriptname.lua\n") + + + -- commandline, no scriptname + local success, code, stdout, stderr = run_script([[ + arg[0] = nil -- simulate no scriptname + local name, err = require("pl.app").script_name() + io.stdout:write(tostring(name)) + io.stderr:write(err) + ]]) + assert(stderr:find("No script name found")) + asserteq(stdout, "nil") + + + -- commandline, no args table + local success, code, stdout, stderr = run_script([[ + arg = nil -- simulate no arg table + local name, err = require("pl.app").script_name() + io.stdout:write(tostring(name)) + io.stderr:write(err) + ]]) + assert(stderr:find("No script name found")) + asserteq(stdout, "nil") +end + +do -- app.require_here + local cd = path.currentdir() --path.dirname(path.tmpname()) + + -- plain script name + local success, code, stdout, stderr = run_script([[ + arg[0] = "justsomescriptname.lua" + local p = package.path + require("pl.app").require_here() + print(package.path:sub(1, -#p-1)) + ]]) + asserteq(stderr, "") + stdout = path.normcase(stdout) + assert(stdout:find(path.normcase(cd.."/?.lua;"), 1, true)) + assert(stdout:find(path.normcase(cd.."/?/init.lua;"), 1, true)) + + + -- plain script name, with a relative base name + local success, code, stdout, stderr = run_script([[ + arg[0] = "justsomescriptname.lua" + local p = package.path + require("pl.app").require_here("basepath/to/somewhere") + print(package.path:sub(1, -#p-1)) + ]]) + asserteq(stderr, "") + stdout = path.normcase(stdout) + assert(stdout:find(path.normcase(cd.."/basepath/to/somewhere/?.lua;"), 1, true)) + assert(stdout:find(path.normcase(cd.."/basepath/to/somewhere/?/init.lua;"), 1, true)) + + + -- plain script name, with an absolute base name + local success, code, stdout, stderr = run_script([[ + arg[0] = "justsomescriptname.lua" + local p = package.path + require("pl.app").require_here("/basepath/to/somewhere") + print(package.path:sub(1, -#p-1)) + ]]) + asserteq(stderr, "") + stdout = path.normcase(stdout) + asserteq(stdout, path.normcase("/basepath/to/somewhere/?.lua;/basepath/to/somewhere/?/init.lua;\n")) + + + -- scriptname with a relative path + local success, code, stdout, stderr = run_script([[ + arg[0] = "relative/prefix/justsomescriptname.lua" + local p = package.path + require("pl.app").require_here() + print(package.path:sub(1, -#p-1)) + os.exit() + ]]) + asserteq(stderr, "") + stdout = path.normcase(stdout) + assert(stdout:find(path.normcase(cd.."/relative/prefix/?.lua;"), 1, true)) + assert(stdout:find(path.normcase(cd.."/relative/prefix/?/init.lua;"), 1, true)) + + + -- script with an absolute path + local success, code, stdout, stderr = run_script([[ + arg[0] = "/fixed/justsomescriptname.lua" + local p = package.path + require("pl.app").require_here() + print(package.path:sub(1, -#p-1)) + ]]) + asserteq(stderr, "") + stdout = path.normcase(stdout) + asserteq(stdout, path.normcase("/fixed/?.lua;/fixed/?/init.lua;\n")) + +end + + +do -- app.appfile + local success, code, stdout, stderr = run_script([[ + arg[0] = "some/path/justsomescriptname_for_penlight_testing.lua" + print(require("pl.app").appfile("filename.data")) + ]]) + asserteq(stderr, "") + stdout = path.normcase(stdout) + local fname = path.normcase(path.expanduser("~/.justsomescriptname_for_penlight_testing/filename.data")) + asserteq(stdout, fname .."\n") + assert(path.isdir(path.dirname(fname))) + path.rmdir(path.dirname(fname)) + +end + + +do -- app.lua + local success, code, stdout, stderr = run_script([[ + arg[0] = "justsomescriptname.lua" + local a,b = require("pl.app").lua() + print(a) + ]]) + asserteq(stderr, "") + asserteq(stdout, cmd .."\n") + +end + + +do -- app.parse_args + + -- no value specified + local args = utils.split("-a -b") + local t,s = app.parse_args(args, { a = true}) + asserteq(t, nil) + asserteq(s, "no value for 'a'") + + + -- flag that take a value, space separated + local args = utils.split("-a -b value -c") + local t,s = app.parse_args(args, { b = true}) + asserteq(t, { + a = true, + b = "value", + c = true, + }) + asserteq(s, {}) + + + -- flag_with_values specified as a list + local args = utils.split("-a -b value -c") + local t,s = app.parse_args(args, { "b" }) + asserteq(t, { + a = true, + b = "value", + c = true, + }) + asserteq(s, {}) + + + -- flag_with_values missing value at end + local args = utils.split("-a -b") + local t,s = app.parse_args(args, { "b" }) + asserteq(t, nil) + asserteq(s, "no value for 'b'") + + + -- error on an unknown flag + local args = utils.split("-a -b value -c") + local t,s = app.parse_args(args, { b = true }, { "b", "c" }) + asserteq(t, nil) + asserteq(s, "unknown flag 'a'") + + + -- flag that doesn't take a value + local args = utils.split("-a -b:value") + local t,s = app.parse_args(args, {}) + asserteq(t, { + ["a"] = true, + ["b"] = "value" + }) + asserteq(s, {}) + + + -- correctly parsed values, spaces, :, =, and multiple : or = + local args = utils.split("-a value -b value:one=two -c=value2:2") + local t,s = app.parse_args(args, { "a", "b", "c" }) + asserteq(t, { + ["a"] = "value", + ["b"] = "value:one=two", + ["c"] = "value2:2", + }) + asserteq(s, {}) + + + -- many values, duplicates, and parameters mixed + local args = utils.split( + "-a -b -cde --long1 --ff:ffvalue --gg=ggvalue -h:hvalue -i=ivalue " .. + "-i=2ndvalue param -i:3rdvalue -j1 -k2 -1:hello remaining values") + local t,s = app.parse_args(args) + asserteq({ + i = "3rdvalue", + ["1"] = "hello", + ff = "ffvalue", + long1 = true, + c = true, + b = true, + gg = "ggvalue", + j = "1", + k = "2", + d = true, + h = "hvalue", + a = true, + e = true + }, t) + asserteq({ + "param", + "remaining", + "values" + }, s) + + + -- specify valid flags and aliasses + local args = utils.split("-a -b value -e -f3") + local t,s = app.parse_args(args, + { + "b", + f = true, + }, { + bully = "b", -- b with value will be reported as 'bully', alias as string + a = true, -- hash-type value + c = { "d", "e" }, -- e will be reported as c, aliasses as list/table + }) + asserteq(t, { + a = true, + bully = "value", + c = true, + f = "3", + }) + asserteq(s, {}) + + + -- error on an unknown flag, in a chain of short ones + local args = utils.split("-b value -cd") + local t,s = app.parse_args(args, { b = true }, { "b", "c" }) + asserteq(t, nil) + asserteq(s, "unknown flag 'd'") + + + -- flag, in a chain of short ones, gets converted to alias + local args = utils.split("-dbc") + local t,s = app.parse_args(args, nil, { "d", full_name = "b", "c" }) + asserteq(t, { + full_name = true, -- specified as b in a chain of short ones + c = true, + d = true, + }) + asserteq(s, {}) + +end diff --git a/Data/Libraries/Penlight/tests/test-class.lua b/Data/Libraries/Penlight/tests/test-class.lua new file mode 100644 index 0000000..8621005 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-class.lua @@ -0,0 +1,207 @@ +local class = require 'pl.class' +local test = require 'pl.test' +asserteq = test.asserteq +T = test.tuple + +A = class() + +function A:_init () + self.a = 1 +end + +-- calling base class' ctor automatically +A1 = class(A) + +asserteq(A1(),{a=1}) + +-- explicitly calling base ctor with super + +B = class(A) + +function B:_init () + self:super() + self.b = 2 +end + +function B:foo () + self.eee = 1 +end + +function B:foo2 () + self.g = 8 +end + +asserteq(B(),{a=1,b=2}) + +-- can continue this chain + +C = class(B) + +function C:_init () + self:super() + self.c = 3 +end + +function C:foo () + -- recommended way to call inherited version of method... + B.foo(self) +end + +c = C() +c:foo() + +asserteq(c,{a=1,b=2,c=3,eee=1}) + +-- test indirect inherit + +D = class(C) + +E = class(D) + +function E:_init () + self:super() + self.e = 4 +end + +function E:foo () + -- recommended way to call inherited version of method... + self.eeee = 5 + C.foo(self) +end + +F = class(E) + +function F:_init () + self:super() + self.f = 6 +end + +f = F() +f:foo() +f:foo2() -- Test : invocation inherits this function from all the way up in B + +asserteq(f,{a=1,b=2,c=3,eee=1,e=4,eeee=5,f=6,g=8}) + +-- Test that inappropriate calls to super() fail gracefully + +G = class() -- Class with no init + +H = class(G) -- Class with an init that wrongly calls super() + +function H:_init() + self:super() -- Notice: G has no _init +end + +I = class(H) -- Inherits the init with a bad super +J = class(I) -- Grandparent-inits the init with a bad super + +K = class(J) -- Has an init, which calls the init with a bad super + +function K:_init() + self:super() +end + +local function createG() + return G() +end + +local function createH() -- Wrapper function for pcall + return H() +end + +local function createJ() + return J() +end + +local function createK() + return K() +end + +assert(pcall(createG)) -- Should succeed +assert(not pcall(createH)) -- These three should fail +assert(not pcall(createJ)) +assert(not pcall(createK)) + +--- class methods! +assert(c:is_a(C)) +assert(c:is_a(B)) +assert(c:is_a(A)) +assert(c:is_a() == C) +assert(C:class_of(c)) +assert(B:class_of(c)) +assert(A:class_of(c)) + +--- metamethods! + +function C:__tostring () + return ("%d:%d:%d"):format(self.a,self.b,self.c) +end + +function C.__eq (c1,c2) + return c1.a == c2.a and c1.b == c2.b and c1.c == c2.c +end + +asserteq(C(),{a=1,b=2,c=3}) + +asserteq(tostring(C()),"1:2:3") + +asserteq(C()==C(),true) + +----- properties ----- + +local MyProps = class(class.properties) +local setted_a, got_b + +function MyProps:_init () + self._a = 1 + self._b = 2 +end + +function MyProps:set_a (v) + setted_a = true + self._a = v +end + +function MyProps:get_b () + got_b = true + return self._b +end + +function MyProps:set (a,b) + self._a = a + self._b = b +end + +local mp = MyProps() + +mp.a = 10 + +asserteq(mp.a,10) +asserteq(mp.b,2) +asserteq(setted_a and got_b, true) + +class.MoreProps(MyProps) +local setted_c + +function MoreProps:_init() + self:super() + self._c = 3 +end + +function MoreProps:set_c (c) + setted_c = true + self._c = c +end + +mm = MoreProps() + +mm:set(10,20) +mm.c = 30 + +asserteq(setted_c, true) +asserteq(T(mm.a, mm.b, mm.c),T(10,20,30)) + + + + + diff --git a/Data/Libraries/Penlight/tests/test-class2.lua b/Data/Libraries/Penlight/tests/test-class2.lua new file mode 100644 index 0000000..b463b6e --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-class2.lua @@ -0,0 +1,28 @@ +-- animal.lua +require 'pl.app'.require_here 'lua' + +local test = require 'pl.test' +local asserteq = test.asserteq + +local A = require 'animal' + +local fido, felix, leo +fido = A.Dog('Fido') +felix = A.Cat('Felix','Tabby') +leo = A.Lion('Leo','African') + +asserteq(fido:speak(),'bark') +asserteq(felix:speak(),'meow') +asserteq(leo:speak(),'roar') + +asserteq(tostring(leo),'Leo: roar') + +test.assertraise(function() leo:circus_act() end, "no such method circus_act") + +asserteq(leo:is_a(A.Animal),true) +asserteq(leo:is_a(A.Dog),false) +asserteq(leo:is_a(A.Cat),true) + +asserteq(A.Dog:class_of(leo),false) +asserteq(A.Cat:class_of(leo),true) +asserteq(A.Lion:class_of(leo),true) diff --git a/Data/Libraries/Penlight/tests/test-class3.lua b/Data/Libraries/Penlight/tests/test-class3.lua new file mode 100644 index 0000000..eb3c26f --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-class3.lua @@ -0,0 +1,29 @@ +-- another way to define classes. Works particularly well +-- with Moonscript +local class = require('pl.class') +local A = class{ + _init = function(self, name) + self.name = name + end, + greet = function(self) + return "hello " .. self.name + end, + __tostring = function(self) + return self.name + end +} + +local B = class{ + _base = A, + + greet = function(self) + return "hola " .. self.name + end +} + +local a = A('john') +assert(a:greet()=="hello john") +assert(tostring(a) == "john") +local b = B('juan') +assert(b:greet()=="hola juan") +assert(tostring(b)=="juan") diff --git a/Data/Libraries/Penlight/tests/test-class4.lua b/Data/Libraries/Penlight/tests/test-class4.lua new file mode 100644 index 0000000..2a4945f --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-class4.lua @@ -0,0 +1,26 @@ +local class = require 'pl.class' +local A = class() +function A:_init() + self.init_chain = "A" +end +local B = class(A) +local C = class(B) +function C:_init() + self:super() + self.init_chain = self.init_chain.."C" +end +local D = class(C) +local E = class(D) +function E:_init() + self:super() + self.init_chain = self.init_chain.."E" +end +local F = class(E) +local G = class(F) +function G:_init() + self:super() + self.init_chain = self.init_chain.."G" +end + +local i = G() +assert(i.init_chain == "ACEG") diff --git a/Data/Libraries/Penlight/tests/test-compat.lua b/Data/Libraries/Penlight/tests/test-compat.lua new file mode 100644 index 0000000..c4a2724 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-compat.lua @@ -0,0 +1,29 @@ +local test = require 'pl.test' +local asserteq = test.asserteq + +local compat = require "pl.compat" +local coroutine = require "coroutine" + +local code_generator = coroutine.wrap(function() + local result = {"ret", "urn \"Hello World!\""} + for _,v in ipairs(result) do + coroutine.yield(v) + end + coroutine.yield(nil) +end) + +local f, err = compat.load(code_generator) +asserteq(err, nil) +asserteq(f(), "Hello World!") + + +-- package.searchpath +if compat.lua51 and not compat.jit then + assert(package.searchpath("pl.compat", package.path):match("lua[/\\]pl[/\\]compat")) + + local path = "some/?/nice.path;another/?.path" + local ok, err = package.searchpath("my.file.name", path, ".", "/") + asserteq(err, "\tno file 'some/my/file/name/nice.path'\n\tno file 'another/my/file/name.path'") + local ok, err = package.searchpath("my/file/name", path, "/", ".") + asserteq(err, "\tno file 'some/my.file.name/nice.path'\n\tno file 'another/my.file.name.path'") +end
\ No newline at end of file diff --git a/Data/Libraries/Penlight/tests/test-comprehension.lua b/Data/Libraries/Penlight/tests/test-comprehension.lua new file mode 100644 index 0000000..900bb45 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-comprehension.lua @@ -0,0 +1,68 @@ +-- test-comprehension.lua +-- test of comprehension.lua +local utils = require 'pl.utils' +local comp = require 'pl.comprehension' . new() +local asserteq = require 'pl.test' . asserteq + +-- test of list building +asserteq(comp 'x for x' {}, {}) +asserteq(comp 'x for x' {2,3}, {2,3}) +asserteq(comp 'x^2 for x' {2,3}, {2^2,3^2}) +asserteq(comp 'x for x if x % 2 == 0' {4,5,6,7}, {4,6}) +asserteq(comp '{x,y} for x for y if x>2 if y>4' ({2,3},{4,5}), {{3,5}}) + +-- test of table building +local t = comp 'table(x,x+1 for x)' {3,4} +assert(t[3] == 3+1 and t[4] == 4+1) +local t = comp 'table(x,x+y for x for y)' ({3,4}, {2}) +assert(t[3] == 3+2 and t[4] == 4+2) +local t = comp 'table(v,k for k,v in pairs(_1))' {[3]=5, [5]=7} +assert(t[5] == 3 and t[7] == 5) + +-- test of sum +assert(comp 'sum(x for x)' {} == 0) +assert(comp 'sum(x for x)' {2,3} == 2+3) +assert(comp 'sum(x^2 for x)' {2,3} == 2^2+3^2) +assert(comp 'sum(x*y for x for y)' ({2,3}, {4,5}) == 2*4+2*5+3*4+3*5) +assert(comp 'sum(x^2 for x if x % 2 == 0)' {4,5,6,7} == 4^2+6^2) +assert(comp 'sum(x*y for x for y if x>2 if y>4)' ({2,3}, {4,5}) == 3*5) + +-- test of min/max +assert(comp 'min(x for x)' {3,5,2,4} == 2) +assert(comp 'max(x for x)' {3,5,2,4} == 5) + +-- test of placeholder parameters -- +assert(comp 'sum(x^_1 + _3 for x if x >= _4)' (2, nil, 3, 4, {3,4,5}) + == 4^2+3 + 5^2+3) + +-- test of for = +assert(comp 'sum(x^2 for x=2,3)' () == 2^2+3^2) +assert(comp 'sum(x^2 for x=2,6,1+1)' () == 2^2+4^2+6^2) +assert(comp 'sum(x*y*z for x=1,2 for y=3,3 for z)' {5,6} == + 1*3*5 + 2*3*5 + 1*3*6 + 2*3*6) +assert(comp 'sum(x*y*z for z for x=1,2 for y=3,3)' {5,6} == + 1*3*5 + 2*3*5 + 1*3*6 + 2*3*6) + +-- test of for in +assert(comp 'sum(i*v for i,v in ipairs(_1))' {2,3} == 1*2+2*3) +assert(comp 'sum(i*v for i,v in _1,_2,_3)' (ipairs{2,3}) == 1*2+2*3) + +-- test of difficult syntax +asserteq(comp '" x for x " for x' {2}, {' x for x '}) +asserteq(comp 'x --[=[for x\n\n]=] for x' {2}, {2}) +asserteq(comp '(function() for i = 1,1 do return x*2 end end)() for x' + {2}, {4}) +assert(comp 'sum(("_5" and x)^_1 --[[_6]] for x)' (2, {4,5}) == 4^2 + 5^2) + +-- error checking +assert(({pcall(function() comp 'x for __result' end)})[2] + :find'not contain __ prefix') + +-- environment. +-- Note: generated functions are set to the environment of the 'new' call. + asserteq(5,(function() + local env = {d = 5} + local comp = comp.new(env) + return comp 'sum(d for x)' {1} + end)()); +print 'DONE' diff --git a/Data/Libraries/Penlight/tests/test-config.lua b/Data/Libraries/Penlight/tests/test-config.lua new file mode 100644 index 0000000..13fc1ac --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-config.lua @@ -0,0 +1,320 @@ +local config = require 'pl.config' +local stringio = require 'pl.stringio' +asserteq = require 'pl.test'.asserteq + +function testconfig(test,tbl,cfg) + local f = stringio.open(test) + local c = config.read(f,cfg) + f:close() + if not tbl then + print(pretty.write(c)) + else + asserteq(c,tbl) + end +end + +testconfig ([[ + ; comment 2 (an ini file) +[section!] +bonzo.dog=20,30 +config_parm=here we go again +depth = 2 +[another] +felix="cat" +]],{ + section_ = { + bonzo_dog = { -- comma-sep values get split by default + 20, + 30 + }, + depth = 2, + config_parm = "here we go again" + }, + another = { + felix = "\"cat\"" + } +}) + + +testconfig ([[ +# this is a more Unix-y config file +fred = 1 +alice = 2 +home.dog = /bonzo/dog/etc +]],{ + home_dog = "/bonzo/dog/etc", -- note the default is {variablilize = true} + fred = 1, + alice = 2 +}) + +-- backspace line continuation works, thanks to config.lines function +testconfig ([[ +foo=frodo,a,c,d, \ + frank, alice, boyo +]], +{ + foo = { + "frodo", + "a", + "c", + "d", + "frank", + "alice", + "boyo" + } +} +) + +------ options to control default behaviour ----- + +-- want to keep key names as is! +testconfig ([[ +alpha.dog=10 +# comment here +]],{ + ["alpha.dog"]=10 +},{variabilize=false}) + +-- don't convert strings to numbers +testconfig ([[ +alpha.dog=10 +; comment here +]],{ + alpha_dog="10" +},{convert_numbers=false}) + +-- convert strings to booleans +testconfig ([[ +alpha.dog=false +alpha.cat=true +; comment here +]],{ + alpha_dog=false, + alpha_cat=true +},{convert_boolean=true}) + +-- don't split comma-lists by setting the list delimiter to something else +testconfig ([[ +extra=10,'hello',42 +]],{ + extra="10,'hello',42" +},{list_delim='@'}) + +-- Unix-style password file +testconfig([[ +lp:x:7:7:lp:/var/spool/lpd:/bin/sh +mail:x:8:8:mail:/var/mail:/bin/sh +news:x:9:9:news:/var/spool/news:/bin/sh +]], +{ + { + "lp", + "x", + 7, + 7, + "lp", + "/var/spool/lpd", + "/bin/sh" + }, + { + "mail", + "x", + 8, + 8, + "mail", + "/var/mail", + "/bin/sh" + }, + { + "news", + "x", + 9, + 9, + "news", + "/var/spool/news", + "/bin/sh" + } +}, +{list_delim=':'}) + +-- Unix updatedb.conf is in shell script form, but config.read +-- copes by extracting the variables as keys and the export +-- commands as the array part; there is an option to remove quotes +-- from values +testconfig([[ +# Global options for invocations of find(1) +FINDOPTIONS='-ignore_readdir_race' +export FINDOPTIONS +]],{ + "export FINDOPTIONS", + FINDOPTIONS = "-ignore_readdir_race" +},{trim_quotes=true}) + +-- Unix fstab format. No key/value assignments so use `ignore_assign`; +-- list values are separated by a number of spaces +testconfig([[ +# <file system> <mount point> <type> <options> <dump> <pass> +proc /proc proc defaults 0 0 +/dev/sda1 / ext3 defaults,errors=remount-ro 0 1 +]], +{ + { + "proc", + "/proc", + "proc", + "defaults", + 0, + 0 + }, + { + "/dev/sda1", + "/", + "ext3", + "defaults,errors=remount-ro", + 0, + 1 + } +}, +{list_delim='%s+',ignore_assign=true} +) + +-- Linux procfs 'files' often use ':' as the key/pair separator; +-- a custom convert_numbers handles the units properly! +-- Here is the first two lines from /proc/meminfo +testconfig([[ +MemTotal: 1024748 kB +MemFree: 220292 kB +]], +{ MemTotal = 1024748, MemFree = 220292 }, +{ + keysep = ':', + convert_numbers = function(s) + s = s:gsub(' kB$','') + return tonumber(s) + end + } +) + +-- altho this works, rather use pl.data.read for this kind of purpose. +testconfig ([[ +# this is just a set of comma-separated values +1000,444,222 +44,555,224 +]],{ + { + 1000, + 444, + 222 + }, + { + 44, + 555, + 224 + } +}) + +--- new with 1.0.3: smart configuration file reading +-- handles a number of common Unix file formats automatically + +function smart(f) + f = stringio.open(f) + return config.read(f,{smart=true}) +end + +-- /etc/fstab +asserteq (smart[[ +# /etc/fstab: static file system information. +# +# Use 'blkid -o value -s UUID' to print the universally unique identifier +# for a device; this may be used with UUID= as a more robust way to name +# devices that works even if disks are added and removed. See fstab(5). +# +# <file system> <mount point> <type> <options> <dump> <pass> +proc /proc proc nodev,noexec,nosuid 0 0 +/dev/sdb2 / ext2 errors=remount-ro 0 1 +/dev/fd0 /media/floppy0 auto rw,user,noauto,exec,utf8 0 0 +]],{ + proc = { + "/proc", + "proc", + "nodev,noexec,nosuid", + 0, + 0 + }, + ["/dev/sdb2"] = { + "/", + "ext2", + "errors=remount-ro", + 0, + 1 + }, + ["/dev/fd0"] = { + "/media/floppy0", + "auto", + "rw,user,noauto,exec,utf8", + 0, + 0 + } +}) + +-- /proc/XXXX/status +asserteq (smart[[ +Name: bash +State: S (sleeping) +Tgid: 30071 +Pid: 30071 +PPid: 1587 +TracerPid: 0 +Uid: 1000 1000 1000 1000 +Gid: 1000 1000 1000 1000 +FDSize: 256 +Groups: 4 20 24 46 105 119 122 1000 +VmPeak: 6780 kB +VmSize: 6716 kB +]],{ + Pid = 30071, + VmSize = 6716, + PPid = 1587, + Tgid = 30071, + State = "S (sleeping)", + Uid = "1000 1000 1000 1000", + Name = "bash", + Gid = "1000 1000 1000 1000", + Groups = "4 20 24 46 105 119 122 1000", + FDSize = 256, + VmPeak = 6780, + TracerPid = 0 +}) + +-- ssh_config +asserteq (smart[[ +Host * +# ForwardAgent no +# ForwardX11 no +# Tunnel no +# TunnelDevice any:any +# PermitLocalCommand no +# VisualHostKey no + SendEnv LANG LC_* + HashKnownHosts yes + GSSAPIAuthentication yes + GSSAPIDelegateCredentials no +]],{ + Host = "*", + GSSAPIAuthentication = "yes", + SendEnv = "LANG LC_*", + HashKnownHosts = "yes", + GSSAPIDelegateCredentials = "no" +}) + +-- updatedb.conf +asserteq (smart[[ +PRUNE_BIND_MOUNTS="yes" +# PRUNENAMES=".git .bzr .hg .svn" +PRUNEPATHS="/tmp /var/spool /media" +PRUNEFS="NFS nfs nfs4 rpc_pipefs afs binfmt_misc proc smbfs autofs iso9660 ncpfs coda devpts ftpfs devfs mfs shfs sysfs cifs lustre_lite tmpfs usbfs udf fuse.glusterfs fuse.sshfs ecryptfs fusesmb devtmpfs" +]],{ + PRUNEPATHS = "/tmp /var/spool /media", + PRUNE_BIND_MOUNTS = "yes", + PRUNEFS = "NFS nfs nfs4 rpc_pipefs afs binfmt_misc proc smbfs autofs iso9660 ncpfs coda devpts ftpfs devfs mfs shfs sysfs cifs lustre_lite tmpfs usbfs udf fuse.glusterfs fuse.sshfs ecryptfs fusesmb devtmpfs" +}) diff --git a/Data/Libraries/Penlight/tests/test-data.lua b/Data/Libraries/Penlight/tests/test-data.lua new file mode 100644 index 0000000..95eeba1 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-data.lua @@ -0,0 +1,245 @@ +local data = require 'pl.data' +local List = require 'pl.List' +local array = require 'pl.array2d' +local func = require 'pl.func' +local seq = require 'pl.seq' +local stringio = require 'pl.stringio' +local open = stringio. open +local asserteq = require 'pl.test' . asserteq +local T = require 'pl.test'. tuple + +--[=[ +dat,err = data.read(open [[ +1.0 0.1 +0.2 1.3 +]]) + +if err then print(err) end + +require 'pl.pretty'.dump(dat) +os.exit(0) +--]=] + +-- tab-separated data, explicit column names +local t1f = open [[ +EventID Magnitude LocationX LocationY LocationZ LocationError EventDate DataFile +981124001 2.0 18988.4 10047.1 4149.7 33.8 24/11/1998 11:18:05 981124DF.AAB +981125001 0.8 19104.0 9970.4 5088.7 3.0 25/11/1998 05:44:54 981125DF.AAB +981127003 0.5 19012.5 9946.9 3831.2 46.0 27/11/1998 17:15:17 981127DF.AAD +981127005 0.6 18676.4 10606.2 3761.9 4.4 27/11/1998 17:46:36 981127DF.AAF +981127006 0.2 19109.9 9716.5 3612.0 11.8 27/11/1998 19:29:51 981127DF.AAG +]] + +local t1 = data.read (t1f) +-- column_by_name returns a List +asserteq(t1:column_by_name 'Magnitude',List{2,0.8,0.5,0.6,0.2}) +-- can use array.column as well +asserteq(array.column(t1,2),{2,0.8,0.5,0.6,0.2}) + +-- only numerical columns (deduced from first data row) are converted by default +-- can look up indices in the list fieldnames. +local EDI = t1.fieldnames:index 'EventDate' +assert(type(t1[1][EDI]) == 'string') + +-- select method returns a sequence, in this case single-valued. +-- (Note that seq.copy returns a List) +asserteq(seq(t1:select 'LocationX where Magnitude > 0.5'):copy(),List{18988.4,19104,18676.4}) + +--[[ +--a common select usage pattern: +for event,mag in t1:select 'EventID,Magnitude sort by Magnitude desc' do + print(event,mag) +end +--]] + +-- space-separated, but with last field containing spaces. +local t2f = open [[ +USER PID %MEM %CPU COMMAND +sdonovan 2333 0.3 0.1 background --n=2 +root 2332 0.4 0.2 fred --start=yes +root 2338 0.2 0.1 backyard-process +]] + +local t2,err = data.read(t2f,{last_field_collect=true}) +if not t2 then return print (err) end + +-- the last_field_collect option is useful with space-delimited data where the last +-- field may contain spaces. Otherwise, a record count mismatch should be an error! +local lt2 = List(t2[2]) +asserteq(lt2:join ',','root,2332,0.4,0.2,fred --start=yes') + +-- fieldnames are converted into valid identifiers by substituting _ +-- (we do this to make select queries parseable by Lua) +asserteq(t2.fieldnames,List{'USER','PID','_MEM','_CPU','COMMAND'}) + +-- select queries are NOT SQL so remember to use == ! (and no 'between' operator, sorry) +--s,err = t2:select('_MEM where USER="root"') +--assert(err == [[[string "tmp"]:9: unexpected symbol near '=']]) + +local s = t2:select('_MEM where USER=="root"') +assert(s() == 0.4) +assert(s() == 0.2) +assert(s() == nil) + +-- CSV, Excel style. Double-quoted fields are allowed, and they may contain commas! +local t3f = open [[ +"Department Name","Employee ID",Project,"Hours Booked" +sales,1231,overhead,4 +sales,1255,overhead,3 +engineering,1501,development,5 +engineering,1501,maintenance,3 +engineering,1433,maintenance,10 +]] + +local t3 = data.read(t3f,{csv=true}) + +-- although fieldnames are turned in valid Lua identifiers, there is always `original_fieldnames` +asserteq(t3.fieldnames,List{'Department_Name','Employee_ID','Project','Hours_Booked'}) +asserteq(t3.original_fieldnames,List{'Department Name','Employee ID','Project','Hours Booked'}) + +-- a common operation is to select using a given list of columns, and each row +-- on some explicit condition. The select() method can take a table with these +-- parameters +local keepcols = {'Employee_ID','Hours_Booked'} + +local q = t3:select { fields = keepcols, + where = function(row) return row[1]=='engineering' end + } + +asserteq(seq.copy2(q),{{1501,5},{1501,3},{1433,10}}) + +-- another pattern is doing a select to restrict rows & columns, process some +-- fields and write out the modified rows. + +local outf = stringio.create() + +local names = {[1501]='don',[1433]='dilbert'} + +t3:write_row (outf,{'Employee','Hours_Booked'}) +q = t3:select_row {fields=keepcols,where=func.Eq(func._1[1],'engineering')} +for row in q do + row[1] = names[row[1]] + t3:write_row(outf,row) +end + +asserteq(outf:value(), +[[ +Employee,Hours_Booked +don,5 +don,3 +dilbert,10 +]]) + +-- data may not always have column headers. When creating a data object +-- from a two-dimensional array, may specify the fieldnames, as a list or a string. +-- The delimiter is deduced from the fieldname string, so a string just containing +-- the delimiter will set it, and the fieldnames will be empty. +local dat = List() +local row = List.range(1,10) +for i = 1,10 do + dat:append(row:map('*',i)) +end +dat = data.new(dat,',') +local out = stringio.create() +dat:write(out,',') +asserteq(out:value(), [[ +1,2,3,4,5,6,7,8,9,10 +2,4,6,8,10,12,14,16,18,20 +3,6,9,12,15,18,21,24,27,30 +4,8,12,16,20,24,28,32,36,40 +5,10,15,20,25,30,35,40,45,50 +6,12,18,24,30,36,42,48,54,60 +7,14,21,28,35,42,49,56,63,70 +8,16,24,32,40,48,56,64,72,80 +9,18,27,36,45,54,63,72,81,90 +10,20,30,40,50,60,70,80,90,100 +]]) + +-- you can always use numerical field indices, AWK-style; +-- note how the copy_select method gives you a data object instead of an +-- iterator over the fields +local res = dat:copy_select '$1,$3 where $1 > 5' +local L = List +asserteq(L(res),L{ + L{6, 18}, + L{7,21}, + L{8,24}, + L{9,27}, + L{10,30}, +}) + +-- the column_by_name method may take a fieldname or an index +asserteq(dat:column_by_name(2), L{2,4,6,8,10,12,14,16,18,20}) + +-- the field list may contain expressions or even constants +local q = dat:select '$3,2*$4 where $1 == 8' +asserteq(T(q()),T(24,64)) + +dat,err = data.read(open [[ +1.0 0.1 +0.2 1.3 +]]) + +if err then print(err) end + +-- if a method cannot be found, then we look up in array2d +-- array2d.flatten(t) makes a 1D list out of a 2D array, +-- and then List.minmax() gets the extrema. + +asserteq(T(dat:flatten():minmax()),T(0.1,1.3)) + +local f = open [[ +Time Message +1266840760 +# EE7C0600006F0D00C00F06010302054000000308010A00002B00407B00 +1266840760 closure data 0.000000 1972 1972 0 +1266840760 ++ 1266840760 EE 1 +1266840760 +# EE7C0600006F0D00C00F06010302054000000408020A00002B00407B00 +1266840764 closure data 0.000000 1972 1972 0 +1266840764 ++ 1266840764 EE 1 +1266840764 +# EE7C0600006F0D00C00F06010302054000000508030A00002B00407B00 +1266840768 duplicate? +1266840768 +# EE7C0600006F0D00C00F06010302054000000508030A00002B00407B00 +1266840768 closure data 0.000000 1972 1972 0 +]] + +-- the `convert` option provides custom converters for each specified column. +-- Here we convert the timestamps into Date objects and collect everything +-- else into one field +local Date = require 'pl.Date' + +local function date_convert (ds) + return Date(tonumber(ds)) +end + +local d = data.read(f,{convert={[1]=date_convert},last_field_collect=true}) + +asserteq(#d[1],2) +asserteq(d[2][1]:year(),2010) + +d = {{1,2,3},{10,20,30}} +out = stringio.create() +data.write(d,out,{'A','B','C'},',') +asserteq(out:value(), +[[ +A,B,C +1,2,3 +10,20,30 +]]) + +out = stringio.create() +d.fieldnames = {'A','B','C'} +data.write(d,out) + +asserteq(out:value(), +[[ +A B C +1 2 3 +10 20 30 +]]) + + +d = data.read(stringio.open 'One,Two\n1,\n,20\n',{csv=true}) +asserteq(d,{ + {1,0},{0,20}, + original_fieldnames={"One","Two"},fieldnames={"One","Two"},delim="," +}) diff --git a/Data/Libraries/Penlight/tests/test-data2.lua b/Data/Libraries/Penlight/tests/test-data2.lua new file mode 100644 index 0000000..e246ebc --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-data2.lua @@ -0,0 +1,23 @@ +local utils = require 'pl.utils' +local stringio = require 'pl.stringio' +local data = require 'pl.data' +local test = require 'pl.test' + +utils.on_error 'quit' + +stuff = [[ +Department Name,Employee ID,Project,Hours Booked +sales, 1231,overhead,4 +sales,1255,overhead,3 +engineering,1501,development,5 +engineering,1501,maintenance,3 +engineering,1433,maintenance,10 +]] + +t = data.read(stringio.open(stuff)) + +q = t:select 'Employee_ID,Hours_Booked where Department_Name == "engineering"' + +test.asserteq2(1501,5,q()) +test.asserteq2(1501,3,q()) +test.asserteq2(1433,10,q()) diff --git a/Data/Libraries/Penlight/tests/test-date.lua b/Data/Libraries/Penlight/tests/test-date.lua new file mode 100644 index 0000000..6a91a09 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-date.lua @@ -0,0 +1,87 @@ +local test = require 'pl.test' +local asserteq, assertmatch = test.asserteq, test.assertmatch +local dump = require 'pl.pretty'.dump +local T = require 'pl.test'.tuple + +local Date = require 'pl.Date' + +iso = Date.Format 'yyyy-mm-dd' -- ISO date +d = iso:parse '2010-04-10' +asserteq(T(d:day(),d:month(),d:year()),T(10,4,2010)) +amer = Date.Format 'mm/dd/yyyy' -- American style +s = amer:tostring(d) +dc = amer:parse(s) +asserteq(d,dc) + +d = Date() -- today +d:add { day = 1 } -- tomorrow +assert(d > Date()) + +--------- Time intervals ----- +-- new explicit Date.Interval class; also returned by Date:diff +d1 = Date.Interval(1202) +d2 = Date.Interval(1500) +asserteq(tostring(d2:diff(d1)),"4 min 58 sec ") + +-------- testing 'flexible' date parsing --------- + + +local df = Date.Format() + +function parse_date (s) + return df:parse(s) +end + +-- ISO 8601 +-- specified as UTC plus/minus offset + +function parse_utc (s) + local d = parse_date(s) + return d:toUTC() +end + +asserteq(parse_utc '2010-05-10 12:35:23Z', Date(2010,05,10,12,35,23)) +asserteq(parse_utc '2008-10-03T14:30+02', Date(2008,10,03,12,30)) +asserteq(parse_utc '2008-10-03T14:00-02:00',Date(2008,10,03,16,0)) + +---- can't do anything before 1970, which is somewhat unfortunate.... +--parse_date '20/03/59' + +asserteq(parse_date '15:30', Date {hour=15,min=30}) +asserteq(parse_date '8.05pm', Date {hour=20,min=5}) +asserteq(parse_date '28/10/02', Date {year=2002,month=10,day=28}) +asserteq(parse_date ' 5 Feb 2012 ', Date {year=2012,month=2,day=5}) +asserteq(parse_date '20 Jul ', Date {month=7,day=20}) +asserteq(parse_date '05/04/02 15:30:43', Date{year=2002,month=4,day=5,hour=15,min=30,sec=43}) +asserteq(parse_date 'march', Date {month=3}) +asserteq(parse_date '2010-05-23T0130', Date{year=2010,month=5,day=23,hour=1,min=30}) +asserteq(parse_date '2008-10-03T14:30:45', Date{year=2008,month=10,day=3,hour=14,min=30,sec=45}) + +-- allow for a comma after the month... +asserteq(parse_date '18 July, 2013 12:00:00', Date{year=2013,month=07,day=18,hour=12,min=0,sec=0}) + +-- This ISO format must result in a UTC date +local d = parse_date '2016-05-01T14:30:00Z' +asserteq(d:year(),2016) +asserteq(d:month(),5) +asserteq(d:day(),1) +asserteq(d:hour(),14) +asserteq(d:min(),30) +asserteq(d:sec(),0) + +function err (status,e) + return e +end + +assertmatch(err(parse_date('2005-10-40 01:30')),'40 is not between 1 and 31') +assertmatch(err(parse_date('14.20pm')),'14 is not between 0 and 12') + +local d = parse_date '2007-08-10' +-- '+' works like add, but can also work with intervals +local nxt = d + {month=1} +-- '-' is an alias for diff method +asserteq(tostring(nxt - d), '1 month ') + +--- Can explicitly get UTC date; these of course refer to same time +local now,utc = Date(), Date 'utc' +asserteq(tostring(now - utc),'zero') diff --git a/Data/Libraries/Penlight/tests/test-dir.lua b/Data/Libraries/Penlight/tests/test-dir.lua new file mode 100644 index 0000000..b293abc --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-dir.lua @@ -0,0 +1,201 @@ +-- This test file expects to be ran from 'run.lua' in the root Penlight directory. + +local dir = require( "pl.dir" ) +local file = require( "pl.file" ) +local path = require( "pl.path" ) +local asserteq = require( "pl.test" ).asserteq +local lfs = require("lfs") + +asserteq(dir.fnmatch("foobar", "foo*bar"), true) +asserteq(dir.fnmatch("afoobar", "foo*bar"), false) +asserteq(dir.fnmatch("foobars", "foo*bar"), false) +asserteq(dir.fnmatch("foonbar", "foo*bar"), true) +asserteq(dir.fnmatch("foo'n'bar", "foo*bar"), true) +asserteq(dir.fnmatch("foonbar", "foo?bar"), true) +asserteq(dir.fnmatch("foo'n'bar", "foo?bar"), false) +asserteq(dir.fnmatch("foo", "FOO"), path.is_windows) +asserteq(dir.fnmatch("FOO", "foo"), path.is_windows) + +local filtered = dir.filter({"foobar", "afoobar", "foobars", "foonbar"}, "foo*bar") +asserteq(filtered, {"foobar", "foonbar"}) + +local normpath = path.normpath + +local doc_files = dir.getfiles(normpath "docs/", "*.css") +asserteq(doc_files, {normpath "docs/ldoc_fixed.css"}) + +local all_doc_files = dir.getallfiles(normpath "docs/", "*.css") +asserteq(all_doc_files, {normpath "docs/ldoc_fixed.css"}) + +local test_samples = dir.getallfiles(normpath "tests/lua") +table.sort(test_samples) +asserteq(test_samples, { + normpath "tests/lua/animal.lua", + normpath "tests/lua/bar.lua", + normpath "tests/lua/foo/args.lua", + normpath "tests/lua/mod52.lua", + normpath "tests/lua/mymod.lua" +}) + +-- Test move files ----------------------------------------- + +-- Create a dummy file +local fileName = path.tmpname() .. "Xx" +file.write( fileName, string.rep( "poot ", 1000 ) ) + +local newFileName = path.tmpname() .. "Xx" +local err, msg = dir.movefile( fileName, newFileName ) + +-- Make sure the move is successful +assert( err, msg ) + +-- Check to make sure the original file is gone +asserteq( path.exists( fileName ), false ) + +-- Check to make sure the new file is there +asserteq( path.exists( newFileName ) , newFileName ) + +-- Test existence again, but explicitly check for correct casing +local files = dir.getfiles(path.dirname(newFileName)) +local found = false +for i, filename in ipairs(files) do + if filename == newFileName then + found = true + break + end +end +assert(found, "file was not found in directory, check casing: " .. newFileName) + + +-- Try to move the original file again (which should fail) +local newFileName2 = path.tmpname() +local err, msg = dir.movefile( fileName, newFileName2 ) +asserteq( err, false ) + +-- Clean up +file.delete( newFileName ) + + +-- Test copy files ----------------------------------------- + +-- Create a dummy file +local fileName = path.tmpname() +file.write( fileName, string.rep( "poot ", 1000 ) ) + +local newFileName = path.tmpname() .. "xX" +local err, msg = dir.copyfile( fileName, newFileName ) + +-- Make sure the move is successful +assert( err, msg ) + +-- Check to make sure the new file is there +asserteq( path.exists( newFileName ) , newFileName ) + +-- Test existence again, but explicitly check for correct casing +local files = dir.getfiles(path.dirname(newFileName)) +local found = false +for i, filename in ipairs(files) do + if filename == newFileName then + found = true + break + end +end +assert(found, "file was not found in directory, check casing: " .. newFileName) + + +-- Try to move a non-existant file (which should fail) +local fileName2 = 'blub' +local newFileName2 = 'snortsh' +local err, msg = dir.copyfile( fileName2, newFileName2 ) +asserteq( err, false ) + +-- Clean up the files +file.delete( fileName ) +file.delete( newFileName ) + + + +-- Test make directory ----------------------------------------- + +-- Create a dummy file +local dirName = path.tmpname() .. "xX" +local fullPath = dirName .. "/and/one/more" +if path.is_windows then + fullPath = fullPath:gsub("/", "\\") +end +local err, msg = dir.makepath(fullPath) + +-- Make sure the move is successful +assert( err, msg ) + +-- Check to make sure the new file is there +assert(path.isdir(dirName)) +assert(path.isdir(fullPath)) + +-- Test existence again, but explicitly check for correct casing +local files = dir.getdirectories(path.dirname(path.tmpname())) +local found = false +for i, filename in ipairs(files) do + if filename == dirName then + found = true + break + end +end +assert(found, "dir was not found in directory, check casing: " .. newFileName) + + +-- Try to move a non-existant file (which should fail) +local fileName2 = 'blub' +local newFileName2 = 'snortsh' +local err, msg = dir.copyfile( fileName2, newFileName2 ) +asserteq( err, false ) + +-- Clean up the files +file.delete( fileName ) +file.delete( newFileName ) + + + + +-- Test rmtree ----------------------------------------- +do + local dirName = path.tmpname() + os.remove(dirName) + assert(dir.makepath(dirName)) + assert(file.write(path.normpath(dirName .. "/file_base.txt"), "hello world")) + assert(dir.makepath(path.normpath(dirName .. "/sub1"))) + assert(file.write(path.normpath(dirName .. "/sub1/file_sub1.txt"), "hello world")) + assert(dir.makepath(path.normpath(dirName .. "/sub2"))) + assert(file.write(path.normpath(dirName .. "/sub2/file_sub2.txt"), "hello world")) + + + local linkTarget = path.tmpname() + os.remove(linkTarget) + assert(dir.makepath(linkTarget)) + local linkFile = path.normpath(linkTarget .. "/file.txt") + assert(file.write(linkFile, "hello world")) + + local linkSource = path.normpath(dirName .. "/link1") + assert(lfs.link(linkTarget, linkSource, true)) + + -- test: rmtree will not follow symlinks + local ok, err = dir.rmtree(linkSource) + asserteq(ok, false) + asserteq(err, "will not follow symlink") + + -- test: rmtree removes a tree without following symlinks in that tree + local ok, err = dir.rmtree(dirName) + asserteq(err, nil) + asserteq(ok, true) + + asserteq(path.exists(dirName), false) -- tree is gone, including symlink + assert(path.exists(linkFile), "expected linked-to file to still exist") -- symlink target file is still there + + -- cleanup + assert(dir.rmtree(linkTarget)) +end + + +-- have NO idea why forcing the return code is necessary here (Windows 7 64-bit) +os.exit(0) + diff --git a/Data/Libraries/Penlight/tests/test-func.lua b/Data/Libraries/Penlight/tests/test-func.lua new file mode 100644 index 0000000..4e43f30 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-func.lua @@ -0,0 +1,112 @@ +local utils = require 'pl.utils' +local List = require 'pl.List' +local tablex = require 'pl.tablex' +asserteq = require('pl.test').asserteq +utils.import('pl.func') + + -- _DEBUG = true + +function pprint (t) + print(pretty.write(t)) +end + +function test (e) + local v = {} + print('test',collect_values(e,v)) + if #v > 0 then pprint(v) end + local rep = repr(e) + print(rep) +end + +function teste (e,rs,ve) + local v = {} + collect_values(e,v) + if #v > 0 then asserteq(v,ve,nil,1) end + local rep = repr(e) + asserteq(rep,rs, nil, 1) +end + +teste(_1+_2('hello'),'_1 + _2(_C1)',{"hello"}) +teste(_1:method(),'_1[_C1](_1)',{"method"}) +teste(Not(_1),'not _1') + +asserteq(instantiate(_1+_2)(10,20),30) +asserteq(instantiate(_1+20)(10),30) +asserteq(instantiate(Or(Not(_1),_2))(true,true),true) + +teste(_1() + _2() + _3(),'_1() + _2() + _3()',30) +asserteq(I(_1+_2)(10,20),30) + +teste(_1() - -_2() % _3(), '_1() - - _2() % _3()') +teste((_1() - -_2()) % _3(), '(_1() - - _2()) % _3()') + +teste(_1() - _2() + _3(), '_1() - _2() + _3()') +teste(_1() - (_2() + _3()), '_1() - (_2() + _3())') +teste((_1() - _2()) + _3(), '_1() - _2() + _3()') + +teste(_1() .. _2() .. _3(), '_1() .. _2() .. _3()') +teste(_1() .. (_2() .. _3()), '_1() .. _2() .. _3()') +teste((_1() .. _2()) .. _3(), '(_1() .. _2()) .. _3()') + +teste(_1() ^ _2() ^ _3(), '_1() ^ _2() ^ _3()') +teste(_1() ^ (_2() ^ _3()), '_1() ^ _2() ^ _3()') +teste((_1() ^ _2()) ^ _3(), '(_1() ^ _2()) ^ _3()') + +teste(-_1() * _2(), '- _1() * _2()') +teste(-(_1() * _2()), '- (_1() * _2())') +teste((-_1()) * _2(), '- _1() * _2()') +teste(-_1() ^ _2(), '- _1() ^ _2()') +teste(-(_1() ^ _2()), '- _1() ^ _2()') +teste((-_1()) ^ _2(), '(- _1()) ^ _2()') + +asserteq(instantiate(_1+_2)(10,20),30) + +ls = List {1,2,3,4} +res = ls:map(10*_1 - 1) +asserteq(res,List {9,19,29,39}) + +-- note that relational operators can't be overloaded for _different_ types +ls = List {10,20,30,40} +asserteq(ls:filter(Gt(_1,20)),List {30,40}) + + +local map,map2 = tablex.map,tablex.map2 + +--~ test(Len(_1)) + +-- methods can be applied to all items in a table with map +asserteq (map(_1:sub(1,2),{'one','four'}),{'on','fo'}) + +--~ -- or you can do this using List:map +asserteq( List({'one','four'}):map(_1:sub(1,2)), List{'on','fo'}) + +--~ -- note that Len can't be represented generally by #, since this can only be overriden by userdata +asserteq( map(Len(_1),{'one','four'}), {3,4} ) + +--~ -- simularly, 'and' and 'or' are not really operators in Lua, so we need a function notation for them +asserteq (map2(Or(_1,_2),{false,'b'},{'.lua',false}),{'.lua','b'}) + +--~ -- binary operators: + - * / % ^ .. +asserteq (map2(_1.._2,{'a','b'},{'.lua','.c'}),{'a.lua','b.c'}) + +t1 = {alice=23,fred=34} +t2 = {bob=25,fred=34} + +intersection = bind(tablex.merge,_1,_2,false) + +asserteq(intersection(t1,t2),{fred=34}) + +union = bind(tablex.merge,_1,_2,true) + +asserteq(union(t1,t2),{bob=25,fred=34,alice=23}) + +asserteq(repr(_1+_2),"_1 + _2") + + + + + + + + + diff --git a/Data/Libraries/Penlight/tests/test-import_into.lua b/Data/Libraries/Penlight/tests/test-import_into.lua new file mode 100644 index 0000000..df65e8a --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-import_into.lua @@ -0,0 +1,39 @@ +local test = require 'pl.test' +local utils = require 'pl.utils' + +require 'pl.app'.require_here 'lua' + +if not utils.lua51 then + --- look at lua/mod52.lua + local m = require 'mod52' + test.asserteq(m.answer(),'{"10","20","30"}') + assert(m.utils) -- !! implementation is leaky! + + -- that's a bugger. However, if 'pl.import_into' is passed true, + -- then the returned module will _only_ contain the newly defined functions + -- So reload after setting the global STRICT + package.loaded.mod52 = nil + STRICT = true + m = require 'mod52' + assert (m.answer) -- as before + assert (not m.utils) -- cool! No underwear showing +end + +local pl = require 'pl.import_into' () + +assert(pl.utils) +assert(pl.tablex) +assert(pl.data) +assert(not _G.utils) +assert(not _G.tablex) +assert(not _G.data) + +require 'pl.import_into'(_G) +assert(_G.utils) +assert(_G.tablex) +assert(_G.data) + +require 'pl.import_into'(_G) +assert(_G.utils) +assert(_G.tablex) +assert(_G.data) diff --git a/Data/Libraries/Penlight/tests/test-lapp.lua b/Data/Libraries/Penlight/tests/test-lapp.lua new file mode 100644 index 0000000..6c0c7f9 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-lapp.lua @@ -0,0 +1,202 @@ + +local test = require 'pl.test' +local lapp = require 'pl.lapp' +local utils = require 'pl.utils' +local tablex = require 'pl.tablex' +local path = require 'pl.path' +local normpath = path.normpath + +local k = 1 +function check (spec,args,match) + local args = lapp(spec,args) + for k,v in pairs(args) do + if type(v) == 'userdata' then args[k]:close(); args[k] = '<file>' end + end + test.asserteq(args,match,nil,1) +end + +-- force Lapp to throw an error, rather than just calling os.exit() +lapp.show_usage_error = 'throw' + +function check_error(spec,args,msg) + arg = args + local ok,err = pcall(lapp,spec) + test.assertmatch(err,msg) +end + +local parmtest = [[ +Testing 'array' parameter handling + -o,--output... (string) + -v... +]] + + +check (parmtest,{'-o','one'},{output={'one'},v={false}}) +check (parmtest,{'-o','one','-v'},{output={'one'},v={true}}) +check (parmtest,{'-o','one','-vv'},{output={'one'},v={true,true}}) +check (parmtest,{'-o','one','-o','two'},{output={'one','two'},v={false}}) + + +local simple = [[ +Various flags and option types + -p A simple optional flag, defaults to false + -q,--quiet A simple flag with long name + -o (string) A required option with argument + <input> (default stdin) Optional input file parameter... +]] + +check(simple, + {'-o','in'}, + {quiet=false,p=false,o='in',input='<file>'}) + +---- value of flag may be separated by '=' or ':' +check(simple, + {'-o=in'}, + {quiet=false,p=false,o='in',input='<file>'}) + +check(simple, + {'-o:in'}, + {quiet=false,p=false,o='in',input='<file>'}) + +-- Check lapp.callback. +local calls = {} +function lapp.callback(param, arg) + table.insert(calls, {param, arg}) +end +check(simple, + {'-o','help','-q',normpath 'tests/test-lapp.lua'}, + {quiet=true,p=false,o='help',input='<file>',input_name=normpath 'tests/test-lapp.lua'}) +test.asserteq(calls, { + {'o', 'help'}, + {'quiet', '-q'}, + {'input', normpath 'tests/test-lapp.lua'} +}) +lapp.callback = nil + +local longs = [[ + --open (string) +]] + +check(longs,{'--open','folder'},{open='folder'}) + +local long_file = [[ + --open (default stdin) +]] + +check(long_file,{'--open',normpath 'tests/test-lapp.lua'},{open='<file>',open_name=normpath 'tests/test-lapp.lua'}) + +local extras1 = [[ + <files...> (string) A bunch of files +]] + +check(extras1,{'one','two'},{files={'one','two'}}) + +-- any extra parameters go into the array part of the result +local extras2 = [[ + <file> (string) A file +]] + +check(extras2,{'one','two'},{file='one','two'}) + +local extended = [[ + --foo (string default 1) + -s,--speed (slow|medium|fast default medium) + -n (1..10 default 1) + -p print + -v verbose +]] + + +check(extended,{},{foo='1',speed='medium',n=1,p=false,v=false}) +check(extended,{'-pv'},{foo='1',speed='medium',n=1,p=true,v=true}) +check(extended,{'--foo','2','-s','fast'},{foo='2',speed='fast',n=1,p=false,v=false}) +check(extended,{'--foo=2','-s=fast','-n2'},{foo='2',speed='fast',n=2,p=false,v=false}) + +check_error(extended,{'--speed','massive'},"value 'massive' not in slow|medium|fast") + +check_error(extended,{'-n','x'},"unable to convert to number: x") + +check_error(extended,{'-n','12'},"n out of range") + +local with_dashes = [[ + --first-dash dash + --second-dash dash also +]] + +check(with_dashes,{'--first-dash'},{first_dash=true,second_dash=false}) + +-- optional parameters don't have to be set +local optional = [[ + -p (optional string) +]] + +check(optional,{'-p', 'test'},{p='test'}) +check(optional,{},{}) + +-- boolean flags may have a true default... +local false_flag = [[ + -g group results + -f (default true) force result +]] + +check (false_flag,{},{f=true,g=false}) + +check (false_flag,{'-g','-f'},{f=false,g=true}) + +-- '--' indicates end of parameter parsing +check (false_flag,{'-g','--'},{f=true,g=true}) +check (false_flag,{'-g','--','-a','frodo'},{f=true,g=true; '-a','frodo'}) + +local addtype = [[ + -l (intlist) List of items +]] + +-- defining a custom type +lapp.add_type('intlist', + function(x) + return tablex.imap(tonumber, utils.split(x, '%s*,%s*')) + end, + function(x) + for _,v in ipairs(x) do + lapp.assert(math.ceil(v) == v,'not an integer!') + end + end) + +check(addtype,{'-l', '1,2,3'},{l={1,2,3}}) + +check_error(addtype,{'-l', '1.5,2,3'},"not an integer!") + +-- short flags may be immediately followed by their value +-- (previously only true for numerical values) +local short_args = [[ + -n (default 10) + -I,--include (string) +]] + +check(short_args,{'-Ifrodo','-n5'},{include='frodo',n=5}) +check(short_args,{'-I/usr/local/lua/5.1'},{include='/usr/local/lua/5.1',n=10}) + +-- ok, introducing _slack_ mode ;) +-- 'short' flags may have multiple characters! (this is otherwise an error) +-- Note that in _any case_ flags may contain hyphens, but these are turned +-- into underscores for convenience. +lapp.slack = true +local spec = [[ +Does some calculations + -vs,--video-set (string) Use the German road sign dataset + -w,--width (default 256) Width of the video + -h,--height (default 144) Height of the video + -t,--time (default 10) Seconds of video to process + -sk,--seek (default 0) Seek number of seconds + -dbg Debug! +]] + +test.asserteq(lapp(spec,{'-vs',200,'-sk',1}),{ + video_set = 200, + time = 10, + height = 144, + seek = 1, + dbg = false, + width = 256 +}) + diff --git a/Data/Libraries/Penlight/tests/test-lexer.lua b/Data/Libraries/Penlight/tests/test-lexer.lua new file mode 100644 index 0000000..1c09b85 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-lexer.lua @@ -0,0 +1,146 @@ +local asserteq = require('pl.test').asserteq +local lexer = require 'pl.lexer' +local seq = require 'pl.seq' +local List = require('pl.List') +local open = require('pl.stringio').open +local copy2 = seq.copy2 + +local function test_scan(str, filter, options, expected_tokens, lang) + local matches + if lang then + matches, filter = filter, options + else + lang = 'scan' + end + + asserteq(copy2(lexer[lang](str, matches, filter, options)), expected_tokens) + if lang == 'scan' then + asserteq(copy2(lexer[lang](open(str), matches, filter, options)), expected_tokens) + end +end + +local s = '20 = hello' +test_scan(s, {space=false}, {number=false}, { + {'number', '20'}, {'space', ' '}, {'=', '='}, {'space', ' '}, {'iden', 'hello'} +}) +test_scan(s, {space=true}, {number=true}, { + {'number', 20}, {'=', '='}, {'iden', 'hello'} +}) +s = [[ 'help' "help" "dolly you're fine" "a \"quote\" here"]] +test_scan(s, nil, nil, { + {'string', 'help'}, {'string', 'help'}, + {'string', "dolly you're fine"}, {'string', 'a \\\"quote\\\" here'} -- Escapes are preserved literally. +}) +test_scan([[\abc\]], nil, nil, { + {'\\', '\\'}, {'iden', 'abc'}, {'\\', '\\'} +}) +test_scan([["" ""]], nil, nil, { + {'string', ''}, {'string', ''} +}) +test_scan([["abc" "def\\"]], nil, nil, { + {'string', 'abc'}, {'string', 'def\\\\'} +}) +test_scan([["abc\\" "def"]], nil, nil, { + {'string', 'abc\\\\'}, {'string', 'def'} +}) +test_scan([["abc\\\" "]], nil, nil, { + {'string', 'abc\\\\\\" '} +}) + +local function test_roundtrip(str) + test_scan(str, {}, {string=false}, {{'string', str}}) +end + +test_roundtrip [["hello\\"]] +test_roundtrip [["hello\"dolly"]] +test_roundtrip [['hello\'dolly']] +test_roundtrip [['']] +test_roundtrip [[""]] + +test_scan('test(20 and a > b)', nil, nil, { + {'iden', 'test'}, {'(', '('}, {'number', 20}, {'keyword', 'and'}, + {'iden', 'a'}, {'>', '>'}, {'iden', 'b'}, {')', ')'} +}, 'lua') +test_scan('10+2.3', nil, nil, { + {'number', 10}, {'+', '+'}, {'number', 2.3} +}, 'lua') + +local txt = [==[ +-- comment +--[[ +block +comment +]][[ +hello dammit +]][[hello]] +]==] + +test_scan(txt, {}, nil, { + {'comment', '-- comment\n'}, + {'comment', '--[[\nblock\ncomment\n]]'}, + {'string', 'hello dammit\n'}, + {'string', 'hello'}, + {'space', '\n'} +}, 'lua') + +local lines = [[ +for k,v in pairs(t) do + if type(k) == 'number' then + print(v) -- array-like case + else + print(k,v) + end -- if +end +]] + +local ls = List() +for tp,val in lexer.lua(lines,{space=true,comments=true}) do + assert(tp ~= 'space' and tp ~= 'comment') + if tp == 'keyword' then ls:append(val) end +end +asserteq(ls,List{'for','in','do','if','then','else','end','end'}) + +txt = [[ +// comment +/* a long +set of words */ // more +]] + +test_scan(txt, {}, nil, { + {'comment', '// comment\n'}, + {'comment', '/* a long\nset of words */'}, + {'space', ' '}, + {'comment', '// more\n'} +}, 'cpp') + +test_scan([['' "" " \\" '\'' "'"]], nil, nil, { + {'char', ''}, -- Char literals with no or more than one characters are not a lexing error. + {'string', ''}, + {'string', ' \\\\'}, + {'char', "\\'"}, + {'string', "'"} +}, 'cpp') + +local iter = lexer.lua([[ +foo +bar +]]) + +asserteq(lexer.lineno(iter), 0) +iter() +asserteq(lexer.lineno(iter), 1) +asserteq(lexer.lineno(iter), 1) +iter() +asserteq(lexer.lineno(iter), 2) +iter() +asserteq(lexer.lineno(iter), 3) +iter() +iter() +asserteq(lexer.lineno(iter), 3) + +do -- numbers without leading zero; ".123" + local s = 'hello = +.234' + test_scan(s, {space=true}, {number=true}, { + {'iden', 'hello'}, {'=', '='}, {'number', .234} + }) +end diff --git a/Data/Libraries/Penlight/tests/test-list.lua b/Data/Libraries/Penlight/tests/test-list.lua new file mode 100644 index 0000000..4e290d6 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-list.lua @@ -0,0 +1,68 @@ +local List = require 'pl.List' +local class = require 'pl.class' +local test = require 'pl.test' +local asserteq, T = test.asserteq, test.tuple + +-- note that a _plain table_ is made directly into a list +local t = {10,20,30} +local ls = List(t) +asserteq(t,ls) + +asserteq(List({}):reverse(), {}) +asserteq(List({1}):reverse(), {1}) +asserteq(List({1,2}):reverse(), {2,1}) +asserteq(List({1,2,3}):reverse(), {3,2,1}) +asserteq(List({1,2,3,4}):reverse(), {4,3,2,1}) + +-- you may derive classes from pl.List, and the result is covariant. +-- That is, slice() etc will return a list of the derived type, not List. + +local NA = class(List) + +local function mapm(a1,op,a2) + local M = type(a2)=='table' and List.map2 or List.map + return M(a1,op,a2) +end + +--- elementwise arithmetric operations +function NA.__unm(a) return a:map '|X|-X' end +function NA.__pow(a,s) return a:map '|X,Y|X^Y' end +function NA.__add(a1,a2) return mapm(a1,'|X,Y|X+Y',a2) end +function NA.__sub(a1,a2) return mapm(a1,'|X,Y|X-Y',a2) end +function NA.__div(a1,a2) return mapm(a1,'|X,Y|X/Y',a2) end +function NA.__mul(a1,a2) return mapm(a2,'|X,Y|X*Y',a1) end + +function NA:minmax () + local min,max = math.huge,-math.huge + for i = 1,#self do + local val = self[i] + if val > max then max = val end + if val < min then min = val end + end + return min,max +end + +function NA:sum () + local res = 0 + for i = 1,#self do + res = res + self[i] + end + return res +end + +function NA:normalize () + return self:transform('|X,Y|X/Y',self:sum()) +end + +n1 = NA{10,20,30} +n2 = NA{1,2,3} +ns = n1 + 2*n2 + +asserteq(List:class_of(ns),true) +asserteq(NA:class_of(ns),true) +asserteq(ns:is_a(NA),true) +asserteq(ns,{12,24,36}) +min,max = ns:slice(1,2):minmax() +asserteq(T(min,max),T(12,24)) + +asserteq(n1:normalize():sum(),1,1e-8) diff --git a/Data/Libraries/Penlight/tests/test-list2.lua b/Data/Libraries/Penlight/tests/test-list2.lua new file mode 100644 index 0000000..174b1c7 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-list2.lua @@ -0,0 +1,60 @@ +local List = require 'pl.List' +local asserteq = require 'pl.test' . asserteq + +local s = List{1,2,3,4,5} + +-- test using: lua pylist.lua +local lst = List() +lst:append(20) +lst:extend{30,40,50} +lst:put(10) +asserteq (lst,List{10,20,30,40,50}) +asserteq (lst:len(),5) +lst:insert(3,11) +lst:remove_value(40) +asserteq (lst,List{10,20,11,30,50}) +asserteq (lst:contains(11),true) +asserteq (lst:contains(40),false) +local _ = lst:pop() +asserteq( lst:index(30),4 ) +asserteq( lst:count(10),1 ) +lst:sort() +lst:reverse() +asserteq (lst , List{30,20,11,10}) +asserteq (lst[#lst] , 10) +asserteq (lst[#lst-2] , 20) +asserteq (tostring(lst) , '{30,20,11,10}') + +lst = List {10,20,30,40,50} +asserteq (lst:slice(2),{20,30,40,50}) +asserteq (lst:slice(-2),{40,50}) +asserteq (lst:slice(nil,3),{10,20,30}) +asserteq (lst:slice(2,4),{20,30,40}) +asserteq (lst:slice(-4,-2),{20,30,40}) + +lst = List.range(0,9) +local seq = List{0,1,2,3,4,5,6,7,8,9} +asserteq(List.range(4),{1,2,3,4}) +asserteq(List.range(0,8,2),{0,2,4,6,8}) +asserteq(List.range(0,1,0.2),{0,0.2,0.4,0.6,0.8,1},1e-9) +asserteq(lst, seq) +asserteq(lst:reduce '+', 45) + +local part = seq:partition(function(v) return v % 2 end) +asserteq (part[0], List{0,2,4,6,8}) +asserteq (part[1], List{1,3,5,7,9}) + +asserteq (List('abcd'),List{'a','b','c','d'}) +local caps = List() +List('abcd'):foreach(function(v) caps:append(v:upper()) end) +asserteq (caps,List{'A','B','C','D'}) +local ls = List{10,20,30,40} +ls:slice_assign(2,3,{21,31}) +asserteq (ls , List{10,21,31,40}) +asserteq (ls:remove(2), List{10,31,40}) +asserteq (ls:clear(), List{}) +asserteq (ls:len(), 0) + +s = 'here the dog is just a dog' +assert (List.split(s) == List{'here', 'the', 'dog', 'is', 'just', 'a', 'dog'}) +assert (List.split('foo;bar;baz', ';') == List{'foo', 'bar', 'baz'}) diff --git a/Data/Libraries/Penlight/tests/test-map.lua b/Data/Libraries/Penlight/tests/test-map.lua new file mode 100644 index 0000000..1c0bee6 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-map.lua @@ -0,0 +1,134 @@ +-- testing Map functionality + +local test = require 'pl.test' +local Map = require 'pl.Map' +local tablex = require 'pl.tablex' +local Set = require 'pl.Set' +local utils = require 'pl.utils' + +local asserteq = test.asserteq +local cmp = tablex.compare_no_order + + + +-- construction, plain +local m = Map{alpha=1,beta=2,gamma=3} + +assert(cmp( + m:values(), + {1, 2, 3} +)) + +assert(cmp( + m:keys(), + {'alpha', 'beta', 'gamma'} +)) + +asserteq( + m:items(), + { + {'alpha', 1}, + {'beta', 2}, + {'gamma', 3}, + } +) + +asserteq (m:getvalues {'alpha','gamma'}, {1,3}) + + + +-- construction, from a set +local s = Set{'red','orange','green','blue'} +m = Map(s) + +asserteq( + m:items(), + { + {'blue', true}, + {'green', true}, + {'orange', true}, + {'red', true}, + } +) + + +-- iter() +m = Map{alpha=1,beta=2,gamma=3} +local t = {alpha=1,beta=2,gamma=3} +for k,v in m:iter() do + asserteq(v, t[k]) + t[k] = nil +end +assert(next(t) == nil, "expected the table to be empty by now") + + + +-- setdefault() +m = Map{alpha=1,beta=2,gamma=3} +local v = m:setdefault("charlie", 4) +asserteq(v, 4) +v = m:setdefault("alpha", 10) +asserteq(v, 1) +asserteq( + m:items(), + { + {'alpha', 1}, + {'beta', 2}, + {'charlie', 4}, + {'gamma', 3}, + } +) +v = m:set("alpha", false) +v = m:setdefault("alpha", true) -- falsy value should not be altered +asserteq(false, m:get("alpha")) + + + +-- len() +m = Map{alpha=1,beta=2,gamma=3} +asserteq(3, m:len()) +m = Map{} +asserteq(0, m:len()) +m:set("charlie", 4) +asserteq(1, m:len()) + + + +-- set() & get() +m = Map{} +m:set("charlie", 4) +asserteq(4, m:get("charlie")) +m:set("charlie", 5) +asserteq(5, m:get("charlie")) +m:set("charlie", nil) +asserteq(nil, m:get("charlie")) + + + +-- getvalues() +m = Map{alpha=1,beta=2,gamma=3} +local x = m:getvalues{"gamma", "beta"} +asserteq({3, 2}, x) + + + +-- __eq() -- equality +local m1 = Map{alpha=1,beta=2,gamma=3} +local m2 = Map{alpha=1,beta=2,gamma=3} +assert(m1 == m2) +m1 = Map() +m2 = Map() +assert(m1 == m2) + + + +-- __tostring() +m = Map() +asserteq("{}", tostring(m)) +m = Map{alpha=1} +asserteq("{alpha=1}", tostring(m)) +m = Map{alpha=1,beta=2} +assert(({ -- test 2 versions, since we cannot rely on order + ["{alpha=1,beta=2}"] = true, + ["{beta=2,alpha=1}"] = true, + })[tostring(m)]) diff --git a/Data/Libraries/Penlight/tests/test-orderedmap.lua b/Data/Libraries/Penlight/tests/test-orderedmap.lua new file mode 100644 index 0000000..1d0fc67 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-orderedmap.lua @@ -0,0 +1,98 @@ +local List = require 'pl.List' + +local asserteq = require 'pl.test' . asserteq +local asserteq2 = require 'pl.test' . asserteq2 +local OrderedMap = require 'pl.OrderedMap' + + +m = OrderedMap() +m:set('one',1) +m:set('two',2) +m:set('three',3) + +asserteq(m:values(),List{1,2,3}) + +-- usually exercized like this: +--for k,v in m:iter() do print(k,v) end + +local fn = m:iter() +asserteq2 ('one',1,fn()) +asserteq2 ('two',2,fn()) +asserteq2 ('three',3,fn()) + +-- Keys overriding methods can be used. +m:set('set', 4) +asserteq(m:values(),List{1,2,3,4}) + +local o1 = OrderedMap {{z=2},{beta=1},{name='fred'}} +asserteq(tostring(o1),'{z=2,beta=1,name="fred"}') + +-- order of keys is not preserved here! +local o2 = OrderedMap {z=4,beta=1.1,name='alice',extra='dolly'} + +o1:update(o2) +asserteq(tostring(o1),'{z=4,beta=1.1,name="alice",extra="dolly"}') + +o1:set('beta',nil) +asserteq(o1,OrderedMap{{z=4},{name='alice'},{extra='dolly'}}) + +local o3 = OrderedMap() +o3:set('dog',10) +o3:set('cat',20) +o3:set('mouse',30) + +asserteq(o3:keys(),{'dog','cat','mouse'}) + +o3:set('dog',nil) + +asserteq(o3:keys(),{'cat','mouse'}) + +-- Vadim found a problem when clearing a key which did not exist already. +-- The keys list would then contain the key, although the map would not +o3:set('lizard',nil) + +asserteq(o3:keys(),{'cat','mouse'}) +asserteq(o3:values(), {20,30}) +asserteq(tostring(o3),'{cat=20,mouse=30}') + +-- copy constructor +local o4 = OrderedMap(o3) + +asserteq(o4,o3) + +-- constructor throws an error if the argument is bad +-- (errors same as OrderedMap:update) +asserteq(false,pcall(function() + m = OrderedMap('string') +end)) + +---- changing order of key/value pairs ---- + +o3 = OrderedMap{{cat=20},{mouse=30}} + +o3:insert(1,'bird',5) -- adds key/value before specified position +o3:insert(1,'mouse') -- moves key keeping old value +asserteq(o3:keys(),{'mouse','bird','cat'}) +asserteq(tostring(o3),'{mouse=30,bird=5,cat=20}') +o3:insert(2,'cat',21) -- moves key and sets new value +asserteq(tostring(o3),'{mouse=30,cat=21,bird=5}') +-- if you don't specify a value for an unknown key, nothing happens to the map +o3:insert(3,'alligator') +asserteq(tostring(o3),'{mouse=30,cat=21,bird=5}') + +---- short-cut notation + +local o5 = OrderedMap() +o5.alpha = 1 +o5.beta = 2 +o5.gamma = 3 + +asserteq(o5,OrderedMap{{alpha=1},{beta=2},{gamma=3}}) + +o5.alpha = 10 +o5.beta = 20 +o5.gamma = 30 +o5.delta = 40 +o5.checked = false + +asserteq(o5,OrderedMap{{alpha=10},{beta=20},{gamma=30},{delta=40},{checked=false}}) diff --git a/Data/Libraries/Penlight/tests/test-path.lua b/Data/Libraries/Penlight/tests/test-path.lua new file mode 100644 index 0000000..3cdee68 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-path.lua @@ -0,0 +1,198 @@ +local path = require 'pl.path' +asserteq = require 'pl.test'.asserteq + +function quote(s) + return '"'..s..'"' +end + +function print2(s1,s2) + print(quote(s1),quote(s2)) +end + +function slash (p) + return (p:gsub('\\','/')) +end + +-- path.currentdir +do + local cp = path.currentdir() + path.chdir("docs") + asserteq(path.currentdir(), cp .. path.sep .. "docs") + path.chdir("..") + asserteq(path.currentdir(), cp) +end + +-- path.isdir +asserteq( path.isdir( "docs" ), true ) +asserteq( path.isdir( "docs/index.html" ), false ) + +-- path.isfile +asserteq( path.isfile( "docs" ), false ) +asserteq( path.isfile( "docs/index.html" ), true ) + +-- path.exists +asserteq( path.exists( "docs"), "docs") +asserteq( path.exists( "docs/index.html"), "docs/index.html") + + +do -- path.splitpath & path.splitext + function testpath(pth,p1,p2,p3) + local dir,rest = path.splitpath(pth) + local name,ext = path.splitext(rest) + asserteq(dir,p1) + asserteq(name,p2) + asserteq(ext,p3) + end + + testpath ([[/bonzo/dog_stuff/cat.txt]],[[/bonzo/dog_stuff]],'cat','.txt') + testpath ([[/bonzo/dog/cat/fred.stuff]],'/bonzo/dog/cat','fred','.stuff') + testpath ([[../../alice/jones]],'../../alice','jones','') + testpath ([[alice]],'','alice','') + testpath ([[/path-to/dog/]],[[/path-to/dog]],'','') + + asserteq({path.splitpath("some/dir/myfile.txt")}, {"some/dir", "myfile.txt"}) + asserteq({path.splitpath("some/dir/")}, {"some/dir", ""}) + asserteq({path.splitpath("some_dir")}, {"", "some_dir"}) + + asserteq({path.splitext("/bonzo/dog_stuff/cat.txt")}, {"/bonzo/dog_stuff/cat", ".txt"}) + asserteq({path.splitext("cat.txt")}, {"cat", ".txt"}) + asserteq({path.splitext("cat")}, {"cat", ""}) + asserteq({path.splitext(".txt")}, {"", ".txt"}) + asserteq({path.splitext("")}, {"", ""}) +end + + +-- TODO: path.abspath + +-- TODO: path.dirname + +-- TODO: path.basename + +-- TODO: path.extension + + +do -- path.isabs + asserteq(path.isabs("/hello/path"), true) + asserteq(path.isabs("hello/path"), false) + asserteq(path.isabs("./hello/path"), false) + asserteq(path.isabs("../hello/path"), false) + if path.is_windows then + asserteq(path.isabs("c:/"), true) + asserteq(path.isabs("c:/hello/path"), true) + asserteq(path.isabs("c:"), false) + asserteq(path.isabs("c:hello/path"), false) + asserteq(path.isabs("c:./hello/path"), false) + asserteq(path.isabs("c:../hello/path"), false) + end +end + + +do -- path.join + assert(path.join("somepath",".") == "somepath"..path.sep..".") + assert(path.join(".","readme.txt") == "."..path.sep.."readme.txt") + assert(path.join("/a_dir", "abs_path/") == "/a_dir"..path.sep.."abs_path/") + assert(path.join("a_dir", "/abs_path/") == "/abs_path/") + assert(path.join("a_dir", "/abs_path/", "/abs_path2/") == "/abs_path2/") + assert(path.join("a_dir", "/abs_path/", "not_abs_path2/") == "/abs_path/not_abs_path2/") + assert(path.join("a_dir", "/abs_path/", "not_abs_path2/", "/abs_path3/", "not_abs_path4/") == "/abs_path3/not_abs_path4/") + assert(path.join("first","second","third") == "first"..path.sep.."second"..path.sep.."third") + assert(path.join("first","second","") == "first"..path.sep.."second"..path.sep) + assert(path.join("first","","third") == "first"..path.sep.."third") + assert(path.join("","second","third") == "second"..path.sep.."third") + assert(path.join("","") == "") +end + + +do -- path.normcase + if path.iswindows then + asserteq('c:\\hello\\world', 'c:\\hello\\world') + asserteq('C:\\Hello\\wORLD', 'c:\\hello\\world') + asserteq('c:/hello/world', 'c:\\hello\\world') + else + asserteq('/Hello/wORLD', '/Hello/wORLD') + end +end + + +do -- path.normpath + local norm = path.normpath + local p = norm '/a/b' + + asserteq(norm '/a/fred/../b',p) + asserteq(norm '/a//b',p) + + function testnorm(p1,p2) + asserteq(norm(p1):gsub('\\','/'), p2) + end + + testnorm('a/b/..','a') + testnorm('a/b/../..','.') + testnorm('a/b/../c/../../d','d') + testnorm('a/.','a') + testnorm('a/./','a') + testnorm('a/b/.././..','.') + testnorm('../../a/b','../../a/b') + testnorm('../../a/b/../../','../..') + testnorm('../../a/b/../c','../../a/c') + testnorm('./../../a/b/../c','../../a/c') + testnorm('a/..b', 'a/..b') + testnorm('./a', 'a') + testnorm('a/.', 'a') + testnorm('a/', 'a') + testnorm('/a', '/a') + testnorm('', ".") + + if path.is_windows then + testnorm('C://a', 'C:/a') + testnorm('C:/../a', 'C:/../a') + asserteq(norm [[\a\.\b]], p) + -- UNC paths + asserteq(norm [[\\bonzo\..\dog]], [[\\dog]]) + asserteq(norm [[\\?\c:\bonzo\dog\.\]], [[\\?\c:\bonzo\dog]]) + else + testnorm('//a', '//a') + testnorm('///a', '/a') + end + + asserteq(norm '1/2/../3/4/../5',norm '1/3/5') + asserteq(norm '1/hello/../3/hello/../HELLO',norm '1/3/HELLO') +end + + +do -- path.relpath + local testpath = '/a/B/c' + + function try (p,r) + asserteq(slash(path.relpath(p,testpath)),r) + end + + try('/a/B/c/one.lua','one.lua') + try('/a/B/c/bonZO/two.lua','bonZO/two.lua') + try('/a/B/three.lua','../three.lua') + try('/a/four.lua','../../four.lua') + try('one.lua','one.lua') + try('../two.lua','../two.lua') +end + + +-- TODO: path.expanduser + +-- TODO: path.tmpname + + +do -- path.common_prefix + asserteq(slash(path.common_prefix("../anything","../anything/goes")),"../anything") + asserteq(slash(path.common_prefix("../anything/goes","../anything")),"../anything") + asserteq(slash(path.common_prefix("../anything/goes","../anything/goes")),"../anything") + asserteq(slash(path.common_prefix("../anything/","../anything/")),"../anything") + asserteq(slash(path.common_prefix("../anything","../anything")),"..") + asserteq(slash(path.common_prefix("/hello/world","/hello/world/filename.doc")),"/hello/world") + asserteq(slash(path.common_prefix("/hello/filename.doc","/hello/filename.doc")),"/hello") + if path.is_windows then + asserteq(path.common_prefix("c:\\hey\\there","c:\\hey"),"c:\\hey") + asserteq(path.common_prefix("c:/HEy/there","c:/hEy"),"c:\\hEy") -- normalized separators, original casing + end +end + + +-- TODO: path.package_path diff --git a/Data/Libraries/Penlight/tests/test-pretty.lua b/Data/Libraries/Penlight/tests/test-pretty.lua new file mode 100644 index 0000000..99058e3 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-pretty.lua @@ -0,0 +1,117 @@ +local pretty = require 'pl.pretty' +local utils = require 'pl.utils' +local test = require 'pl.test' +local asserteq, assertmatch = test.asserteq, test.assertmatch + +t1 = { + 'one','two','three',{1,2,3}, + alpha=1,beta=2,gamma=3,['&']=true,[0]=false, + _fred = {true,true}, + s = [[ +hello dolly +you're so fine +]] +} + +s = pretty.write(t1) --,' ',true) +t2,err = pretty.read(s) +if err then return print(err) end +asserteq(t1,t2) + +res,err = pretty.read [[ + { + ['function'] = true, + ['do'] = true, + } +]] +assert(res) + +res,err = pretty.read [[ + { + ['function'] = true, + ['do'] = "no function here...", + } +]] +assert(res) + +res,err = pretty.read [[ + { + ['function'] = true, + ['do'] = function() return end + } +]] +assertmatch(err,'cannot have functions in table definition') + +res,err = pretty.load([[ +-- comments are ok +a = 2 +bonzo = 'dog' +t = {1,2,3} +]]) + +asserteq(res,{a=2,bonzo='dog',t={1,2,3}}) + +--- another potential problem is string functions called implicitly as methods-- +res,err = pretty.read [[ +{s = ('woo'):gsub('w','wwwwww'):gsub('w','wwwwww')} +]] + +assertmatch(err,(_VERSION ~= "Lua 5.2") and 'attempt to index a string value' or "attempt to index constant 'woo'") + +---- pretty.load has a _paranoid_ option +res,err = pretty.load([[ +k = 0 +for i = 1,1e12 do k = k + 1 end +]],{},true) + +assertmatch(err,'looping not allowed') + +-- Check to make sure that no spaces exist when write is told not to +local tbl = { "a", 2, "c", false, 23, 453, "poot", 34 } +asserteq( pretty.write( tbl, "" ), [[{"a",2,"c",false,23,453,"poot",34}]] ) + +-- Check that write correctly prevents cycles + +local t1,t2 = {},{} +t1[1] = t1 +asserteq( pretty.write(t1,""), [[{<cycle>}]] ) +t1[1],t1[2],t2[1] = 42,t2,t1 +asserteq( pretty.write(t1,""), [[{42,{<cycle>}}]] ) + +-- Check false positives in write's cycles prevention + +t2 = {} +t1[1],t1[2] = t2,t2 +asserteq( pretty.write(t1,""), [[{{},{}}]] ) + +-- Check that write correctly print table with non number or string as keys + +t1 = { [true] = "boolean", a = "a", b = "b", [1] = 1, [0] = 0 } +asserteq( pretty.write(t1,""), [[{1,["true"]="boolean",a="a",b="b",[0]=0}]] ) + + +-- Check number formatting +asserteq(pretty.write({1/0, -1/0, 0/0, 1, 1/2}, ""), "{Inf,-Inf,NaN,1,0.5}") + +if _VERSION == "Lua 5.3" then + asserteq(pretty.write({1.0}, ""), "{1.0}") +else + asserteq(pretty.write({1.0}, ""), "{1}") +end + +do -- issue #203, item 3 + local t = {}; t[t] = 1 + pretty.write(t) -- should not crash +end + + +-- pretty.write fails if an __index metatable raises an error #257 +-- only applies to 5.3+ where iterators respect metamethods +do + local t = setmetatable({},{ + __index = function(self, key) + error("oops... couldn't find " .. tostring(key)) + end + }) + asserteq(pretty.write(t), "{\n}") +end diff --git a/Data/Libraries/Penlight/tests/test-seq.lua b/Data/Libraries/Penlight/tests/test-seq.lua new file mode 100644 index 0000000..e59a4be --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-seq.lua @@ -0,0 +1,223 @@ +local input = require 'pl.input' +local seq = require 'pl.seq' +local asserteq = require('pl.test').asserteq +local utils = require 'pl.utils' +local stringio = require 'pl.stringio' +local unpack = utils.unpack + +local L = utils.string_lambda +local S = seq.list +local C = seq.copy +local C2 = seq.copy2 + + +asserteq (seq.sum(input.numbers '10 20 30 40 50'),150) +local x,y = unpack(C(input.numbers('10 20'))) +assert (x == 10 and y == 20) + + +local test = {{1,10},{2,20},{3,30}} +asserteq(C2(ipairs{10,20,30}),test) +local res = C2(input.fields({1,2},',','1,10\n2,20\n3,30\n')) +asserteq(res,test) + +asserteq( + seq.copy(seq.filter(seq.list{10,20,5,15},seq.greater_than(10))), + {20,15} +) + +asserteq( + seq.copy(seq.filter(seq.list{10,20,5,15},seq.less_than(15))), + {10,5} +) + +asserteq( + #C(seq.filter(seq.list{10,20,5,10,15},seq.equal_to(10))), + 2 +) + +asserteq( + #seq{'green','yellow','red','blue','red'}:filter(seq.equal_to'red'):copy(), + 2 +) + +asserteq( + seq{'apple','orange','pineapple'}:filter(seq.matching'apple'):copy(), + {'apple','pineapple'} +) + +asserteq( + C(seq.sort(seq.keys{[11] = true, [17]= true, [23] = true})), + {11,17,23} +) + +asserteq( + C(seq.range(2,5)), + {2,3,4,5} +) + +asserteq(seq.reduce('-',{1,2,3,4,5}),-13) + +asserteq(seq.count(S{10,20,30,40},L'|x| x > 20'), 2) + +asserteq(C2(seq.zip({1,2,3},{10,20,30})),test) + +asserteq(C(seq.splice({10,20},{30,40})),{10,20,30,40}) + +asserteq(C(seq.map(L'#_',{'one','tw'})),{3,2}) + +--for l1,l2 in seq.last{10,20,30} do print(l1,l2) end + +asserteq( C2(seq.last{10,20,30}),{{20,10},{30,20}} ) + +asserteq( C2(seq.last{40}),{} ) + +asserteq( C2(seq.last{}),{} ) + +asserteq( + seq{10,20,30}:map(L'_+1'):copy(), + {11,21,31} +) + +asserteq( + seq {1,2,3,4,5}:reduce ('*'), 120 +) + +-- test reduce with an initial value +asserteq( + seq {1,2,3,4,5}:reduce ('+', 42), 57 +) + +-- test reduce with a short sequence +asserteq( + seq {7}:reduce ('+'), 7 +) + +asserteq( + seq {5}:reduce ('/', 40), 8 +) + +asserteq( + seq {}:reduce ('+', 42), 42 +) + +asserteq( + seq {}:reduce ('-'), nil +) + +asserteq( + seq{'one','two'}:upper():copy(), + {'ONE','TWO'} +) + +asserteq( + seq{'one','two','three'}:skip(1):copy(), + {'two','three'} +) + +-- test skipping pass sequence +asserteq( + seq{'one','two','three'}:skip(4):copy(), + {} +) + +asserteq( + seq{7,8,9,10}:take(3):copy(), + {7,8,9} +) + +asserteq( + seq{7,8,9,10}:take(6):copy(), + {7,8,9,10} +) + +asserteq( + seq{7,8,9,10}:take(0):copy(), + {} +) + +asserteq( + seq{7,8,9,10}:take(-1):copy(), + {} +) + +local l, u = 50, 100 +local rand_seq = seq(seq.random(7, l, u)) +asserteq( + #rand_seq:filter(seq.less_than(u+1)):filter(seq.greater_than(l-1)):copy(), + 7 +) + +rand_seq = seq(seq.random(7, u)) +asserteq( + #rand_seq:filter(seq.less_than(u+1)):filter(seq.greater_than(0)):copy(), + 7 +) + +rand_seq = seq(seq.random(7)) +asserteq( + #rand_seq:filter(seq.less_than(1)):filter(seq.greater_than(0)):copy(), + 7 +) + +test = {275,127,286,590,961,687,802,453,705,182} +asserteq( + C(seq.sort{seq(test):minmax()}), + {127,961} +) + +asserteq( + seq(test):take(5):enum():copy_tuples(), + {{1,275},{2,127},{3,286},{4,590},{5,961}} +) + +asserteq( + C(seq.unique(seq.list{1,2,3,2,1})), + {1,2,3} +) + +local actualstr = {} +local expectedstr = "275.00 127.00 286.00 590.00 961.00 687.00 802.00\n".. + "453.00 705.00 182.00 \n" +local function proxywrite_printall(head, ...) + table.insert(actualstr, tostring(head)) + if select('#', ...) == 0 then return true end + return proxywrite_printall(...) +end + +local iowrite = io.write +io.write = proxywrite_printall +seq(test):printall(nil,nil,'%.2f') +io.write = iowrite +asserteq( + table.concat(actualstr), + expectedstr +) + + +local f = stringio.open '1 2 3 4' + +-- seq.lines may take format specifiers if using Lua 5.2, or a 5.2-compatible +-- file object like that returned by stringio. +asserteq( + seq.lines(f,'*n'):copy(), + {1,2,3,4} +) + +-- the seq() constructor can now take an iterator which consists of two parts, +-- a function and an object - as returned e.g. by lfs.dir() + +local function my_iter(T) + local idx = 0 + return function(self) + idx = idx + 1 + return self[idx] + end, + T +end + +asserteq( + seq(my_iter{10,20,30}):copy(), + {10,20,30} +) + diff --git a/Data/Libraries/Penlight/tests/test-sip.lua b/Data/Libraries/Penlight/tests/test-sip.lua new file mode 100644 index 0000000..cf4b344 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-sip.lua @@ -0,0 +1,105 @@ +local sip = require 'pl.sip' +local tablex = require 'pl.tablex' +local test = require 'pl.test' + +local function check(pat,line,tbl) + local parms = {} + if type(pat) == 'string' then + pat = sip.compile(pat) + end + if pat(line,parms) then + test.asserteq(parms,tbl) + else -- only should happen if we're passed a nil! + assert(tbl == nil) + end +end + +local c = sip.compile('ref=$S{file}:$d{line}') +check(c,'ref=bonzo:23',{file='bonzo',line=23}) +check(c,'here we go ref=c:\\bonzo\\dog.txt:53',{file='c:\\bonzo\\dog.txt',line=53}) +check(c,'here is a line ref=xxxx:xx',nil) + +c = sip.compile('($i{x},$i{y},$i{z})') +check(c,'(10,20,30)',{x=10,y=20,z=30}) +check(c,' (+233,+99,-40) ',{x=233,y=99,z=-40}) + +local pat = '$v{name} = $q{str}' +--assert(sip.create_pattern(pat) == [[([%a_][%w_]*)%s*=%s*(["'])(.-)%2]]) +local m = sip.compile(pat) + +check(m,'a = "hello"',{name='a',str='hello'}) +check(m,"a = 'hello'",{name='a',str='hello'}) +check(m,'_fred="some text"',{name='_fred',str='some text'}) + +-- some cases broken in 0.6b release +check('$v is $v','bonzo is dog for sure',{'bonzo','dog'}) +check('$v is $','bonzo is dog for sure',{'bonzo','dog for sure'}) + +-- spaces +check('$v $d','age 23',{'age',23}) +check('$v $d','age 23',{'age',23}) +check('$v $d','age23') -- the space is 'imcompressible' +check('a b c $r', 'a bc d') +check('a b c $r', 'a b c d',{'d'}) + +-- the spaces in this pattern, however, are compressible. +check('$v = $d','age=23',{'age',23}) + +-- patterns without patterns +check('just a string', 'just a string', {}) +check('just a string', 'not that string') + +local months={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"} + +local function adjust_year(res) + if res.year < 100 then + if res.year < 70 then + res.year = res.year + 2000 + else + res.year = res.year + 1900 + end + end +end + +local shortdate = sip.compile('$d{day}/$d{month}/$d{year}') +local longdate = sip.compile('$d{day} $v{month} $d{year}') +local isodate = sip.compile('$d{year}-$d{month}-$d{day}') + +local function dcheck (d1,d2) + adjust_year(d1) + test.asserteq(d1, d2) +end + +local function dates(str,tbl) + local res = {} + if shortdate(str,res) then + dcheck(res,tbl) + elseif isodate(str,res) then + dcheck(res,tbl) + elseif longdate(str,res) then + res.month = tablex.find(months,res.month) + dcheck(res,tbl) + else + assert(tbl == nil) + end +end + +dates ('10/12/2007',{year=2007,month=12,day=10}) +dates ('2006-03-01',{year=2006,month=3,day=1}) +dates ('25/07/05',{year=2005,month=7,day=25}) +dates ('20 Mar 1959',{year=1959,month=3,day=20}) + +local sio = require 'pl.stringio' +local lines = [[ +dodge much amazement +kitteh cheezburger +]] +sip.read(sio.open(lines),{ + {'dodge $',function(rest) test.asserteq(rest,'much amazement') end}, + {'kitteh $',function(rest) test.asserteq(rest,'cheezburger') end} +}) + + + + + diff --git a/Data/Libraries/Penlight/tests/test-strict.lua b/Data/Libraries/Penlight/tests/test-strict.lua new file mode 100644 index 0000000..12b0fad --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-strict.lua @@ -0,0 +1,145 @@ +require 'pl.compat' -- require this one before loading strict +local strict = require 'pl.strict' +local test = require 'pl.test' +local app = require 'pl.app' + +-- in strict mode, you must assign to a global first, even if just nil. +test.assertraise(function() + print(x) + print 'ok?' +end,"variable 'x' is not declared") + +-- can assign to globals in main (or from C extensions) but not anywhere else! +test.assertraise(function() + Boo = 3 +end,"assign to undeclared global 'Boo'") + +Boo = true +Boo2 = nil + +-- once declared, you can assign to globals from anywhere +(function() Boo = 42; Boo2 = 6*7 end)() + +--- a module may use strict.module() to generate a simularly strict environment +-- (see lua/mymod.lua) +app.require_here 'lua' +local M = require 'mymod' + +--- these are fine +M.answer() +M.question() + +-- spelling mistakes become errors... +test.assertraise(function() + print(M.Answer()) +end,"variable 'Answer' is not declared in 'mymod'") + +--- for the extra paranoid, you can choose to make all global tables strict... +strict.make_all_strict(_G) + +test.assertraise(function() + print(math.sine(1.2)) +end,"variable 'sine' is not declared in 'math'") + + + +-- module +do + local testmodule = { + hello = function() return "supremacy" end + } + -- make strict and allow extra field "world" + strict.module("my_test", testmodule, { world = true }) + + test.asserteq(testmodule.hello(), "supremacy") + test.assertraise(function() + print(testmodule.not_allowed_key) + end, "variable 'not_allowed_key' is not declared in 'my_test'") + + test.asserteq(testmodule.world, nil) + testmodule.world = "supremacy" + test.asserteq(testmodule.world, "supremacy") + + + -- table with a __newindex method + local mod1 = strict.module("mod1", setmetatable( + { + hello = "world", + }, { + __newindex = function(self, key, value) + if key == "Lua" then + rawset(self, key, value) + end + end, + } + )) + test.asserteq(mod1.hello, "world") + mod1.Lua = "hello world" + test.asserteq(mod1.Lua, "hello world") + test.assertraise(function() + print(mod1.not_allowed_key) + end, "variable 'not_allowed_key' is not declared in 'mod1'") + + + -- table with a __index method + local mod1 = strict.module("mod1", setmetatable( + { + hello = "world", + }, { + __index = function(self, key) + if key == "Lua" then + return "rocks" + end + end, + } + )) + test.asserteq(mod1.hello, "world") + test.asserteq(mod1.Lua, "rocks") + test.assertraise(function() + print(mod1.not_allowed_key) + end, "variable 'not_allowed_key' is not declared in 'mod1'") + + + -- table with a __index table + local mod1 = strict.module("mod1", setmetatable( + { + hello = "world", + }, { + __index = { + Lua = "rocks!" + } + } + )) + test.asserteq(mod1.hello, "world") + test.asserteq(mod1.Lua, "rocks!") + test.assertraise(function() + print(mod1.not_allowed_key) + end, "variable 'not_allowed_key' is not declared in 'mod1'") + +end + + +do + -- closed_module + -- what does this do? this does not seem a usefull function??? + + local testmodule = { + hello = function() return "supremacy" end + } + local M = strict.closed_module(testmodule, "my_test") + + -- read acces to original is granted, but not to the new one + test.asserteq(testmodule.hello(), "supremacy") + test.assertraise(function() + print(M.hello()) + end, "variable 'hello' is not declared in 'my_test'") + + -- write access to both is granted + testmodule.world = "domination" + M.world = "domination" + + -- read acces to set field in original is granted, but not set + test.asserteq(testmodule.world, nil) + test.asserteq(M.world, "domination") + +end diff --git a/Data/Libraries/Penlight/tests/test-stringio.lua b/Data/Libraries/Penlight/tests/test-stringio.lua new file mode 100644 index 0000000..93a1990 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-stringio.lua @@ -0,0 +1,72 @@ +local stringio = require 'pl.stringio' +local test = require 'pl.test' +local asserteq = test.asserteq +local T = test.tuple + +function fprintf(f,fmt,...) + f:write(fmt:format(...)) +end + +fs = stringio.create() +for i = 1,100 do + fs:write('hello','\n','dolly','\n') +end +asserteq(#fs:value(),1200) + +fs = stringio.create() +fs:writef("%s %d",'answer',42) -- note writef() extension method +asserteq(fs:value(),"answer 42") + +inf = stringio.open('10 20 30') +asserteq(T(inf:read('*n','*n','*n')),T(10,20,30)) + +local txt = [[ +Some lines +here are they +not for other +english? + +]] + +inf = stringio.open (txt) +fs = stringio.create() +for l in inf:lines() do + fs:write(l,'\n') +end +asserteq(txt,fs:value()) + +inf = stringio.open '1234567890ABCDEF' +asserteq(T(inf:read(3), inf:read(5), inf:read()),T('123','45678','90ABCDEF')) + +s = stringio.open 'one\ntwo' +asserteq(s:read() , 'one') +asserteq(s:read() , 'two') +asserteq(s:read() , nil) +s = stringio.open 'one\ntwo' +iter = s:lines() +asserteq(iter() , 'one') +asserteq(iter() , 'two') +asserteq(iter() , nil) +s = stringio.open 'ABC' +iter = s:lines(1) +asserteq(iter() , 'A') +asserteq(iter() , 'B') +asserteq(iter() , 'C') +asserteq(iter() , nil) + +s = stringio.open '20 5.2e-2 52.3' +x,y,z = s:read('*n','*n','*n') +out = stringio.create() +fprintf(out,"%5.2f %5.2f %5.2f!",x,y,z) +asserteq(out:value(),"20.00 0.05 52.30!") + +s = stringio.open 'one\ntwo\n\n' +iter = s:lines '*L' +asserteq(iter(),'one\n') +asserteq(iter(),'two\n') +asserteq(iter(),'\n') +asserteq(iter(),nil) + + + + diff --git a/Data/Libraries/Penlight/tests/test-stringx.lua b/Data/Libraries/Penlight/tests/test-stringx.lua new file mode 100644 index 0000000..20ebe17 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-stringx.lua @@ -0,0 +1,381 @@ +local stringx = require 'pl.stringx' +local utils = require 'pl.utils' +local asserteq = require 'pl.test' . asserteq +local T = require 'pl.test'.tuple + +local function FIX(s) + io.stderr:write('FIX:' .. s .. '\n') +end + + +-- isalpha +asserteq(T(stringx.isalpha''), T(false)) +asserteq(T(stringx.isalpha' '), T(false)) +asserteq(T(stringx.isalpha'0'), T(false)) +asserteq(T(stringx.isalpha'\0'), T(false)) +asserteq(T(stringx.isalpha'azAZ'), T(true)) +asserteq(T(stringx.isalpha'az9AZ'), T(false)) + +-- isdigit +asserteq(T(stringx.isdigit''), T(false)) +asserteq(T(stringx.isdigit' '), T(false)) +asserteq(T(stringx.isdigit'a'), T(false)) +asserteq(T(stringx.isdigit'0123456789'), T(true)) + +-- isalnum +asserteq(T(stringx.isalnum''), T(false)) +asserteq(T(stringx.isalnum' '), T(false)) +asserteq(T(stringx.isalnum('azAZ01234567890')), T(true)) + +-- isspace +asserteq(T(stringx.isspace''), T(false)) +asserteq(T(stringx.isspace' '), T(true)) +asserteq(T(stringx.isspace' \r\n\f\t'), T(true)) +asserteq(T(stringx.isspace' \r\n-\f\t'), T(false)) + +-- islower +asserteq(T(stringx.islower''), T(false)) +asserteq(T(stringx.islower'az'), T(true)) +asserteq(T(stringx.islower'aMz'), T(false)) +asserteq(T(stringx.islower'a z'), T(true)) + +-- isupper +asserteq(T(stringx.isupper''), T(false)) +asserteq(T(stringx.isupper'AZ'), T(true)) +asserteq(T(stringx.isupper'AmZ'), T(false)) +asserteq(T(stringx.isupper'A Z'), T(true)) + +-- startswith +local startswith = stringx.startswith +asserteq(T(startswith('', '')), T(true)) +asserteq(T(startswith('', 'a')), T(false)) +asserteq(T(startswith('a', '')), T(true)) +asserteq(T(startswith('a', 'a')), T(true)) +asserteq(T(startswith('a', 'b')), T(false)) +asserteq(T(startswith('a', 'ab')), T(false)) +asserteq(T(startswith('abc', 'ab')), T(true)) +asserteq(T(startswith('abc', 'bc')), T(false)) -- off by one +asserteq(T(startswith('abc', '.')), T(false)) -- Lua pattern char +asserteq(T(startswith('a\0bc', 'a\0b')), T(true)) -- '\0' + +asserteq(startswith('abcfoo',{'abc','def'}),true) +asserteq(startswith('deffoo',{'abc','def'}),true) +asserteq(startswith('cdefoo',{'abc','def'}),false) + + +-- endswith +-- http://snippets.luacode.org/sputnik.lua?p=snippets/Check_string_ends_with_other_string_74 +local endswith = stringx.endswith +asserteq(T(endswith("", "")), T(true)) +asserteq(T(endswith("", "a")), T(false)) +asserteq(T(endswith("a", "")), T(true)) +asserteq(T(endswith("a", "a")), T(true)) +asserteq(T(endswith("a", "A")), T(false)) -- case sensitive +asserteq(T(endswith("a", "aa")), T(false)) +asserteq(T(endswith("abc", "")), T(true)) +asserteq(T(endswith("abc", "ab")), T(false)) -- off by one +asserteq(T(endswith("abc", "c")), T(true)) +asserteq(T(endswith("abc", "bc")), T(true)) +asserteq(T(endswith("abc", "abc")), T(true)) +asserteq(T(endswith("abc", " abc")), T(false)) +asserteq(T(endswith("abc", "a")), T(false)) +asserteq(T(endswith("abc", ".")), T(false)) -- Lua pattern char +asserteq(T(endswith("ab\0c", "b\0c")), T(true)) -- \0 +asserteq(T(endswith("ab\0c", "b\0d")), T(false)) -- \0 + +asserteq(endswith('dollar.dot',{'.dot','.txt'}),true) +asserteq(endswith('dollar.txt',{'.dot','.txt'}),true) +asserteq(endswith('dollar.rtxt',{'.dot','.txt'}),false) + +-- join +asserteq(stringx.join(' ', {1,2,3}), '1 2 3') + +-- splitlines +asserteq(stringx.splitlines(''), {}) +asserteq(stringx.splitlines('a'), {'a'}) +asserteq(stringx.splitlines('\n'), {''}) +asserteq(stringx.splitlines('\n\n'), {'', ''}) +asserteq(stringx.splitlines('\r\r'), {'', ''}) +asserteq(stringx.splitlines('\r\n'), {''}) +asserteq(stringx.splitlines('ab\ncd\n'), {'ab', 'cd'}) +asserteq(stringx.splitlines('ab\ncd\n', true), {'ab\n', 'cd\n'}) +asserteq(stringx.splitlines('\nab\r\r\ncd\n', true), {'\n', 'ab\r', '\r\n', 'cd\n'}) + +-- expandtabs +---FIX[[raises error +asserteq(T(stringx.expandtabs('',0)), T('')) +asserteq(T(stringx.expandtabs('',1)), T('')) +asserteq(T(stringx.expandtabs(' ',1)), T(' ')) +-- expandtabs now works like Python's str.expandtabs (up to next tab stop) +asserteq(T(stringx.expandtabs(' \t ')), T((' '):rep(1+8))) +asserteq(T(stringx.expandtabs(' \t ',2)), T(' ')) +--]] + +-- lfind +asserteq(T(stringx.lfind('', '')), T(1)) +asserteq(T(stringx.lfind('a', '')), T(1)) +asserteq(T(stringx.lfind('ab', 'b')), T(2)) +asserteq(T(stringx.lfind('abc', 'cd')), T(nil)) +asserteq(T(stringx.lfind('abcbc', 'bc')), T(2)) +asserteq(T(stringx.lfind('ab..cd', '.')), T(3)) -- pattern char +asserteq(T(stringx.lfind('abcbcbbc', 'bc', 3)), T(4)) +asserteq(T(stringx.lfind('abcbcbbc', 'bc', 3, 4)), T(nil)) +asserteq(T(stringx.lfind('abcbcbbc', 'bc', 3, 5)), T(4)) +asserteq(T(stringx.lfind('abcbcbbc', 'bc', nil, 5)), T(2)) + +-- rfind +asserteq(T(stringx.rfind('', '')), T(1)) +asserteq(T(stringx.rfind('ab', '')), T(3)) +asserteq(T(stringx.rfind('abc', 'cd')), T(nil)) +asserteq(T(stringx.rfind('abcbc', 'bc')), T(4)) +asserteq(T(stringx.rfind('abcbcb', 'bc')), T(4)) +asserteq(T(stringx.rfind('ab..cd', '.')), T(4)) -- pattern char +asserteq(T(stringx.rfind('abcbcbbc', 'bc', 3)), T(7)) +asserteq(T(stringx.rfind('abcbcbbc', 'bc', 3, 4)), T(nil)) +asserteq(T(stringx.rfind('abcbcbbc', 'bc', 3, 5)), T(4)) +asserteq(T(stringx.rfind('abcbcbbc', 'bc', nil, 5)), T(4)) +asserteq(T(stringx.rfind('banana', 'ana')), T(4)) + +-- replace +asserteq(T(stringx.replace('', '', '')), T('')) +asserteq(T(stringx.replace(' ', '', '')), T(' ')) +asserteq(T(stringx.replace(' ', '', ' ')), T(' ')) +asserteq(T(stringx.replace(' ', ' ', '')), T('')) +asserteq(T(stringx.replace('abcabcabc', 'bc', 'BC')), T('aBCaBCaBC')) +asserteq(T(stringx.replace('abcabcabc', 'bc', 'BC', 1)), T('aBCabcabc')) +asserteq(T(stringx.replace('abcabcabc', 'bc', 'BC', 0)), T('abcabcabc')) +asserteq(T(stringx.replace('abc', 'd', 'e')), T('abc')) +asserteq(T(stringx.replace('a.b', '.', '%d')), T('a%db')) + +-- split +local split = stringx.split +asserteq(split('', ''), {''}) +asserteq(split('', 'z'), {}) --FIX:intended and specified behavior? +asserteq(split('a', ''), {'a'}) --FIX:intended and specified behavior? +asserteq(split('a', 'a'), {''}) +-- stringx.split now follows the Python pattern, so it uses a substring, not a pattern. +-- If you need to split on a pattern, use utils.split() +-- asserteq(split('ab1cd23ef%d', '%d+'), {'ab', 'cd', 'ef%d'}) -- pattern chars +-- note that leading space is ignored by the default +asserteq(split(' 1 2 3 '),{'1','2','3'}) +asserteq(split('a*bb*c*ddd','*'),{'a','bb','c','ddd'}) +asserteq(split('dog:fred:bonzo:alice',':',3), {'dog','fred','bonzo:alice'}) +asserteq(split('dog:fred:bonzo:alice:',':',3), {'dog','fred','bonzo:alice:'}) +asserteq(split('///','/'),{'','','',''}) +-- capitalize +asserteq(T(stringx.capitalize('')), T('')) +asserteq(T(stringx.capitalize('abC deF1')), T('Abc Def1')) -- Python behaviour + +-- count +asserteq(T(stringx.count('', '')), T(0)) --infinite loop]] +asserteq(T(stringx.count(' ', '')), T(2)) --infinite loop]] +asserteq(T(stringx.count('a..c', '.')), T(2)) -- pattern chars +asserteq(T(stringx.count('a1c', '%d')), T(0)) -- pattern chars +asserteq(T(stringx.count('Anna Anna Anna', 'Anna')), T(3)) -- no overlap +asserteq(T(stringx.count('banana', 'ana', false)), T(1)) -- no overlap +asserteq(T(stringx.count('banana', 'ana', true)), T(2)) -- overlap + +-- ljust +asserteq(T(stringx.ljust('', 0)), T('')) +asserteq(T(stringx.ljust('', 2)), T(' ')) +asserteq(T(stringx.ljust('ab', 3)), T('ab ')) +asserteq(T(stringx.ljust('ab', 3, '%')), T('ab%')) +asserteq(T(stringx.ljust('abcd', 3)), T('abcd')) -- agrees with Python + +-- rjust +asserteq(T(stringx.rjust('', 0)), T('')) +asserteq(T(stringx.rjust('', 2)), T(' ')) +asserteq(T(stringx.rjust('ab', 3)), T(' ab')) +asserteq(T(stringx.rjust('ab', 3, '%')), T('%ab')) +asserteq(T(stringx.rjust('abcd', 3)), T('abcd')) -- agrees with Python + +-- center +asserteq(T(stringx.center('', 0)), T('')) +asserteq(T(stringx.center('', 1)), T(' ')) +asserteq(T(stringx.center('', 2)), T(' ')) +asserteq(T(stringx.center('a', 1)), T('a')) +asserteq(T(stringx.center('a', 2)), T('a ')) +asserteq(T(stringx.center('a', 3)), T(' a ')) + + +-- ltrim +-- http://snippets.luacode.org/sputnik.lua?p=snippets/trim_whitespace_from_string_76 +local trim = stringx.lstrip +asserteq(T(trim''), T'') +asserteq(T(trim' '), T'') +asserteq(T(trim' '), T'') +asserteq(T(trim'a'), T'a') +asserteq(T(trim' a'), T'a') +asserteq(T(trim'a '), T'a ') +asserteq(T(trim' a '), T'a ') +asserteq(T(trim' a '), T'a ') +asserteq(T(trim' ab cd '), T'ab cd ') +asserteq(T(trim' \t\r\n\f\va\000b \r\t\n\f\v'), T'a\000b \r\t\n\f\v') +-- more + + +-- rtrim +-- http://snippets.luacode.org/sputnik.lua?p=snippets/trim_whitespace_from_string_76 +local trim = stringx.rstrip +asserteq(T(trim''), T'') +asserteq(T(trim' '), T'') +asserteq(T(trim' '), T'') +asserteq(T(trim'a'), T'a') +asserteq(T(trim' a'), T' a') +asserteq(T(trim'a '), T'a') +asserteq(T(trim' a '), T' a') +asserteq(T(trim' a '), T' a') +asserteq(T(trim' ab cd '), T' ab cd') +asserteq(T(trim' \t\r\n\f\va\000b \r\t\n\f\v'), T' \t\r\n\f\va\000b') +-- more + + +-- trim +-- http://snippets.luacode.org/sputnik.lua?p=snippets/trim_whitespace_from_string_76 +local trim = stringx.strip +asserteq(T(trim''), T'') +asserteq(T(trim' '), T'') +asserteq(T(trim' '), T'') +asserteq(T(trim'a'), T'a') +asserteq(T(trim' a'), T'a') +asserteq(T(trim'a '), T'a') +asserteq(T(trim' a '), T'a') +asserteq(T(trim' a '), T'a') +asserteq(T(trim' ab cd '), T'ab cd') +asserteq(T(trim' \t\r\n\f\va\000b \r\t\n\f\v'), T'a\000b') +local long = 'a' .. string.rep(' ', 200000) .. 'a' +asserteq(T(trim(long)), T(long)) +-- more + + +asserteq({stringx.splitv("hello dolly")}, {"hello", "dolly"}) + + +-- partition +-- as per str.partition in Python, delimiter must be non-empty; +-- interpreted as a plain string +--asserteq(T(stringx.partition('', '')), T('', '', '')) -- error]] +--asserteq(T(stringx.partition('a', '')), T('', '', 'a')) --error]] +asserteq(T(stringx.partition('a', 'a')), T('', 'a', '')) +asserteq(T(stringx.partition('abc', 'b')), T('a', 'b', 'c')) +asserteq(T(stringx.partition('abc', '.+')), T('abc','','')) +asserteq(T(stringx.partition('a,b,c', ',')), T('a',',','b,c')) +asserteq(T(stringx.partition('abc', '/')), T('abc', '', '')) +-- rpartition +asserteq(T(stringx.rpartition('a/b/c', '/')), T('a/b', '/', 'c')) +asserteq(T(stringx.rpartition('abc', 'b')), T('a', 'b', 'c')) +asserteq(T(stringx.rpartition('a', 'a')), T('', 'a', '')) +asserteq(T(stringx.rpartition('abc', '/')), T('', '', 'abc')) + + +-- at (works like s:sub(idx,idx), so negative indices allowed +asserteq(T(stringx.at('a', 1)), T('a')) +asserteq(T(stringx.at('ab', 2)), T('b')) +asserteq(T(stringx.at('abcd', -1)), T('d')) +asserteq(T(stringx.at('abcd', 10)), T('')) -- not found + +-- lines +local function merge(it, ...) + assert(select('#', ...) == 0) + local ts = {} + for val in it do ts[#ts+1] = val end + return ts +end +asserteq(merge(stringx.lines('')), {''}) +asserteq(merge(stringx.lines('ab')), {'ab'}) +asserteq(merge(stringx.lines('ab\ncd')), {'ab', 'cd'}) + +asserteq(stringx.capitalize("hello world"), "Hello World") +asserteq(stringx.title("hello world"), "Hello World") + +-- shorten +-- The returned string is always equal or less to the given size. +asserteq(T(stringx.shorten('', 0)), T'') +asserteq(T(stringx.shorten('a', 1)), T'a') +asserteq(T(stringx.shorten('ab', 1)), T'.') --FIX:ok? +asserteq(T(stringx.shorten('abc', 3)), T'abc') +asserteq(T(stringx.shorten('abcd', 3)), T'...') +asserteq(T(stringx.shorten('abcde', 5)), T'abcde') +asserteq(T(stringx.shorten('abcde', 4)), T'a...') +asserteq(T(stringx.shorten('abcde', 3)), T'...') +asserteq(T(stringx.shorten('abcde', 2)), T'..') +asserteq(T(stringx.shorten('abcde', 0)), T'') +asserteq(T(stringx.shorten('', 0, true)), T'') +asserteq(T(stringx.shorten('a', 1, true)), T'a') +asserteq(T(stringx.shorten('ab', 1, true)), T'.') +asserteq(T(stringx.shorten('abcde', 5, true)), T'abcde') +asserteq(T(stringx.shorten('abcde', 4, true)), T'...e') +asserteq(T(stringx.shorten('abcde', 3, true)), T'...') +asserteq(T(stringx.shorten('abcde', 2, true)), T'..') +asserteq(T(stringx.shorten('abcde', 0, true)), T'') + +-- strip +asserteq(stringx.strip(' hello '),'hello') +asserteq(stringx.strip('--[hello] -- - ','-[] '),'hello') +asserteq(stringx.rstrip('--[hello] -- - ','-[] '),'--[hello') +asserteq(stringx.strip('hello'..((" "):rep(500))), "hello") + +-- + +local assert_str_round_trip = function(s) + + local qs = stringx.quote_string(s) + local compiled, err = utils.load("return "..qs) + + if not compiled then + print( + ("stringx.quote_string assert failed: invalid string created: Received:\n%s\n\nCompiled to\n%s\n\nError:\t%s\n"): + format(s, qs, err) + ) + error() + else + compiled = compiled() + end + + if compiled ~= s then + print("stringx.quote_string assert Failed: String compiled but did not round trip.") + print("input string:\t\t",s, #s) + print("compiled string:\t", compiled, #compiled) + print("output string:\t\t",qs, #qs) + error() + else + -- print("input string:\t\t",s) + -- print("compiled string:\t", compiled) + -- print("output string:\t\t",qs) + end +end + +assert_str_round_trip( "normal string with nothing weird.") +assert_str_round_trip( "Long string quoted with escaped quote \\\" and a long string pattern match [==[ found near the end.") + +assert_str_round_trip( "Unescapped quote \" in the middle") +assert_str_round_trip( "[[Embedded long quotes \\\". Escaped must stay! ]]") +assert_str_round_trip( [[Long quoted string with a slash prior to quote \\\". ]]) +assert_str_round_trip( "[[Completely normal\n long quote. ]]") +assert_str_round_trip( "String with a newline\nending with a closing bracket]") +assert_str_round_trip( "[[String with opening brackets ending with part of a long closing bracket]=") +assert_str_round_trip( "\n[[Completely normal\n long quote. Except that we lead with a return! Tricky! ]]") +assert_str_round_trip( '"balance [======[ doesn\'t ]====] mater when searching for embedded long-string quotes.') +assert_str_round_trip( "Any\0 \t control character other than a return will be handled by the %q mechanism.") +assert_str_round_trip( "This\tincludes\ttabs.") +assert_str_round_trip( "But not returns.\n Returns are easier to see using long quotes.") +assert_str_round_trip( "The \z escape does not trigger a control pattern, however.") + +assert_str_round_trip( "[==[If a string is long-quoted, escaped \\\" quotes have to stay! ]==]") +assert_str_round_trip('"A quoted string looks like what?"') +assert_str_round_trip( "'I think that it should be quoted, anyway.'") +assert_str_round_trip( "[[Even if they're long quoted.]]") +assert_str_round_trip( "]=]==]") + +assert_str_round_trip( "\"\\\"\\' pathalogical:starts with a quote ]\"\\']=]]==][[]]]=========]") +assert_str_round_trip( "\\\"\\\"\\' pathalogical: quote is after this text with a quote ]\"\\']=]]==][[]]]=========]") +assert_str_round_trip( "\\\"\\\"\\' pathalogical: quotes are all escaped. ]\\\"\\']=]]==][[]]]=========]") +assert_str_round_trip( "") +assert_str_round_trip( " ") +assert_str_round_trip( "\n") --tricky. +assert_str_round_trip( "\r") +assert_str_round_trip( "\r\n") +assert_str_round_trip( "\r1\n") +assert_str_round_trip( "[[") +assert_str_round_trip( "''") +assert_str_round_trip( '""') diff --git a/Data/Libraries/Penlight/tests/test-stringx2.lua b/Data/Libraries/Penlight/tests/test-stringx2.lua new file mode 100644 index 0000000..c690e58 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-stringx2.lua @@ -0,0 +1,20 @@ +local asserteq = require 'pl.test' . asserteq + + +-- strings --- +require 'pl.stringx'.import() ---> convenient! +local s = '123' +assert (s:isdigit()) +assert (not s:isspace()) +s = 'here the dog is just a dog' +assert (s:startswith('here')) +assert (s:endswith('dog')) +assert (s:count('dog') == 2) +s = ' here we go ' +asserteq (s:lstrip() , 'here we go ') +asserteq (s:rstrip() , ' here we go') +asserteq (s:strip() , 'here we go') +asserteq (('hello'):center(20,'+') , '+++++++hello++++++++') + +asserteq (('hello dolly'):title() , 'Hello Dolly') +asserteq (('h bk bonzo TOK fred m'):title() , 'H Bk Bonzo Tok Fred M') diff --git a/Data/Libraries/Penlight/tests/test-tablex.lua b/Data/Libraries/Penlight/tests/test-tablex.lua new file mode 100644 index 0000000..c8bbf01 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-tablex.lua @@ -0,0 +1,348 @@ +local tablex = require 'pl.tablex' +local utils = require ('pl.utils') +local L = utils.string_lambda +local test = require('pl.test') +-- bring tablex funtions into global namespace +utils.import(tablex) +local asserteq = test.asserteq + +local cmp = deepcompare + +function asserteq_no_order (x,y) + if not compare_no_order(x,y) then + test.complain(x,y,"these lists contained different elements") + end +end + + +asserteq( + copy {10,20,30}, + {10,20,30} +) + +asserteq( + deepcopy {10,20,{30,40}}, + {10,20,{30,40}} +) + +local t = { + a = "hello", + b = { + c = "world" + } +} +t.b.d = t.b + +local tcopy = { + a = "hello", + b = { + c = "world" + } +} +tcopy.b.d = tcopy.b + +asserteq( + deepcopy(t), + tcopy +) + +asserteq( + pairmap(function(i,v) return v end,{10,20,30}), + {10,20,30} +) + +asserteq_no_order( + pairmap(L'_',{fred=10,bonzo=20}), + {'fred','bonzo'} +) + +asserteq_no_order( + pairmap(function(k,v) return v end,{fred=10,bonzo=20}), + {10,20} +) + +asserteq_no_order( + pairmap(function(i,v) return v,i end,{10,20,30}), + {10,20,30} +) + +asserteq( + pairmap(function(k,v) return {k,v},k end,{one=1,two=2}), + {one={'one',1},two={'two',2}} +) +-- same as above, using string lambdas +asserteq( + pairmap(L'|k,v|{k,v},k',{one=1,two=2}), + {one={'one',1},two={'two',2}} +) + + +asserteq( + map(function(v) return v*v end,{10,20,30}), + {100,400,900} +) + +-- extra arguments to map() are passed to the function; can use +-- the abbreviations provided by pl.operator +asserteq( + map('+',{10,20,30},1), + {11,21,31} +) + +asserteq( + map(L'_+1',{10,20,30}), + {11,21,31} +) + +-- map2 generalizes for operations on two tables +asserteq( + map2(math.max,{1,2,3},{0,4,2}), + {1,4,3} +) + +-- mapn operates over an arbitrary number of input tables (but use map2 for n=2) +asserteq( + mapn(function(x,y,z) return x+y+z end, {1,2,3},{10,20,30},{100,200,300}), + {111,222,333} +) + +asserteq( + mapn(math.max, {1,20,300},{10,2,3},{100,200,100}), + {100,200,300} +) + +asserteq( + count_map({"foo", "bar", "foo", "baz"}), + {foo = 2, bar = 1, baz = 1} +) + +asserteq( + zip({10,20,30},{100,200,300}), + {{10,100},{20,200},{30,300}} +) + +assert(compare_no_order({1,2,3,4},{2,1,4,3})==true) +assert(compare_no_order({1,2,3,4},{2,1,4,4})==false) + +asserteq(range(10,9),{}) +asserteq(range(10,10),{10}) +asserteq(range(10,11),{10,11}) + +-- update inserts key-value pairs from the second table +t1 = {one=1,two=2} +t2 = {three=3,two=20,four=4} +asserteq(update(t1,t2),{one=1,three=3,two=20,four=4}) + +-- the difference between move and icopy is that the second removes +-- any extra elements in the destination after end of copy +-- 3rd arg is the index to start in the destination, defaults to 1 +asserteq(move({1,2,3,4,5,6},{20,30}),{20,30,3,4,5,6}) +asserteq(move({1,2,3,4,5,6},{20,30},2),{1,20,30,4,5,6}) +asserteq(icopy({1,2,3,4,5,6},{20,30},2),{1,20,30}) +-- 5th arg determines how many elements to copy (default size of source) +asserteq(icopy({1,2,3,4,5,6},{20,30},2,1,1),{1,20}) +-- 4th arg is where to stop copying from the source (default s to 1) +asserteq(icopy({1,2,3,4,5,6},{20,30},2,2,1),{1,30}) + +-- whereas insertvalues works like table.insert, but inserts a range of values +-- from the given table. +asserteq(insertvalues({1,2,3,4,5,6},2,{20,30}),{1,20,30,2,3,4,5,6}) +asserteq(insertvalues({1,2},{3,4}),{1,2,3,4}) + +-- the 4th arg of move and icopy gives the start index in the source table +asserteq(move({1,2,3,4,5,6},{20,30},2,2),{1,30,3,4,5,6}) +asserteq(icopy({1,2,3,4,5,6},{20,30},2,2),{1,30}) + +t = {1,2,3,4,5,6} +move(t,{20,30},2,1,1) +asserteq(t,{1,20,3,4,5,6}) +set(t,0,2,3) +asserteq(t,{1,0,0,4,5,6}) +insertvalues(t,1,{10,20}) +asserteq(t,{10,20,1,0,0,4,5,6}) + +asserteq(merge({10,20,30},{nil, nil, 30, 40}), {[3]=30}) +asserteq(merge({10,20,30},{nil, nil, 30, 40}, true), {10,20,30,40}) + + +-- Function to check that the order of elements returned by the iterator +-- match the order of the elements in the list. +function assert_iter_order(iter,l) + local i = 0 + for k,v in iter do + i = i + 1 + asserteq(k,l[i][1]) + asserteq(v,l[i][2]) + end +end + +local t = {a=10,b=9,c=8,d=7,e=6,f=5,g=4,h=3,i=2,j=1} + +assert_iter_order( + sort(t), + {{'a',10},{'b',9},{'c',8},{'d',7},{'e',6},{'f',5},{'g',4},{'h',3},{'i',2},{'j',1}}) + +assert_iter_order( + sortv(t), + {{'j',1},{'i',2},{'h',3},{'g',4},{'f',5},{'e',6},{'d',7},{'c',8},{'b',9},{'a',10}}) + + +asserteq(difference({a = true, b = true},{a = true, b = true}),{}) + +-- no longer confused by false values ;) +asserteq(difference({v = false},{v = false}),{}) + +asserteq(difference({a = true},{b = true}),{a=true}) + +-- symmetric difference +asserteq(difference({a = true},{b = true},true),{a=true,b=true}) + +--basic index_map test +asserteq(index_map({10,20,30}), {[10]=1,[20]=2,[30]=3}) +--test that repeated values return multiple indices +asserteq(index_map({10,20,30,30,30}), {[10]=1,[20]=2,[30]={3,4,5}}) + +-- Reduce +asserteq(tablex.reduce('-', {}, 2), 2) +asserteq(tablex.reduce('-', {}), nil) +asserteq(tablex.reduce('-', {1,2,3,4,5}), -13) +asserteq(tablex.reduce('-', {1,2,3,4,5}, 1), -14) + + +-- tablex.compare +do + asserteq(tablex.compare({},{}, "=="), true) + asserteq(tablex.compare({1,2,3}, {1,2,3}, "=="), true) + asserteq(tablex.compare({1,"hello",3}, {1,2,3}, "=="), false) + asserteq(tablex.compare( + {1,2,3, hello = "world"}, + {1,2,3}, + function(v1, v2) return v1 == v2 end), + true) -- only compares the list part +end + + +-- tablex.rfind +do + local rfind = tablex.rfind + local lst = { "Rudolph", "the", "red-nose", "raindeer" } + asserteq(rfind(lst, "Santa"), nil) + asserteq(rfind(lst, "raindeer", -2), nil) + asserteq(rfind(lst, "raindeer"), 4) + asserteq(rfind(lst, "Rudolph"), 1) + asserteq(rfind(lst, "the", -3), 2) + asserteq(rfind(lst, "the", -30), nil) + asserteq(rfind({10,10,10},10), 3) +end + + +-- tablex.find_if +do + local fi = tablex.find_if + local lst = { "Rudolph", true, false, 15 } + asserteq({fi(lst, "==", "Rudolph")}, {1, true}) + asserteq({fi(lst, "==", true)}, {2, true}) + asserteq({fi(lst, "==", false)}, {3, true}) + asserteq({fi(lst, "==", 15)}, {4, true}) + + local cmp = function(v1, v2) return v1 == v2 and v2 end + asserteq({fi(lst, cmp, "Rudolph")}, {1, "Rudolph"}) + asserteq({fi(lst, cmp, true)}, {2, true}) + asserteq({fi(lst, cmp, false)}, {}) -- 'false' cannot be returned! + asserteq({fi(lst, cmp, 15)}, {4, 15}) +end + + +-- tablex.map_named_method +do + local Car = {} + Car.__index = Car + function Car.new(car) + return setmetatable(car or {}, Car) + end + Car.speed = 0 + function Car:faster(increase) + self.speed = self.speed + (increase or 1) + return self.speed + end + function Car:slower(self, decrease) + self.speed = self.speed - (decrease or 1) + return self.speed + end + + local ferrari = Car.new{ name = "Ferrari" } + local lamborghini = Car.new{ name = "Lamborghini", speed = 50 } + local cars = { ferrari, lamborghini } + + asserteq(ferrari.speed, 0) + asserteq(lamborghini.speed, 50) + asserteq(tablex.map_named_method("faster", cars, 10), {10, 60}) + asserteq(ferrari.speed, 10) + asserteq(lamborghini.speed, 60) + +end + + +-- tablex.foreach +do + local lst = { "one", "two", "three", hello = "world" } + tablex.foreach(lst, function(v, k, sep) + lst[k] = tostring(k) .. sep .. v + end, " = ") + asserteq(lst, {"1 = one", "2 = two", "3 = three", hello = "hello = world"}) +end + + +-- tablex.foreachi +do + local lst = { "one", "two", "three", hello = "world" } + tablex.foreachi(lst, function(v, k, sep) + lst[k] = tostring(k) .. sep .. v + end, " = ") + asserteq(lst, {"1 = one", "2 = two", "3 = three", hello = "world"}) +end + + +-- tablex.new +asserteq(tablex.new(3, "hi"), { "hi", "hi", "hi" }) + + +-- tablex.search +do + local t = { + penlight = { + battery = { + type = "AA", + capacity = "1500mah", + }, + }, + hello = { + world = { + also = "AA" + } + } + } + asserteq(tablex.search(t, "1500mah"), "penlight.battery.capacity") + asserteq(tablex.search(t, "AA", {t.penlight} ), "hello.world.also") + asserteq(tablex.search(t, "xxx"), nil) +end + + +-- tablex.readonly +do + local ro = tablex.readonly { 1,2,3, hello = "world" } + asserteq(pcall(function() ro.hello = "hi there" end), false) + asserteq(getmetatable(ro), false) + + if not utils.lua51 then + asserteq(#ro, 3) + + local r = {} + for k,v in pairs(ro) do r[k] = v end + asserteq(r, { 1,2,3, hello = "world" }) + + r = {} + for k,v in ipairs(ro) do r[k] = v end + asserteq(r, { 1,2,3 }) + end +end diff --git a/Data/Libraries/Penlight/tests/test-tablex3.lua b/Data/Libraries/Penlight/tests/test-tablex3.lua new file mode 100644 index 0000000..6f4ea18 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-tablex3.lua @@ -0,0 +1,12 @@ +-- tablex.move when the tables are the same +-- and there are overlapping ranges +T = require 'pl.tablex' +asserteq = require 'pl.test'.asserteq + +t1 = {1,2,3,4,5,6,7,8,9,10} +t2 = T.copy(t1) +t3 = T.copy(t1) + +T.move(t1,t2,4,1,4) +T.move(t3,t3,4,1,4) +asserteq(t1,t3) diff --git a/Data/Libraries/Penlight/tests/test-template.lua b/Data/Libraries/Penlight/tests/test-template.lua new file mode 100644 index 0000000..fb13108 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-template.lua @@ -0,0 +1,245 @@ +local template = require 'pl.template' +local subst = template.substitute +local List = require 'pl.List' +local asserteq = require 'pl.test'.asserteq +local utils = require 'pl.utils' + + + +asserteq(subst([[ +# for i = 1,2 do +<p>Hello $(tostring(i))</p> +# end +]],_G),[[ +<p>Hello 1</p> +<p>Hello 2</p> +]]) + + + +asserteq(subst([[ +<ul> +# for name in ls:iter() do + <li>$(name)</li> +#end +</ul> +]],{ls = List{'john','alice','jane'}}),[[ +<ul> + <li>john</li> + <li>alice</li> + <li>jane</li> +</ul> +]]) + + + +-- can change the default escape from '#' so we can do C/C++ output. +-- note that the environment can have a parent field. +asserteq(subst([[ +> for i,v in ipairs{'alpha','beta','gamma'} do + cout << obj.${v} << endl; +> end +]],{_parent=_G, _brackets='{}', _escape='>'}),[[ + cout << obj.alpha << endl; + cout << obj.beta << endl; + cout << obj.gamma << endl; +]]) + + + +-- handle templates with a lot of substitutions +asserteq(subst(("$(x)\n"):rep(300), {x = "y"}), ("y\n"):rep(300)) + + + +-------------------------------------------------- +-- Test using no leading nor trailing linebreak +local tmpl = [[<ul> +# for i,val in ipairs(T) do +<li>$(i) = $(val:upper())</li> +# end +</ul>]] + +local my_env = { + ipairs = ipairs, + T = {'one','two','three'}, + _debug = true, +} +local res, err = template.substitute(tmpl, my_env) + +--print(res, err) +asserteq(res, [[<ul> +<li>1 = ONE</li> +<li>2 = TWO</li> +<li>3 = THREE</li> +</ul>]]) + + + +-------------------------------------------------- +-- Test using both leading and trailing linebreak +local tmpl = [[ +<ul> +# for i,val in ipairs(T) do +<li>$(i) = $(val:upper())</li> +# end +</ul> +]] + +local my_env = { + ipairs = ipairs, + T = {'one','two','three'}, + _debug = true, +} +local res, err = template.substitute(tmpl, my_env) + +--print(res, err) +asserteq(res, [[ +<ul> +<li>1 = ONE</li> +<li>2 = TWO</li> +<li>3 = THREE</li> +</ul> +]]) + + + +-------------------------------------------------- +-- Test reusing a compiled template +local tmpl = [[ +<ul> +# for i,val in ipairs(T) do +<li>$(i) = $(val:upper())</li> +# end +</ul> +]] + +local my_env = { + ipairs = ipairs, + T = {'one','two','three'} +} +local t, err = template.compile(tmpl, { debug = true }) +local res, err, code = t:render(my_env) +--print(res, err, code) +asserteq(res, [[ +<ul> +<li>1 = ONE</li> +<li>2 = TWO</li> +<li>3 = THREE</li> +</ul> +]]) + + +-- now reuse with different env +local my_env = { + ipairs = ipairs, + T = {'four','five','six'} +} +local t, err = template.compile(tmpl, { debug = true }) +local res, err, code = t:render(my_env) +--print(res, err, code) +asserteq(res, [[ +<ul> +<li>1 = FOUR</li> +<li>2 = FIVE</li> +<li>3 = SIX</li> +</ul> +]]) + + + +-------------------------------------------------- +-- Test the newline parameter +local tmpl = [[ +some list: $(T[1]:upper()) +# for i = 2, #T do +,$(T[i]:upper()) +# end +]] + +local my_env = { + ipairs = ipairs, + T = {'one','two','three'} +} +local t, err = template.compile(tmpl, { debug = true, newline = "" }) +local res, err, code = t:render(my_env) +--print(res, err, code) +asserteq(res, [[some list: ONE,TWO,THREE]]) + + + +-------------------------------------------------- +-- Test template run-time error +local tmpl = [[ +header: $("hello" * 10) +]] + +local t, err = template.compile(tmpl, { debug = true, newline = "" }) +local res, err, code = t:render() +--print(res, err, code) +assert(res == nil, "expected nil here because of the runtime error") +asserteq(type(err), "string") +asserteq(type(utils.load(code)), "function") + + + +-------------------------------------------------- +-- Test template run-time, doesn't fail on table value +-- table.concat fails if we insert a non-string (table) value +local tmpl = [[ +header: $(myParam) +]] + +local t, err = template.compile(tmpl, { debug = true, newline = "" }) +local myParam = {} +local res, err, code = t:render( {myParam = myParam } ) -- insert a table +--print(res, err, code) +asserteq(res, "header: "..tostring(myParam)) +asserteq(type(err), "nil") + + + +-------------------------------------------------- +-- Test template compile-time error +local tmpl = [[ +header: $(this doesn't work) +]] + +local my_env = { + ipairs = ipairs, + T = {'one','two','three'} +} +local t, err, code = template.compile(tmpl, { debug = true, newline = "" }) +--print(t, err, code) +assert(t==nil, "expected t to be nil here because of the syntax error") +asserteq(type(err), "string") +asserteq(type(code), "string") + + + +-------------------------------------------------- +-- Test using template being a single static string +local tmpl = [[ +<ul> +<p>a paragraph</p> +<p>a paragraph</p> +</ul> +]] + +local t, err = template.compile(tmpl, { debug = true }) +local res, err, code = t:render(my_env) +--print(res, err, code) + +asserteq(res, [[<ul> +<p>a paragraph</p> +<p>a paragraph</p> +</ul> +]]) +asserteq(code, [[return "<ul>\ +<p>a paragraph</p>\ +<p>a paragraph</p>\ +</ul>\ +"]]) + + +print("template: success") diff --git a/Data/Libraries/Penlight/tests/test-text.lua b/Data/Libraries/Penlight/tests/test-text.lua new file mode 100644 index 0000000..9419b22 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-text.lua @@ -0,0 +1,177 @@ +local T = require 'pl.text' +local utils = require 'pl.utils' +local Template = T.Template +local asserteq = require 'pl.test'.asserteq +local OrderedMap = require 'pl.OrderedMap' + +local t1 = Template [[ +while true do + $contents +end +]] + +assert(t1:substitute {contents = 'print "hello"'},[[ +while true do + print "hello" +end +]]) + +assert(t1:indent_substitute {contents = [[ +for i = 1,10 do + gotcha(i) +end +]]},[[ +while true do + for i = 1,10 do + gotcha(i) + end +end +]]) + +asserteq(T.dedent [[ + one + two + three +]],[[ +one +two +three +]]) +asserteq(T.fill ([[ +It is often said of Lua that it does not include batteries. That is because the goal of Lua is to produce a lean expressive language that will be used on all sorts of machines, (some of which don't even have hierarchical filesystems). The Lua language is the equivalent of an operating system kernel; the creators of Lua do not see it as their responsibility to create a full software ecosystem around the language. That is the role of the community. +]],20),[[ +It is often said of Lua +that it does not include +batteries. That is because +the goal of Lua is to +produce a lean expressive +language that will be +used on all sorts of machines, +(some of which don't +even have hierarchical +filesystems). The Lua +language is the equivalent +of an operating system +kernel; the creators of +Lua do not see it as their +responsibility to create +a full software ecosystem +around the language. That +is the role of the community. +]]) + +local template = require 'pl.template' + +local t = [[ +# for i = 1,3 do + print($(i+1)) +# end +]] + +asserteq(template.substitute(t),[[ + print(2) + print(3) + print(4) +]]) + +t = [[ +> for i = 1,3 do + print(${i+1}) +> end +]] + +asserteq(template.substitute(t,{_brackets='{}',_escape='>'}),[[ + print(2) + print(3) + print(4) +]]) + +t = [[ +#@ for i = 1,3 do + print(@{i+1}) +#@ end +]] + +asserteq(template.substitute(t,{_brackets='{}',_escape='#@',_inline_escape='@'}),[[ + print(2) + print(3) + print(4) +]]) + +--- iteration using pairs is usually unordered. But using OrderedMap +--- we can get the exact original ordering. + +t = [[ +# for k,v in pairs(T) do + "$(k)", -- $(v) +# end +]] + +if utils.lua51 then + -- easy enough to define a general pairs in Lua 5.1 + local rawpairs = pairs + function pairs(t) + local mt = getmetatable(t) + local f = mt and mt.__pairs + if f then + return f(t) + else + return rawpairs(t) + end + end +end + + +local Tee = OrderedMap{{Dog = 'Bonzo'}, {Cat = 'Felix'}, {Lion = 'Leo'}} + +-- note that the template will also look up global functions using _parent +asserteq(template.substitute(t,{T=Tee,_parent=_G}),[[ + "Dog", -- Bonzo + "Cat", -- Felix + "Lion", -- Leo +]]) + +-- for those with a fondness for Python-style % formatting... +T.format_operator() +asserteq('[%s]' % 'home', '[home]') +asserteq('%s = %d' % {'fred',42},'fred = 42') + +-- mostly works like string.format, except that %s forces use of tostring() +-- rather than throwing an error +local List = require 'pl.List' +asserteq('TBL:%s' % List{1,2,3},'TBL:{1,2,3}') + +-- table with keys and format with $ +asserteq('<$one>' % {one=1}, '<1>') +-- (second arg may also be a function, like os.getenv) +function subst(k) + if k == 'A' then return 'ay' + elseif k == 'B' then return 'bee' + else return '?' + end +end +asserteq( + '$A & $B' % subst,'ay & bee' +) + +t = [[ +a whole lot +of love +]] + +asserteq(T.indent(t,4),[[ + a whole lot + of love +]]) + +asserteq(T.indent([[ +easy + +enough! +]],2,'*'),[[ +**easy +** +**enough! +]]) + + diff --git a/Data/Libraries/Penlight/tests/test-types.lua b/Data/Libraries/Penlight/tests/test-types.lua new file mode 100644 index 0000000..bfb3c8f --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-types.lua @@ -0,0 +1,124 @@ +---- testing types +local types = require 'pl.types' +local asserteq = require 'pl.test'.asserteq +local List = require 'pl.List' + +local list = List() +local array = {10,20,30} +local map = {one=1,two=2} + +-- extended type() function +asserteq(types.type(array),'table') +asserteq(types.type('hello'),'string') +-- knows about Lua file objects +asserteq(types.type(io.stdin),'file') +local f = io.open("tests/test-types.lua") +asserteq(types.type(f),'file') +f:close() +-- and class names +asserteq(types.type(list),'List') +-- tables with unknown metatable +asserteq(types.type(setmetatable({},{})), "unknown table") +-- userdata with unknown metatable +if newproxy then + asserteq(types.type(newproxy(true)), "unknown userdata") +end + +asserteq(types.is_integer(10),true) +asserteq(types.is_integer(10.1),false) +asserteq(types.is_integer(-10),true) +asserteq(types.is_integer(-10.1),false) +-- do note that for Lua < 5.3, 10.0 is the same as 10; an integer. + +asserteq(types.is_callable(asserteq),true) +asserteq(types.is_callable(List),true) + +asserteq(types.is_indexable(array),true) +asserteq(types.is_indexable('hello'),nil) +asserteq(types.is_indexable(10),nil) +if newproxy then + local v = newproxy(true) + local mt = getmetatable(v) + mt.__len = true + mt.__index = true + asserteq(types.is_indexable(v), true) +end +if newproxy then + local v = newproxy(true) + asserteq(types.is_indexable(v), nil) +end + +asserteq(types.is_iterable(array),true) +asserteq(types.is_iterable(true),nil) +asserteq(types.is_iterable(42),nil) +asserteq(types.is_iterable("array"),nil) +if newproxy then + local v = newproxy(true) + local mt = getmetatable(v) + mt.__pairs = true + asserteq(types.is_iterable(v), true) +end +if newproxy then + local v = newproxy(true) + asserteq(types.is_iterable(v), nil) +end + +asserteq(types.is_writeable(array),true) +asserteq(types.is_writeable(true),nil) +asserteq(types.is_writeable(42),nil) +asserteq(types.is_writeable("array"),nil) +if newproxy then + local v = newproxy(true) + local mt = getmetatable(v) + mt.__newindex = true + asserteq(types.is_writeable(v), true) +end +if newproxy then + local v = newproxy(true) + asserteq(types.is_writeable(v), nil) +end + +asserteq(types.is_empty(nil),true) +asserteq(types.is_empty({}),true) +asserteq(types.is_empty({[false] = false}),false) +asserteq(types.is_empty(""),true) +asserteq(types.is_empty(" ",true),true) +asserteq(types.is_empty(" "),false) +asserteq(types.is_empty(true),true) +-- Numbers +asserteq(types.is_empty(0), true) +asserteq(types.is_empty(20), true) +-- Booleans +asserteq(types.is_empty(false), true) +asserteq(types.is_empty(true), true) +-- Functions +asserteq(types.is_empty(print), true) +-- Userdata +--asserteq(types.is_empty(newproxy()), true) --newproxy was removed in Lua 5.2 + +-- a more relaxed kind of truthiness.... +asserteq(types.to_bool('yes'),true) +asserteq(types.to_bool('true'),true) +asserteq(types.to_bool('y'),true) +asserteq(types.to_bool('t'),true) +asserteq(types.to_bool('YES'),true) +asserteq(types.to_bool('1'),true) +asserteq(types.to_bool('no'),false) +asserteq(types.to_bool('false'),false) +asserteq(types.to_bool('n'),false) +asserteq(types.to_bool('f'),false) +asserteq(types.to_bool('NO'),false) +asserteq(types.to_bool('0'),false) +asserteq(types.to_bool(1),true) +asserteq(types.to_bool(0),false) +local de_fr = { 'ja', 'oui' } +asserteq(types.to_bool('ja', de_fr),true) +asserteq(types.to_bool('OUI', de_fr),true) +local t_e = {} +local t_ne = { "not empty" } +asserteq(types.to_bool(t_e,nil,false),false) +asserteq(types.to_bool(t_e,nil,true),false) +asserteq(types.to_bool(t_ne,nil,false),false) +asserteq(types.to_bool(t_ne,nil,true),true) +asserteq(types.to_bool(coroutine.create(function() end),nil,true),true) +asserteq(types.to_bool(coroutine.create(function() end),nil,false),false) diff --git a/Data/Libraries/Penlight/tests/test-url.lua b/Data/Libraries/Penlight/tests/test-url.lua new file mode 100644 index 0000000..9d9af6a --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-url.lua @@ -0,0 +1,37 @@ +local url = require 'pl.url' +local asserteq = require 'pl.test' . asserteq + +asserteq(url.quote(''), '') +asserteq(url.quote('ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') +asserteq(url.quote('abcdefghijklmnopqrstuvwxyz'), 'abcdefghijklmnopqrstuvwxyz') +asserteq(url.quote('0123456789'), '0123456789') +asserteq(url.quote(' -_./'), '%20-_./') +asserteq(url.quote('`~!@#$%^&*()'), '%60%7E%21%40%23%24%25%5E%26%2A%28%29') +asserteq(url.quote('%2'), '%252') +asserteq(url.quote('2R%1%%'), '2R%251%25%25') + +asserteq(url.quote('', true), '') +asserteq(url.quote('ABCDEFGHIJKLMNOPQRSTUVWXYZ', true), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') +asserteq(url.quote('abcdefghijklmnopqrstuvwxyz', true), 'abcdefghijklmnopqrstuvwxyz') +asserteq(url.quote('0123456789'), '0123456789', true) +asserteq(url.quote(' -_./', true), '+-_.%2F') +asserteq(url.quote('`~!@#$%^&*()', true), '%60%7E%21%40%23%24%25%5E%26%2A%28%29') +asserteq(url.quote('%2', true), '%252') +asserteq(url.quote('2R%1%%', true), '2R%251%25%25') + +asserteq(url.unquote(''), '') +asserteq(url.unquote('ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') +asserteq(url.unquote('abcdefghijklmnopqrstuvwxyz'), 'abcdefghijklmnopqrstuvwxyz') +asserteq(url.unquote('0123456789'), '0123456789') +asserteq(url.unquote(' -_./'), ' -_./') +asserteq(url.unquote('+-_.%2F'), ' -_./') +asserteq(url.unquote('%20-_./'), ' -_./') +asserteq(url.unquote('%60%7E%21%40%23%24%25%5E%26%2A%28%29'), '`~!@#$%^&*()') +asserteq(url.unquote('%252'), '%2') +asserteq(url.unquote('2%52%1%%'), '2R%1%%') +asserteq(url.unquote('2R%251%25%25'), '2R%1%%') + +asserteq(url.quote(true), true) +asserteq(url.quote(42), 42) +asserteq(url.unquote(true), true) +asserteq(url.unquote(42), 42) diff --git a/Data/Libraries/Penlight/tests/test-utils.lua b/Data/Libraries/Penlight/tests/test-utils.lua new file mode 100644 index 0000000..cff400a --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-utils.lua @@ -0,0 +1,320 @@ +local utils = require 'pl.utils' +local path = require 'pl.path' +local test = require 'pl.test' +local asserteq, T = test.asserteq, test.tuple + + +local function quote(s) + if utils.is_windows then + return '"'..s..'"' + else + return "'"..s.."'" + end +end + +-- construct command to run external lua, we need to to be able to run some +-- tests on the same lua engine, but also need to pass on the LuaCov flag +-- if it was used, to make sure we report the proper coverage. +local cmd = "-e " +do + local i = 0 + while arg[i-1] do + local a = arg[i-1] + if a:find("package%.path") and a:sub(1,1) ~= "'" then + a = quote(a) + end + cmd = a .. " " .. cmd + i = i - 1 + end +end + + +--- quitting +do + local luacode = quote("require([[pl.utils]]).quit([[hello world]])") + local success, code, stdout, stderr = utils.executeex(cmd..luacode) + asserteq(success, false) + if utils.is_windows then + asserteq(code, -1) + else + asserteq(code, 255) + end + asserteq(stdout, "") + asserteq(stderr, "hello world\n") + + local luacode = quote("require([[pl.utils]]).quit(2, [[hello world]])") + local success, code, stdout, stderr = utils.executeex(cmd..luacode) + asserteq(success, false) + asserteq(code, 2) + asserteq(stdout, "") + asserteq(stderr, "hello world\n") + + local luacode = quote("require([[pl.utils]]).quit(2, [[hello %s]], 42)") + local success, code, stdout, stderr = utils.executeex(cmd..luacode) + asserteq(success, false) + asserteq(code, 2) + asserteq(stdout, "") + asserteq(stderr, "hello 42\n") + + local luacode = quote("require([[pl.utils]]).quit(2)") + local success, code, stdout, stderr = utils.executeex(cmd..luacode) + asserteq(success, false) + asserteq(code, 2) + asserteq(stdout, "") + asserteq(stderr, "") +end + +----- importing module tables wholesale --- +utils.import(math) +asserteq(type(sin),"function") +asserteq(type(abs),"function") + +--- useful patterns +local P = utils.patterns +asserteq(("+0.1e10"):match(P.FLOAT) ~= nil, true) +asserteq(("-23430"):match(P.INTEGER) ~= nil, true) +asserteq(("my_little_pony99"):match(P.IDEN) ~= nil, true) + +--- escaping magic chars +local escape = utils.escape +asserteq(escape '[a]','%[a%]') +asserteq(escape '$(bonzo)','%$%(bonzo%)') + +--- choose +asserteq(utils.choose(true, 1, 2), 1) +asserteq(utils.choose(false, 1, 2), 2) + +--- splitting strings --- +local split = utils.split +asserteq(split("hello dolly"),{"hello","dolly"}) +asserteq(split("hello,dolly",","),{"hello","dolly"}) +asserteq(split("hello,dolly,",","),{"hello","dolly"}) + +local first,second = utils.splitv("hello:dolly",":") +asserteq(T(first,second),T("hello","dolly")) +local first,second = utils.splitv("hello:dolly:parton",":", false, 2) +asserteq(T(first,second),T("hello","dolly:parton")) +local first,second,third = utils.splitv("hello=dolly:parton","[:=]") +asserteq(T(first,second,third),T("hello","dolly","parton")) +local first,second = utils.splitv("hello=dolly:parton","[:=]", false, 2) +asserteq(T(first,second),T("hello","dolly:parton")) + +----- table of values to table of strings +asserteq(utils.array_tostring{1,2,3},{"1","2","3"}) +-- writing into existing table +local tmp = {} +utils.array_tostring({1,2,3},tmp) +asserteq(tmp,{"1","2","3"}) + +--- memoizing a function +local kount = 0 +local f = utils.memoize(function(x) + kount = kount + 1 + return x*x +end) +asserteq(f(2),4) +asserteq(f(10),100) +asserteq(f(2),4) +-- actual function only called twice +asserteq(kount,2) + +-- string lambdas +local L = utils.string_lambda +local g = L"|x| x:sub(1,1)" +asserteq(g("hello"),"h") + +local f = L"|x,y| x - y" +asserteq(f(10,2),8) + +-- alternative form for _one_ argument +asserteq(L("2 * _")(4), 8) + +local List = require 'pl.List' +local ls = List{10,20,30} + +-- string lambdas can be used throughout Penlight +asserteq(ls:map"_+1", {11,21,31}) + +-- because they use this common function +local function test_fn_arg(f) + f = utils.function_arg(1,f) + asserteq(f(10),11) +end + +test_fn_arg (function (x) return x + 1 end) +test_fn_arg '_ + 1' +test.assertraise(function() test_fn_arg {} end, 'not a callable object') +test.assertraise(function() test_fn_arg (0) end, 'must be callable') + +-- partial application + +local f1 = utils.bind1(f,10) +asserteq(f1(2), 8) + +local f2 = utils.bind2(f,2) +asserteq(f2(10), 8) + +--- extended type checking + +local is_type = utils.is_type +-- anything without a metatable works as regular type() function +asserteq(is_type("one","string"),true) +asserteq(is_type({},"table"),true) + +-- but otherwise the type of an object is considered to be its metatable +asserteq(is_type(ls,List),true) + +-- compatibility functions +local chunk = utils.load 'return 42' +asserteq(chunk(),42) + +chunk = utils.load 'a = 42' +chunk() +asserteq(a,42) + +local t = {} +chunk = utils.load ('b = 42','<str>','t',t) +chunk() +asserteq(t.b,42) + +chunk,err = utils.load ('a = ?','<str>') +assert(err,[[[string "<str>"]:1: unexpected symbol near '?']]) + +asserteq(utils.quote_arg("foo"), [[foo]]) +if path.is_windows then + asserteq(utils.quote_arg(""), '^"^"') + asserteq(utils.quote_arg('"'), '^"') + asserteq(utils.quote_arg([[ \]]), [[^" \\^"]]) + asserteq(utils.quote_arg([[foo\\ bar\\" baz\]]), [[^"foo\\ bar\\\\\^" baz\\^"]]) + asserteq(utils.quote_arg("%path% ^^!()"), [[^"^%path^% ^^^^^!()^"]]) +else + asserteq(utils.quote_arg(""), "''") + asserteq(utils.quote_arg("'"), [[''\''']]) + asserteq(utils.quote_arg([['a\'b]]), [[''\''a\'\''b']]) +end + +-- packing and unpacking arguments in a nil-safe way +local t = utils.pack(nil, nil, "hello", nil) +asserteq(t.n, 4) -- the last nil does count as an argument + +local arg1, arg2, arg3, arg4 = utils.unpack(t) +assert(arg1 == nil) +assert(arg2 == nil) +asserteq("hello", arg3) +assert(arg4 == nil) + + +-- Assert arguments assert_arg +local ok, err = pcall(function() + utils.assert_arg(4,'!@#$%^&*','string',require("pl.path").isdir,'not a directory') +end) +asserteq(ok, false) +asserteq(err:match("(argument .+)$"), "argument 4: '!@#$%^&*' not a directory") + +local ok, err = pcall(function() + utils.assert_arg(1, "hello", "table") +end) +asserteq(ok, false) +asserteq(err:match("(argument .+)$"), "argument 1 expected a 'table', got a 'string'") + +local ok, err = pcall(function() + return utils.assert_arg(1, "hello", "string") +end) +asserteq(ok, true) +asserteq(err, "hello") + +-- assert_string +local success, err = pcall(utils.assert_string, 2, 5) +asserteq(success, false) +asserteq(err:match("(argument .+)$"), "argument 2 expected a 'string', got a 'number'") + +local x = utils.assert_string(2, "5") +asserteq(x, "5") + + +do + -- printf -- without template + local luacode = quote("require([[pl.utils]]).printf([[hello world]])") + local success, code, stdout, stderr = utils.executeex(cmd..luacode) + asserteq(success, true) + asserteq(code, 0) + asserteq(stdout, "hello world") + asserteq(stderr, "") + + -- printf -- with template + local luacode = quote("require([[pl.utils]]).printf([[hello %s]], [[world]])") + local success, code, stdout, stderr = utils.executeex(cmd..luacode) + asserteq(success, true) + asserteq(code, 0) + asserteq(stdout, "hello world") + asserteq(stderr, "") + + -- printf -- with bad template + local luacode = quote("require([[pl.utils]]).printf(42)") + local success, code, stdout, stderr = utils.executeex(cmd..luacode) + asserteq(success, false) + asserteq(code, 1) + asserteq(stdout, "") + assert(stderr:find("argument 1 expected a 'string', got a 'number'")) +end + +do + -- on_error, raise -- default + utils.on_error("default") + local ok, err = utils.raise("some error") + asserteq(ok, nil) + asserteq(err, "some error") + local ok, err = pcall(utils.on_error, "bad one") + asserteq(ok, false) + asserteq(err, "Bad argument expected string; 'default', 'quit', or 'error'. Got 'bad one'") + + -- on_error, raise -- error + utils.on_error("error") + local ok, err = pcall(utils.raise, "some error") + asserteq(ok, false) + asserteq(err, "some error") + local ok, err = pcall(utils.on_error, "bad one") + asserteq(ok, false) + assert(err:find("Bad argument expected string; 'default', 'quit', or 'error'. Got 'bad one'")) + + -- on_error, raise -- quit + utils.on_error("quit") + local luacode = quote("local u=require([[pl.utils]]) u.on_error([[quit]]) u.raise([[some error]])") + local success, code, stdout, stderr = utils.executeex(cmd..luacode) + asserteq(success, false) + if utils.is_windows then + asserteq(code, -1) + else + asserteq(code, 255) + end + asserteq(stdout, "") + asserteq(stderr, "some error\n") + + local luacode = quote("local u=require([[pl.utils]]) u.on_error([[quit]]) u.on_error([[bad one]])") + local success, code, stdout, stderr = utils.executeex(cmd..luacode) + asserteq(success, false) + if utils.is_windows then + asserteq(code, -1) + else + asserteq(code, 255) + end + asserteq(stdout, "") + asserteq(stderr, "Bad argument expected string; 'default', 'quit', or 'error'. Got 'bad one'\n") + + utils.on_error("default") -- cleanup by restoring behaviour after on_error + raise tests +end + +do + -- readlines + local f = utils.readlines("tests/test-utils.lua") + asserteq(type(f), "table") + local v = "some extraordinary string this is only in this file for test purposes so we can go and find it" + local found = false + for i, line in ipairs(f) do + if line:find(v) then + found = true + break + end + end + asserteq(found, true) +end diff --git a/Data/Libraries/Penlight/tests/test-utils2.lua b/Data/Libraries/Penlight/tests/test-utils2.lua new file mode 100644 index 0000000..897da3e --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-utils2.lua @@ -0,0 +1,53 @@ +local path = require 'pl.path' +local utils = require 'pl.utils' +local asserteq = require 'pl.test'.asserteq + +local echo_lineending = "\n" +if path.is_windows then + echo_lineending = " \n" +end + +local function test_executeex(cmd, expected_successful, expected_retcode, expected_stdout, expected_stderr) +--print("\n"..cmd) +--print(os.execute(cmd)) +--print(utils.executeex(cmd)) + local successful, retcode, stdout, stderr = utils.executeex(cmd) + asserteq(successful, expected_successful) + asserteq(retcode, expected_retcode) + asserteq(stdout, expected_stdout) + asserteq(stderr, expected_stderr) +end + +-- Check the return codes +if utils.is_windows then + test_executeex("exit", true, 0, "", "") + test_executeex("exit 0", true, 0, "", "") + test_executeex("exit 1", false, 1, "", "") + test_executeex("exit 13", false, 13, "", "") + test_executeex("exit 255", false, 255, "", "") + test_executeex("exit 256", false, 256, "", "") + test_executeex("exit 257", false, 257, "", "") + test_executeex("exit 3809", false, 3809, "", "") + test_executeex("exit -1", false, -1, "", "") + test_executeex("exit -13", false, -13, "", "") + test_executeex("exit -255", false, -255, "", "") + test_executeex("exit -256", false, -256, "", "") + test_executeex("exit -257", false, -257, "", "") + test_executeex("exit -3809", false, -3809, "", "") +else + test_executeex("exit", true, 0, "", "") + test_executeex("exit 0", true, 0, "", "") + test_executeex("exit 1", false, 1, "", "") + test_executeex("exit 13", false, 13, "", "") + test_executeex("exit 255", false, 255, "", "") + -- on posix anything other than 0-255 is undefined + test_executeex("exit 256", true, 0, "", "") + test_executeex("exit 257", false, 1, "", "") + test_executeex("exit 3809", false, 225, "", "") +end + +-- Check output strings +test_executeex("echo stdout", true, 0, "stdout" .. echo_lineending, "") +test_executeex("(echo stderr 1>&2)", true, 0, "", "stderr" .. echo_lineending) +test_executeex("(echo stdout && (echo stderr 1>&2))", true, 0, "stdout" .. echo_lineending, "stderr" .. echo_lineending) + diff --git a/Data/Libraries/Penlight/tests/test-utils3.lua b/Data/Libraries/Penlight/tests/test-utils3.lua new file mode 100644 index 0000000..a635de5 --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-utils3.lua @@ -0,0 +1,76 @@ +--- testing Lua 5.1/5.2 compatibility functions +-- these are global side-effects of pl.utils +local utils = require 'pl.utils' +local test = require 'pl.test' +local asserteq = test.asserteq +local _,lua = require 'pl.app'. lua() +local setfenv,getfenv = utils.setfenv, utils.getfenv + +-- utils.execute is a compromise between 5.1 and 5.2 for os.execute changes +-- can we call Lua ? +local ok,code = utils.execute(lua..' -v') +asserteq(ok,true) +asserteq(code,0) +-- does it return false when it fails ? +asserteq(utils.execute('most-likely-nonexistent-command'),false) + +-- table.pack is defined for 5.1 +local t = table.pack(1,nil,'hello') +asserteq(t.n,3) +assert(t[1] == 1 and t[3] == 'hello') + +-- unpack is not globally available for 5.2 unless in compat mode. +-- But utils.unpack is always defined. +local a,b = utils.unpack{10,'wow'} +assert(a == 10 and b == 'wow') + +-- utils.load() is Lua 5.2 style +chunk = utils.load('return x+y','tmp','t',{x=1,y=2}) +asserteq(chunk(),3) + +-- can only load a binary chunk if the mode permits! +local f = string.dump(function() end) +local res,err = utils.load(f,'tmp','t') +test.assertmatch(err,'attempt to load') + +-- package.searchpath for Lua 5.1 +-- nota bene: depends on ./?.lua being in the package.path! +-- So we hack it if not found +if not package.path:find '.[/\\]%?' then + package.path = './?.lua;'..package.path +end + +asserteq( + package.searchpath('tests.test-utils3',package.path):gsub('\\','/'), + './tests/test-utils3.lua' +) + +-- testing getfenv and setfenv for both interpreters + +function test() + return X + Y + Z +end + +t = {X = 1, Y = 2, Z = 3} + +setfenv(test,t) + +assert(test(),6) + +t.X = 10 + +assert(test(),15) + +local getfenv,_G = getfenv,_G + +function test2() + local env = {x=2} + setfenv(1,env) + asserteq(getfenv(1),env) + asserteq(x,2) +end + +test2() + + + diff --git a/Data/Libraries/Penlight/tests/test-xml.lua b/Data/Libraries/Penlight/tests/test-xml.lua new file mode 100644 index 0000000..cd8253a --- /dev/null +++ b/Data/Libraries/Penlight/tests/test-xml.lua @@ -0,0 +1,526 @@ +local xml = require 'pl.xml' +local asserteq = require 'pl.test'.asserteq +local dump = require 'pl.pretty'.dump + +-- Prosody stanza.lua style XML building + +d = xml.new 'top' : addtag 'child' : text 'alice' : up() : addtag 'child' : text 'bob' + +d = xml.new 'children' : + addtag 'child' : + addtag 'name' : text 'alice' : up() : addtag 'age' : text '5' : up() : addtag('toy',{type='fluffy'}) : up() : + up() : + addtag 'child': + addtag 'name' : text 'bob' : up() : addtag 'age' : text '6' : up() : addtag('toy',{type='squeaky'}) + +asserteq( +xml.tostring(d,'',' '), +[[ + +<children> + <child> + <name>alice</name> + <age>5</age> + <toy type='fluffy'/> + </child> + <child> + <name>bob</name> + <age>6</age> + <toy type='squeaky'/> + </child> +</children>]]) + +-- Orbit-style 'xmlification' + +local children,child,toy,name,age = xml.tags 'children, child, toy, name, age' + +d1 = children { + child {name 'alice', age '5', toy {type='fluffy'}}, + child {name 'bob', age '6', toy {type='squeaky'}} +} + +assert(xml.compare(d,d1)) + +-- or we can use a template document to convert Lua data to LOM + +templ = child {name '$name', age '$age', toy{type='$toy'}} + +d2 = children(templ:subst{ + {name='alice',age='5',toy='fluffy'}, + {name='bob',age='6',toy='squeaky'} +}) + +assert(xml.compare(d1,d2)) + +-- Parsing Google Weather service results -- + +local joburg = [[ +<?xml version="1.0"?> +<xml_api_reply version='1'> + <weather module_id='0' tab_id='0' mobile_zipped='1' section='0' row='0' mobile_row='0'> + <forecast_information> + <city data='Johannesburg, Gauteng'/> + <postal_code data='Johannesburg,ZA'/> + <latitude_e6 data=''/> + <longitude_e6 data=''/> + <forecast_date data='2010-10-02'/> + <current_date_time data='2010-10-02 18:30:00 +0000'/> + <unit_system data='US'/> + </forecast_information> + <current_conditions> + <condition data='Clear'/> + <temp_f data='75'/> + <temp_c data='24'/> + <humidity data='Humidity: 19%'/> + <icon data='/ig/images/weather/sunny.gif'/> + <wind_condition data='Wind: NW at 7 mph'/> + </current_conditions> + <forecast_conditions> + <day_of_week data='Sat'/> + <low data='60'/> + <high data='89'/> + <icon data='/ig/images/weather/sunny.gif'/> + <condition data='Clear'/> + </forecast_conditions> + <forecast_conditions> + <day_of_week data='Sun'/> + <low data='53'/> + <high data='86'/> + <icon data='/ig/images/weather/sunny.gif'/> + <condition data='Clear'/> + </forecast_conditions> + <forecast_conditions> + <day_of_week data='Mon'/> + <low data='57'/> + <high data='87'/> + <icon data='/ig/images/weather/sunny.gif'/> + <condition data='Clear'/> + </forecast_conditions> + <forecast_conditions> + <day_of_week data='Tue'/> + <low data='60'/> + <high data='84'/> + <icon data='/ig/images/weather/sunny.gif'/> + <condition data='Clear'/> + </forecast_conditions> + </weather> +</xml_api_reply> + +]] + +-- we particularly want to test the built-in XML parser here, not lxp.lom +local function parse (str) + return xml.parse(str,false,true) +end + +local d = parse(joburg) + + +function match(t,xpect) + local res,ret = d:match(t) + asserteq(res,xpect,0,1) ---> note extra level, so we report on calls to this function! +end + +t1 = [[ + <weather> + <current_conditions> + <condition data='$condition'/> + <temp_c data='$temp'/> + </current_conditions> + </weather> +]] + + + +match(t1,{ + condition = "Clear", + temp = "24", +} ) + +t2 = [[ + <weather> + {{<forecast_conditions> + <day_of_week data='$day'/> + <low data='$low'/> + <high data='$high'/> + <condition data='$condition'/> + </forecast_conditions>}} + </weather> +]] + +local conditions = { + { + low = "60", + high = "89", + day = "Sat", + condition = "Clear", + }, + { + low = "53", + high = "86", + day = "Sun", + condition = "Clear", + }, + { + low = "57", + high = "87", + day = "Mon", + condition = "Clear", + }, + { + low = "60", + high = "84", + day = "Tue", + condition = "Clear", + } +} + +match(t2,conditions) + + +config = [[ +<config> + <alpha>1.3</alpha> + <beta>10</beta> + <name>bozo</name> +</config> +]] +d,err = parse(config) +if not d then print(err); os.exit(1) end + +-- can match against wildcard tag names (end with -) +-- can be names +match([[ +<config> + {{<key->$value</key->}} +</config> +]],{ + {key="alpha", value = "1.3"}, + {key="beta", value = "10"}, + {key="name",value = "bozo"}, +}) +-- can be numerical indices +match([[ +<config> + {{<1->$2</1->}} +</config> +]],{ + {"alpha","1.3"}, + {"beta","10"}, + {"name","bozo"}, +}) +-- _ is special; means 'this value is key of captured table' +match([[ +<config> + {{<_->$1</_->}} +</config> +]],{ + alpha = {"1.3"}, + beta = {"10"}, + name = {"bozo"}, +}) + +-- the numerical index 0 is special: a capture of {[0]=val} becomes simply the value val +match([[ +<config> + {{<_->$0</_->}} +</config> +]],{ + alpha = "1.3", + name = "bozo", + beta = "10" +}) + +-- this can of course also work with attributes, but then we don't want to collapse! + +config = [[ +<config> + <alpha type='number'>1.3</alpha> + <beta type='number'>10</beta> + <name type='string'>bozo</name> +</config> +]] +d,err = parse(config) +if not d then print(err); os.exit(1) end + +match([[ +<config> + {{<_- type='$1'>$2</_->}} +</config> +]],{ + alpha = {"number","1.3"}, + beta = {"number","10"}, + name = {"string","bozo"}, +}) + +d,err = parse [[ + +<configuremap> + <configure name="NAME" value="ImageMagick"/> + <configure name="LIB_VERSION" value="0x651"/> + <configure name="LIB_VERSION_NUMBER" value="6,5,1,3"/> + <configure name="RELEASE_DATE" value="2009-05-01"/> + <configure name="VERSION" value="6.5.1"/> + <configure name="CC" value="vs7"/> + <configure name="HOST" value="windows-unknown-linux-gnu"/> + <configure name="DELEGATES" value="bzlib freetype jpeg jp2 lcms png tiff x11 xml wmf zlib"/> + <configure name="COPYRIGHT" value="Copyright (C) 1999-2009 ImageMagick Studio LLC"/> + <configure name="WEBSITE" value="http://www.imagemagick.org"/> + +</configuremap> +]] +if not d then print(err); os.exit(1) end +--xml.debug = true + +res,err = d:match [[ +<configuremap> + {{<configure name="$_" value="$0"/>}} +</configuremap> +]] + +asserteq(res,{ + HOST = "windows-unknown-linux-gnu", + COPYRIGHT = "Copyright (C) 1999-2009 ImageMagick Studio LLC", + NAME = "ImageMagick", + LIB_VERSION = "0x651", + VERSION = "6.5.1", + RELEASE_DATE = "2009-05-01", + WEBSITE = "http://www.imagemagick.org", + LIB_VERSION_NUMBER = "6,5,1,3", + CC = "vs7", + DELEGATES = "bzlib freetype jpeg jp2 lcms png tiff x11 xml wmf zlib" +}) + +-- short excerpt from +-- /usr/share/mobile-broadband-provider-info/serviceproviders.xml + +d = parse [[ +<serviceproviders format="2.0"> +<country code="za"> + <provider> + <name>Cell-c</name> + <gsm> + <network-id mcc="655" mnc="07"/> + <apn value="internet"> + <username>Cellcis</username> + <dns>196.7.0.138</dns> + <dns>196.7.142.132</dns> + </apn> + </gsm> + </provider> + <provider> + <name>MTN</name> + <gsm> + <network-id mcc="655" mnc="10"/> + <apn value="internet"> + <dns>196.11.240.241</dns> + <dns>209.212.97.1</dns> + </apn> + </gsm> + </provider> + <provider> + <name>Vodacom</name> + <gsm> + <network-id mcc="655" mnc="01"/> + <apn value="internet"> + <dns>196.207.40.165</dns> + <dns>196.43.46.190</dns> + </apn> + <apn value="unrestricted"> + <name>Unrestricted</name> + <dns>196.207.32.69</dns> + <dns>196.43.45.190</dns> + </apn> + </gsm> + </provider> + <provider> + <name>Virgin Mobile</name> + <gsm> + <apn value="vdata"> + <dns>196.7.0.138</dns> + <dns>196.7.142.132</dns> + </apn> + </gsm> + </provider> +</country> + +</serviceproviders> +]] + +res = d:match [[ + <serviceproviders> + {{<country code="$_"> + {{<provider> + <name>$0</name> + </provider>}} + </country>}} + </serviceproviders> +]] + +asserteq(res,{ + za = { + "Cell-c", + "MTN", + "Vodacom", + "Virgin Mobile" + } +}) + +res = d:match [[ +<serviceproviders> + <country code="$country"> + <provider> + <name>$name</name> + <gsm> + <apn value="$apn"> + <dns>196.43.46.190</dns> + </apn> + </gsm> + </provider> + </country> +</serviceproviders> +]] + +asserteq(res,{ + name = "Vodacom", + country = "za", + apn = "internet" +}) + +d = parse[[ +<!DOCTYPE xml> +<params> +<param> + <name>XXX</name> + <value></value> +</param> +<param> + <name>YYY</name> + <value>1</value> +</param> +</params> +]] + +match([[ +<params> +{{<param> + <name>$_</name> + <value>$0</value> +</param>}} +</params> +]],{XXX = '',YYY = '1'}) + + +-- can always use xmlification to generate your templates... + +local SP, country, provider, gsm, apn, dns = xml.tags 'serviceprovider, country, provider, gsm, apn, dns' + +t = SP{country{code="$country",provider{ + name '$name', gsm{apn {value="$apn",dns '196.43.46.190'}} + }}} + +out = xml.tostring(t,' ',' ') +asserteq(out,[[ + + <serviceprovider> + <country code='$country'> + <provider> + <name>$name</name> + <gsm> + <apn value='$apn'> + <dns>196.43.46.190</dns> + </apn> + </gsm> + </provider> + </country> + </serviceprovider>]]) + +----- HTML is a degenerate form of XML ;) +-- attribute values don't need to be quoted, tags are case insensitive, +-- and some are treated as self-closing + +doc = xml.parsehtml [[ +<BODY a=1> +Hello dolly<br> +HTML is <b>slack</b><br> +</BODY> +]] + +asserteq(xml.tostring(doc),[[ +<body a='1'> +Hello dolly<br/> +HTML is <b>slack</b><br/></body>]]) + +doc = xml.parsehtml [[ +<!DOCTYPE html> +<html lang=en> +<head><!--head man--> +</head> +<body> +</body> +</html> +]] + +asserteq(xml.tostring(doc),"<html lang='en'><head/><body/></html>") + +-- note that HTML mode currently barfs if there isn't whitespace around things +-- like '<' and '>' in scripts. +doc = xml.parsehtml [[ +<html> +<head> +<script>function less(a,b) { return a < b; }</script> +</head> +<body> +<h2>hello dammit</h2> +</body> +</html> +]] + +script = doc:get_elements_with_name 'script' +asserteq(script[1]:get_text(), 'function less(a,b) { return a < b; }') + + +-- test attribute order + +local test_attrlist = xml.new('AttrList',{ + Attr3="Value3", + ['Attr1'] = "Value1", + ['Attr2'] = "Value2", + [1] = 'Attr1', [2] = 'Attr2', [3] = 'Attr3' +}) +asserteq( +xml.tostring(test_attrlist), +"<AttrList Attr1='Value1' Attr2='Value2' Attr3='Value3'/>" +) + + +-- commments +str = [[ +<hello> +<!-- any <i>momentous</i> stuff here --> +dolly +</hello> +]] +doc = parse(str) +asserteq(xml.tostring(doc),[[ +<hello> +dolly +</hello>]]) + + +-- underscores and dashes in attributes + +str = [[ +<hello> + <tag my_attribute='my_value'>dolly</tag> +</hello> +]] +doc = parse(str) + +print(doc) +print(xml.tostring(doc)) + +asserteq(xml.tostring(doc),[[ +<hello><tag my_attribute='my_value'>dolly</tag></hello>]]) + + diff --git a/Runtime/FileSystem/FileWatcher.cpp b/Editor/FileSystem/FileWatcher.cpp index 7095253..443e0ac 100644 --- a/Runtime/FileSystem/FileWatcher.cpp +++ b/Editor/FileSystem/FileWatcher.cpp @@ -1,3 +1,3 @@ -#include "FileWatcher.h"
-
-
+#include "FileWatcher.h" + + diff --git a/Runtime/FileSystem/FileWatcher.h b/Editor/FileSystem/FileWatcher.h index 4626c0a..528fbe5 100644 --- a/Runtime/FileSystem/FileWatcher.h +++ b/Editor/FileSystem/FileWatcher.h @@ -1,16 +1,16 @@ -#pragma once
-
-#include <string>
-#include "Runtime/Threading/Thread.h"
-
-// 检查某个指定文件夹中的文件或子文件夹是否被变动过。
-class FileWatcher : public Thread
-{
-public:
-
-
-private:
- std::string m_WorkingDirectory;
-
-
+#pragma once + +#include <string> +#include "Runtime/Threading/Thread.h" + +// 检查某个指定文件夹中的文件或子文件夹是否被变动过。 +class FileWatcher : public Thread +{ +public: + + +private: + std::string m_WorkingDirectory; + + };
\ No newline at end of file diff --git a/Editor/GUI/GUIWindow.cpp b/Editor/GUI/GUIWindow.cpp index 13249ac..fddc68d 100644 --- a/Editor/GUI/GUIWindow.cpp +++ b/Editor/GUI/GUIWindow.cpp @@ -2,6 +2,7 @@ #include "WinUtils.h" #include "Runtime/Graphics/OpenGL.h" #include "Editor/Graphics/Graphics.h" +#include "Editor/Win/Win.h" static bool RedirectMouseWheel(HWND window, WPARAM wParam, LPARAM lParam) { diff --git a/Editor/GUI/WinUtils.h b/Editor/GUI/WinUtils.h index 9257d77..bef6420 100644 --- a/Editor/GUI/WinUtils.h +++ b/Editor/GUI/WinUtils.h @@ -5,7 +5,7 @@ namespace winutils
{
- HINSTANCE GetInstanceHandle();
+ HINSTANCE GetInstanceHandle();
// 注册windows窗口类
ATOM RegisterWindowClass(const wchar_t* className, WNDPROC windowProc, unsigned int style);
diff --git a/Editor/Win/Win.cpp b/Editor/Win/Win.cpp index f6310e2..1081492 100644 --- a/Editor/Win/Win.cpp +++ b/Editor/Win/Win.cpp @@ -3,6 +3,17 @@ namespace Win { + static HINSTANCE s_InstanceHandle = NULL; + + HINSTANCE GetInstanceHandle() + { + if (s_InstanceHandle == NULL) + { + s_InstanceHandle = GetModuleHandle(NULL); + } + return s_InstanceHandle; + } + std::string GetCurrentWorkingDirectory() { char path[MAX_PATH]; @@ -15,4 +26,58 @@ namespace Win SetDllDirectory(path.c_str()); } + //https://stackoverflow.com/questions/59608898/how-to-get-the-state-of-the-cursor + //https://iwoohaha.tistory.com/archive/20080917 + void SetCursor(ECursor cursor) + { + LPCSTR name = IDC_ARROW; + switch (cursor) + { + case Win::Cursor_AppStarting: + name = IDC_APPSTARTING; + break; + case Win::Cursor_Arrow: + name = IDC_ARROW; + break; + case Win::Cursor_Cross: + name = IDC_CROSS; + break; + case Win::Cursor_Help: + name = IDC_HELP; + break; + case Win::Cursor_IBeam: + name = IDC_IBEAM; + break; + case Win::Cursor_NO: + name = IDC_NO; + break; + case Win::Cursor_SizeAll: + name = IDC_SIZEALL; + break; + case Win::Cursor_SizeNESW: + name = IDC_SIZENESW; + break; + case Win::Cursor_SizeNS: + name = IDC_SIZENS; + break; + case Win::Cursor_SizeNWSE: + name = IDC_SIZENWSE; + break; + case Win::Cursor_SizeWE: + name = IDC_SIZEWE; + break; + case Win::Cursor_UpArrow: + name = IDC_UPARROW; + break; + case Win::Cursor_Wait: + name = IDC_WAIT; + break; + default: + name = IDC_ARROW; + break; + } + HCURSOR hCursor = LoadCursorA(NULL, name); + ::SetCursor(hCursor); + } + }
\ No newline at end of file diff --git a/Editor/Win/Win.h b/Editor/Win/Win.h index 8d7d85d..a60cb83 100644 --- a/Editor/Win/Win.h +++ b/Editor/Win/Win.h @@ -8,6 +8,10 @@ namespace Win { + // 拿到当前程序的handle + HINSTANCE GetInstanceHandle(); + + // 当前工作目录 std::string GetCurrentWorkingDirectory(); void SetDllSearchDirectory(std::string path); @@ -18,4 +22,23 @@ namespace Win // 当前的执行是否在主线程 bool IsInMainThread(); -} + // 鼠标光标设置 + enum ECursor + { + Cursor_AppStarting, // 标准的箭头和小沙漏 + Cursor_Arrow, // 标准的箭头 + Cursor_Cross, // 十字光标 + Cursor_Help, // 标准的箭头和问号 + Cursor_IBeam, // 工字光标 + Cursor_NO, // 禁止圈 + Cursor_SizeAll, // 四向箭头指向东、西、南、北 + Cursor_SizeNESW, // 双箭头指向东北和西南 + Cursor_SizeNS, // 双箭头指向南北 + Cursor_SizeNWSE, // 双箭头指向西北和东南 + Cursor_SizeWE, // 双箭头指向东西 + Cursor_UpArrow, // 垂直箭头 + Cursor_Wait, // 沙漏 + }; + void SetCursor(ECursor cursor); + +}
\ No newline at end of file diff --git a/Projects/VisualStudio/Editor/Editor.vcxproj b/Projects/VisualStudio/Editor/Editor.vcxproj index 4c93fd7..b8c107f 100644 --- a/Projects/VisualStudio/Editor/Editor.vcxproj +++ b/Projects/VisualStudio/Editor/Editor.vcxproj @@ -146,6 +146,7 @@ <ClCompile Include="..\..\..\Editor\EditorApplication.cpp" />
<ClCompile Include="..\..\..\Editor\EditorMain.cpp" />
<ClCompile Include="..\..\..\Editor\EditorManager.cpp" />
+ <ClCompile Include="..\..\..\Editor\FileSystem\FileWatcher.cpp" />
<ClCompile Include="..\..\..\Editor\Graphics\Graphics.cpp" />
<ClCompile Include="..\..\..\Editor\GUI\ContainerWindow.cpp" />
<ClCompile Include="..\..\..\Editor\GUI\Dock.cpp" />
@@ -237,6 +238,7 @@ <ItemGroup>
<ClInclude Include="..\..\..\Editor\EditorApplication.h" />
<ClInclude Include="..\..\..\Editor\EditorManager.h" />
+ <ClInclude Include="..\..\..\Editor\FileSystem\FileWatcher.h" />
<ClInclude Include="..\..\..\Editor\Graphics\Graphics.h" />
<ClInclude Include="..\..\..\Editor\GUI\Dock.h" />
<ClInclude Include="..\..\..\Editor\GUI\EditorWindows.h" />
diff --git a/Projects/VisualStudio/Editor/Editor.vcxproj.filters b/Projects/VisualStudio/Editor/Editor.vcxproj.filters index 5323a7c..202f3e5 100644 --- a/Projects/VisualStudio/Editor/Editor.vcxproj.filters +++ b/Projects/VisualStudio/Editor/Editor.vcxproj.filters @@ -109,6 +109,9 @@ <Filter Include="Runtime\Scripting\Resource">
<UniqueIdentifier>{5579c035-9fb8-4c27-9d3a-6435e23e07be}</UniqueIdentifier>
</Filter>
+ <Filter Include="Editor\FileSystem">
+ <UniqueIdentifier>{2e2ecfca-fbaf-4761-8ea6-e7b2ab8a5b11}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\Editor\GUI\Dock.cpp">
@@ -381,6 +384,9 @@ <ClCompile Include="..\..\..\Runtime\FileSystem\FileWatcher.cpp">
<Filter>Runtime\FileSystem</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\Editor\FileSystem\FileWatcher.cpp">
+ <Filter>Editor\FileSystem</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\Editor\GUI\Dock.h">
@@ -653,6 +659,9 @@ <ClInclude Include="..\..\..\Runtime\FileSystem\FileWatcher.h">
<Filter>Runtime\FileSystem</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\Editor\FileSystem\FileWatcher.h">
+ <Filter>Editor\FileSystem</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\Runtime\Lua\LuaBind\LuaBindClass.inc">
diff --git a/Runtime/Graphics/DefaultVertexLayout.cpp b/Runtime/Graphics/DefaultVertexLayout.cpp index 0781c2e..a468100 100644 --- a/Runtime/Graphics/DefaultVertexLayout.cpp +++ b/Runtime/Graphics/DefaultVertexLayout.cpp @@ -30,8 +30,8 @@ namespace VertexLayout if (attr == VertexAttr_Color) return true; /* - if (format == VertexAttrFormat_Color || format == VertexAttrFormat_Byte) - return true; + if (format == VertexAttrFormat_Color || format == VertexAttrFormat_Byte) + return true; */ return false; } diff --git a/Runtime/Graphics/DefaultVertexLayout.h b/Runtime/Graphics/DefaultVertexLayout.h index fbb1f76..be5437c 100644 --- a/Runtime/Graphics/DefaultVertexLayout.h +++ b/Runtime/Graphics/DefaultVertexLayout.h @@ -7,7 +7,7 @@ #include "GPUDataBuffer.h" #include "VertexAttribute.h" -// 默认的顶点布局,适用于Mesh导入的结果,保持shader的一致性 +// 默认的顶点布局,适用于Mesh导入的结果,以保持shader的一致性 // 如果需要修改布局,比如编辑器UI中,用CustomVertexLayout // 默认的顶点属性以及顺序 @@ -36,16 +36,16 @@ struct DefaultVertexLayout namespace VertexLayout { // ibo无论是default还是custom布局都是short - extern uint GetDefaultIndexSize(); - extern GLenum GetDefaultIndexFormat(); - - extern uint32 GetDynamicChunkStride(uint32 vertexAttrMask); - extern bool IsGLVertexAttrNeedNormalized(uint attr); - extern uint GetDefaultShaderChannelFormat(uint attr); - extern uint32 GetDefaultVertexAttrSize(int attr); - extern uint GetDefaultVertexAttrDimension(uint attr); - extern uint GetDefaultShaderChannelDimension(uint attr); - extern GLenum GetDefaultVertexAttrComponentType(uint attr); + uint GetDefaultIndexSize(); + GLenum GetDefaultIndexFormat(); + + uint32 GetDynamicChunkStride(uint32 vertexAttrMask); + bool IsGLVertexAttrNeedNormalized(uint attr); + uint GetDefaultShaderChannelFormat(uint attr); + uint32 GetDefaultVertexAttrSize(int attr); + uint GetDefaultVertexAttrDimension(uint attr); + uint GetDefaultShaderChannelDimension(uint attr); + GLenum GetDefaultVertexAttrComponentType(uint attr); void SetupDefaultVertexLayout(const DefaultVertexLayout& info); void InvalidateVertexInputCache(); diff --git a/Runtime/Graphics/Texture.cpp b/Runtime/Graphics/Texture.cpp index 1fdc0ca..6b682e9 100644 --- a/Runtime/Graphics/Texture.cpp +++ b/Runtime/Graphics/Texture.cpp @@ -3,44 +3,66 @@ using namespace LuaBind;
+
+Texture::Texture(TextureSetting setting, ImageData* imgData)
+ : NativeClass<Texture>()
+{
+ Init(setting, imgData);
+ // force not keep imgData
+ m_KeepPixelData = false;
+}
+
Texture::Texture(LuaBind::VM* vm, TextureSetting setting, ImageData* imgData)
: NativeClass<Texture>(vm)
{
- m_Width = imgData->width;
- m_Height = imgData->height;
- m_Type = setting.type;
- m_Format = setting.format;
- m_WrapMode = setting.wrapMode;
- m_FilterMode = setting.filterMode;
- m_KeepPixelData = setting.keepImageData;
+ Init(setting, imgData);
+ // keep image data ?
+ if (m_KeepPixelData)
+ {
+ LuaBind::State state = vm->GetCurThread();
+ imgData->PushUserdata(state);
+ SetMemberRef(state, m_ImageData, -1);
+ state.Pop(1);
+ }
+}
- glGenTextures(1, &m_GPUID);
- glBindTexture(GL_TEXTURE_2D, m_GPUID);
+void Texture::Init(TextureSetting setting, ImageData* imgData)
+{
+ m_Width = imgData->width;
+ m_Height = imgData->height;
+ m_Type = setting.type;
+ m_Format = setting.format;
+ m_WrapMode = setting.wrapMode;
+ m_FilterMode = setting.filterMode;
+ m_KeepPixelData = setting.keepImageData;
+
+ glGenTextures(1, &m_GPUID);
+ glBindTexture(GL_TEXTURE_2D, m_GPUID);
CheckGLError(
glDeleteTextures(1, &m_GPUID);
glBindTexture(GL_TEXTURE_2D, 0);
throw TextureException(error);
);
-
- switch (m_WrapMode) {
- case ETextureWrapMode::Clamp:
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- break;
- case ETextureWrapMode::Repeat:
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- break;
- case ETextureWrapMode::Mirror:
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
- break;
- default:
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- break;
- }
+
+ switch (m_WrapMode) {
+ case ETextureWrapMode::Clamp:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ break;
+ case ETextureWrapMode::Repeat:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ break;
+ case ETextureWrapMode::Mirror:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
+ break;
+ default:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ break;
+ }
CheckGLError(
glDeleteTextures(1, &m_GPUID);
@@ -48,37 +70,28 @@ Texture::Texture(LuaBind::VM* vm, TextureSetting setting, ImageData* imgData) throw TextureException(error);
);
- switch (m_FilterMode) {
- case ETextureFilterMode::Linear:
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- break;
- case ETextureFilterMode::Nearest:
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- break;
- default:
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- break;
- }
+ switch (m_FilterMode) {
+ case ETextureFilterMode::Linear:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ break;
+ case ETextureFilterMode::Nearest:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ break;
+ default:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ break;
+ }
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imgData->width, imgData->height, 0, GL_RGB, GL_UNSIGNED_BYTE, imgData->pixels);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imgData->width, imgData->height, 0, GL_RGB, GL_UNSIGNED_BYTE, imgData->pixels);
CheckGLError(
glDeleteTextures(1, &m_GPUID);
glBindTexture(GL_TEXTURE_2D, 0);
throw TextureException(error);
);
-
- // keep image data ?
- if (m_KeepPixelData)
- {
- LuaBind::State state = vm->GetCurThread();
- imgData->PushUserdata(state);
- SetMemberRef(state, m_ImageData, -1);
- state.Pop(1);
- }
}
Texture::~Texture()
diff --git a/Runtime/Graphics/Texture.h b/Runtime/Graphics/Texture.h index 1aa8bdf..d02634c 100644 --- a/Runtime/Graphics/Texture.h +++ b/Runtime/Graphics/Texture.h @@ -61,6 +61,7 @@ public: class Texture : public LuaBind::NativeClass<Texture> { public: + Texture(TextureSetting setting, ImageData* imgData)/*throw TextureException*/; Texture(LuaBind::VM* vm, TextureSetting setting, ImageData* imgData)/*throw TextureException*/; ~Texture(); @@ -69,7 +70,9 @@ public: GET(GLuint, GpuID, m_GPUID); -protected: +private: + void Init(TextureSetting setting, ImageData* imgData); + GLuint m_GPUID; int m_Width, m_Height; diff --git a/Runtime/Utilities/IncrementalTask.h b/Runtime/Utilities/IncrementalTask.h index 29163d9..741e55a 100644 --- a/Runtime/Utilities/IncrementalTask.h +++ b/Runtime/Utilities/IncrementalTask.h @@ -7,7 +7,7 @@ public: IncrementalTask() {};
virtual ~IncrementalTask() {};
- virtual void OnStep() = 0;
+ virtual void Execute() = 0;
virtual bool IsDone() = 0;
};
|