aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBaptiste Daroussin <bapt@FreeBSD.org>2025-06-26 07:09:58 +0000
committerBaptiste Daroussin <bapt@FreeBSD.org>2025-06-26 07:09:58 +0000
commit95286bbb7907e5ae9f7971e09072d1805f50cd9b (patch)
tree1375c664480c303c38f138fa8fb2725a0393d4f7
-rw-r--r--.gitignore9
-rw-r--r--.luacov8
-rw-r--r--AUTHORS6
-rw-r--r--LICENSE27
-rw-r--r--NEWS.md352
-rw-r--r--README.md232
-rw-r--r--build-aux/config.ld.in34
-rwxr-xr-xbuild-aux/luke672
-rw-r--r--doc/index.html97
-rw-r--r--doc/ldoc.css303
-rw-r--r--doc/modules/lyaml.explicit.html267
-rw-r--r--doc/modules/lyaml.functional.html236
-rw-r--r--doc/modules/lyaml.html224
-rw-r--r--doc/modules/lyaml.implicit.html533
-rw-r--r--ext/yaml/emitter.c459
-rw-r--r--ext/yaml/lyaml.h161
-rw-r--r--ext/yaml/parser.c410
-rw-r--r--ext/yaml/scanner.c340
-rw-r--r--ext/yaml/yaml.c66
-rw-r--r--lib/lyaml/explicit.lua120
-rw-r--r--lib/lyaml/functional.lua87
-rw-r--r--lib/lyaml/implicit.lua283
-rw-r--r--lib/lyaml/init.lua534
-rw-r--r--lukefile47
-rw-r--r--lyaml-6.2.8-1.rockspec59
-rw-r--r--spec/ext_yaml_emitter_spec.yaml239
-rw-r--r--spec/ext_yaml_parser_spec.yaml391
-rw-r--r--spec/ext_yaml_scanner_spec.yaml380
-rw-r--r--spec/lib_lyaml_functional_spec.yaml121
-rw-r--r--spec/lib_lyaml_spec.yaml343
-rw-r--r--spec/spec_helper.lua277
31 files changed, 7317 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000000..19d7cbb6dd7d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+*~
+*.o
+*.so
+*.src.rock
+/ChangeLog
+/build-aux/config.ld
+/luacov.*.out
+/lyaml-*.tar.gz
+/TAGS
diff --git a/.luacov b/.luacov
new file mode 100644
index 000000000000..0aa52d25e0ea
--- /dev/null
+++ b/.luacov
@@ -0,0 +1,8 @@
+modules = {
+ ['lyaml'] = 'lib/lyaml/init.lua',
+ ['lyaml.*'] = 'lib',
+}
+
+runreport = true
+
+tick = true
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 000000000000..1eb673371bf7
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,6 @@
+lyaml is the work of several authors (see git history for
+contributors).
+
+There is an earlier C LibYAML binding
+
+ Copyright Andrew Danforth <acd@weirdness.net>
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000000..402e64dfec29
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+This software comprises files that are copyright their respective
+authors (see the AUTHORS file for details), and distributed under
+the terms of the MIT license (the same license as Lua itself),
+unless noted otherwise in the body of that file.
+
+====================================================================
+Copyright (C) 2013-2022 Gary V. Vaughan
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGE-
+MENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+====================================================================
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 000000000000..32611d83671a
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,352 @@
+# lyaml NEWS - User visible changes
+
+## Noteworthy changes in release 6.2.8 (2022-10-22) [stable]
+
+### Bug fixes
+
+ - `luke` no longer crashes in `std.normalize` require loops
+ occasionally in Lua 5.4.
+
+ - lyaml emitter no longer leaks at least six bytes for every
+ map, sequence and scalar emitted.
+
+
+## Noteworthy changes in release 6.2.7 (2020-11-27) [stable]
+
+### Bug fixes
+
+ - Don't skip YAML entries from mixed key Lua tables.
+
+
+## Noteworthy changes in release 6.2.6 (2020-08-28) [stable]
+
+### Bug fixes
+
+ - `luke` really propagates `LDFLAGS` to module compilation
+ commands.
+
+
+## Noteworthy changes in release 6.2.5 (2020-04-15) [stable]
+
+### Bug fixes
+
+ - `luke` really propagates `YAML_BINDIR`, `YAML_DIR`,
+ `YAML_INCDIR` and `YAML_LIBDIR` to checksymbol test in lukefile
+ given the change to `external_dependencies` layout in 6.1.2.
+
+
+## Noteworthy changes in release 6.2.4 (2019-07-20) [stable]
+
+### Bug fixes
+
+ - `luke` works with upgraded bootstrap luarocks version of
+ `require`.
+
+
+## Noteworthy changes in release 6.2.3 (2018-09-16) [stable]
+
+### New Features
+
+ - Initial support for Lua 5.4.
+
+
+## Noteworthy changes in release 6.2.2 (2018-03-28) [stable]
+
+### Bug fixes
+
+ - Remove spurious dependency on `std.normalize` and `std._debug`
+ libraries.
+
+
+## Noteworthy changes in release 6.2.1 (2018-02-20) [stable]
+
+### Bug fixes
+
+ - `spec/spec_helper.lua` now looks in the correct objdir
+ for object modules built by luke, instead of adding unused
+ paths from old Autotools objdirs. So now specl is properly
+ running examples against the not yet installed lyaml objects.
+
+
+## Noteworthy changes in release 6.2 (2017-11-26) [stable]
+
+### Bug fixes
+
+ - `luke` uses the correct spelling of LIBFLAG to match luarocks now.
+
+ - `luke` no longer throws spurious `cp: file exists` errors.
+
+ - `luke` works on luajit again.
+
+
+## Noteworthy changes in release 6.1.3 (2017-05-29) [stable]
+
+### Bug fixes
+
+ - `luke` no longer bombs out with a nil concat error.
+
+
+## Noteworthy changes in release 6.1.2 (2017-04-30) [stable]
+
+### Bug fixes
+
+ - `luke` now propagates `LUA_DIR`, `YAML_INCDIR` and `YAML_LIBDIR`
+ correctly.
+
+
+## Noteworthy changes in release 6.1.1 (2017-01-22) [stable]
+
+### New Features
+
+ - Builds and installs with `luke` instead of Autotools.
+
+
+## Noteworthy changes in release 6.1 (2016-10-08) [stable]
+
+### Bug fixes
+
+ - `lyaml.load` now correctly reads implicit null scalars in a YAML
+ document as an `lyaml.null` reference, identical to the "~"
+ shorthand syntax, according to [the specification][nullspec].
+
+ ```yaml
+ empty:
+ canonical: ~
+ english: null
+ ~: null key
+ ```
+
+
+## Noteworthy changes in release 6.0 (2015-07-27) [stable]
+
+### New Features
+
+ - `lyaml.load` now correctly reads a !!bool tagged scalar from a
+ YAML document, or an implicit bool value, according to
+ [the specification][boolspec].
+
+ ```yaml
+ %TAG ! tag:yaml.org,2002:
+ ---
+ truthy:
+ - !bool Y
+ - !bool y
+ - !bool True
+ - !bool "on"
+ falsey:
+ - !bool n
+ - !bool OFF
+ - !bool garbage
+ ```
+
+ - `lyaml.load` now correctly reads a !!float tagged scalar from a
+ YAML document, or an implicit float value, according to
+ [the specification][floatspec].
+
+ - `lyaml.load` now correctly reads a !!int tagged scalar from a
+ YAML document, or an implicit integer value, according to
+ [the specification][intspec].
+
+ - `lyaml.load` now supports the !!merge key type according to
+ [the specification][mergespec].
+
+ ```yaml
+ - &MERGE { x: 1, y: 2 }
+ - &OVERRIDE { x: 0, z: 1 }
+ -
+ << : [&MERGE, &OVERRIDE]
+ z: 3
+ ```
+
+ The anchored tables remain in the document too, so this results in
+ the following Lua table:
+
+ ```lua
+ { -- START_STREAM
+ { -- START_DOCUMENT
+ { x = 1, y = 2 }, -- MERGE
+ { x = 0, z = 1 }, -- OVERRIDE
+ { x = 1, y = 2, z = 3}, -- <<<
+ } -- END_DOCUMENT
+ } -- END_STREAM
+ ```
+
+### Bug fixes
+
+ - Multi-line strings were previously being dumped using single quotes
+ which caused the dumped YAML to break.
+
+ For example, { foo = "a\nmultiline\nstring" } would get dumped as:
+
+ ```yaml
+ foo: 'a
+
+ multiline
+
+ string'
+ ```
+
+ Note the extra line-breaks in between each line. This also causes
+ YAML parsing to fail (since the blank lines didn't have the expected
+ indentation).
+
+ This patch fixes the dump to use the YAML literal syntax for any
+ multi-line strings so the same example gets dumped as:
+
+ ```yaml
+ foo: |-
+ a
+ multiline
+ string
+ ```
+
+ - `lyaml.load` now correctly reads the !!null tag in a YAML
+ document as an `lyaml.null` reference, identical to the "~"
+ shorthand syntax, according to [the specification][nullspec].
+
+### Incompatible Changes
+
+ - `lyaml.load` now takes a table of options as an optional second
+ argument, not a simple boolean to determine whether all documents
+ should be returned from the stream. For now, a `true` second
+ argument will be converted to the modern equivalent:
+
+ ```lua
+ lyaml.load (document, { all = true })
+ ```
+
+ - `lyaml.dump` now takes a table of options as an optional second
+ argument, not an initial table of anchors. For now, a second
+ argument without any new API keys will be converted to the modern
+ equivalent:
+
+ ```lua
+ lyaml.dump (t, { anchors = arg2 })
+ ```
+
+[boolspec]: http://yaml.org/type/bool.html
+[floatspec]: http://yaml.org/type/float.html
+[intspec]: http://yaml.org/type/int.html
+[mergespec]: http://yaml.org/type/merge.html
+[nullspec]: http://yaml.org/type/null.html
+
+
+## Noteworthy changes in release 5.1.4 (2015-01-01) [stable]
+
+ - This release is functionally identical to the last.
+
+
+## Noteworthy changes in release 5.1.3 (2015-01-01) [stable]
+
+ - This release is functionally identical to the last.
+
+
+## Noteworthy changes in release 5.1.2 (2014-12-27) [stable]
+
+### Bugs Fixed
+
+ - No more spurious .travis.yml is out of date warnings during
+ `luarocks install lyaml`.
+
+
+## Noteworthy changes in release 5.1.1 (2014-12-19) [stable]
+
+### Bugs Fixed
+
+ - When using `sudo make install` instead of LuaRocks, `lyaml.so`
+ is now correctly installed to `$luaexecdir`.
+
+
+## Noteworthy changes in release 5.1.0 (2014-12-17) [stable]
+
+### New Features
+
+ - Lua 5.3.0 compatibility.
+
+
+## Noteworthy changes in release 5 (2014-09-25) [beta]
+
+### Build
+
+ - Significantly reduced pointer mismatch warnings from modern GNU
+ compilers.
+
+### New Features
+
+ - `lyaml.dump` now takes a second argument containing a table of
+ potential anchor values in `ANCHOR_NAME = { "match", "elements" }`
+ pairs format. The first time any are matched in the table being
+ dumped, they are preceded by `&ANCHOR_NAME` in the output YAML
+ document; subsequent matches are not written out in full, but
+ shortened to the appropriate `*ANCHOR_NAME` alias.
+
+### Bugs Fixed
+
+ - `yaml.emitter` no longer emits numbers in SINGLE_QUOTE style by
+ default.
+
+ - `yaml.emitter ().emit` returns error strings correctly for invalid
+ STREAM_START encoding, and MAPPING_START, SEQUENCE_START & SCALAR
+ style fields.
+
+
+## Noteworthy changes in release 4 (2013-09-11) [beta]
+
+### New Features
+
+ - New yaml.emitter API returns an object with an emit method for
+ adding events using yaml_*_event_initialize() calls.
+
+ - New yaml.parser API returns a Lua iterator that fetches the next
+ event using yaml_parser_parse().
+
+ - New yaml.scanner API returns a Lua iterator that fetches the next
+ token using yaml_parser_scan().
+
+ - Beginnings of Specl specs, starting with a reasonably comprehensive
+ specifications for the new APIs above.
+
+ - C implementation of lyaml.dump has moved to Lua implementation as
+ yaml.dump.
+
+ - C implementation of lyaml.load has moved to Lua implementation as
+ yaml.load.
+
+ - The new Lua implementation of lyaml.load () handles multi-document
+ streams, and returns a table of documents when the new second
+ argument is `true`.
+
+
+## Noteworthy changes in release 3 (2013-04-27) [beta]
+
+ - This release is functionally identical to the last.
+
+### New Features
+
+ - lyaml builds are now made against Lua 5.1, Lua 5.2 and luajit 2.0.0
+ automatically, with every commit.
+
+ - move to a cleaner, automated release system.
+
+
+## Noteworthy changes in release 2 (2013-03-18) [beta]
+
+ - This release is functionally identical to the last.
+
+ - Use correct MIT license attribution, relicensing build files to match
+ Andrew Danforth''s MIT licensed lyaml.c too.
+
+
+## Noteworthy changes in release 1 (2013-03-17) [beta]
+
+### New Features
+
+ - A binding for libYAML, by Andrew Danforth: Updated for Lua 5.1 and
+ 5.2, and packaged as a luarock.
+
+ - I spun this out of Specl (http://github.com/gvvaughan/specl) so that
+ other projects may use it, and to simplify the Specl build.
+
+### Known Issues
+
+ - There's not really any documentation, sorry. Contributions welcome!
diff --git a/README.md b/README.md
new file mode 100644
index 000000000000..d1e96845ddfa
--- /dev/null
+++ b/README.md
@@ -0,0 +1,232 @@
+LYAML
+=====
+
+Copyright (C) 2013-2022 Gary V. Vaughan
+
+[![License](https://img.shields.io/:license-mit-blue.svg)](https://mit-license.org)
+[![workflow status](https://github.com/gvvaughan/lyaml/actions/workflows/spec.yml/badge.svg?branch=release-v6.2.8)](https://github.com/gvvaughan/lyaml/actions)
+[![codecov.io](https://codecov.io/github/gvvaughan/lyaml/coverage.svg?branch=release-v6.2.8)](https://codecov.io/github/gvvaughan/lyaml?branch=release-v6.2.8)
+
+[LibYAML] binding for [Lua], with a fast C implementation
+for converting between [%YAML 1.1][yaml11] and [Lua] tables,
+and a low-level [YAML] event parser for implementing more
+intricate [YAML] document loading.
+
+Usage
+-----
+
+### High Level API
+
+These functions quickly convert back and forth between Lua tables
+and [%YAML 1.1][yaml11] format strings.
+
+```lua
+local lyaml = require "lyaml"
+local t = lyaml.load (YAML-STRING, [OPTS-TABLE])
+local yamlstr = lyaml.dump (LUA-TABLE, [OPTS-TABLE])
+local null = lyaml.null ()
+```
+
+#### `lyaml.load`
+
+`lyaml.load` accepts a YAML string for parsing. If the YAML string contains
+multiple documents, only the first document will be returned by default. To
+return multiple documents as a table, set `all = true` in the second
+argument OPTS-TABLE.
+
+```lua
+lyaml.load("foo: bar")
+--> { foo = "bar" }
+
+lyaml.load("foo: bar", { all = true })
+--> { { foo = "bar" } }
+
+multi_doc_yaml = [[
+---
+one
+...
+---
+two
+...
+]]
+
+lyaml.load(multi_doc_yaml)
+--> "one"
+
+lyaml.load(multi_doc_yaml, { all = true })
+--> { "one", "two" }
+```
+
+You can supply an alternative function for converting implicit plain
+scalar values in the `implicit_scalar` field of the OPTS-TABLE argument;
+otherwise a default is composed from the functions in the `lyaml.implicit`
+module.
+
+You can also supply an alternative table for coverting explicitly tagged
+scalar values in the `explicit_scalar` field of the OPTS-TABLE argument;
+otherwise all supported tags are parsed by default using the functions
+from the `lyaml.explicit` module.
+
+#### `lyaml.dump`
+
+`lyaml.dump` accepts a table of values to dump. Each value in the table
+represents a single YAML document. To dump a table of lua values this means
+the table must be wrapped in another table (the outer table represents the
+YAML documents, the inner table is the single document table to dump).
+
+```lua
+lyaml.dump({ { foo = "bar" } })
+--> ---
+--> foo: bar
+--> ...
+
+lyaml.dump({ "one", "two" })
+--> --- one
+--> ...
+--> --- two
+--> ...
+```
+
+If you need to round-trip load a dumped document, and you used a custom
+function for converting implicit scalars, then you should pass that same
+function in the `implicit_scalar` field of the OPTS-TABLE argument to
+`lyaml.dump` so that it can quote strings that might otherwise be
+implicitly converted on reload.
+
+#### Nil Values
+
+[Lua] tables treat `nil` valued keys as if they were not there,
+where [YAML] explicitly supports `null` values (and keys!). Lyaml
+will retain [YAML] `null` values as `lyaml.null ()` by default,
+though it is straight forward to wrap the low level APIs to use `nil`,
+subject to the usual caveats of how nil values work in [Lua] tables.
+
+
+### Low Level APIs
+
+```lua
+local emitter = require ("yaml").emitter ()
+
+emitter.emit {type = "STREAM_START"}
+for _, event in ipairs (event_list) do
+ emitter.emit (event)
+end
+str = emitter.emit {type = "STREAM_END"}
+```
+
+The `yaml.emitter` function returns an emitter object that has a
+single emit function, which you call with event tables, the last
+`STREAM_END` event returns a string formatted as a [YAML 1.1][yaml11]
+document.
+
+```lua
+local iter = require ("yaml").scanner (YAML-STRING)
+
+for token_table in iter () do
+ -- process token table
+end
+```
+
+Each time the iterator returned by `scanner` is called, it returns
+a table describing the next token of YAML-STRING. See LibYAML's
+[yaml.h] for details of the contents and semantics of the various
+tokens produced by `yaml_parser_scan`, the underlying call made by
+the iterator.
+
+[LibYAML] implements a fast parser in C using `yaml_parser_scan`, which
+is also bound to lyaml, and easier to use than the token API above:
+
+```lua
+local iter = require ("yaml").parser (YAML-STRING)
+
+for event_table in iter () do
+ -- process event table
+end
+```
+
+Each time the iterator returned by `parser` is called, it returns
+a table describing the next event from the "Parse" process of the
+"Parse, Compose, Construct" processing model described in the
+[YAML 1.1][yaml11] specification using [LibYAML].
+
+Implementing the remaining "Compose" and "Construct" processes in
+[Lua] is left as an exercise for the reader -- though, unlike the
+high-level API, `lyaml.parser` exposes all details of the input
+stream events, such as line and column numbers.
+
+
+Installation
+------------
+
+There's no need to download an [lyaml] release, or clone the git repo,
+unless you want to modify the code. If you use [LuaRocks], you can
+use it to install the latest release from its repository:
+
+ luarocks --server=http://rocks.moonscript.org install lyaml
+
+Or from the rockspec in a release tarball:
+
+ luarocks make lyaml-?-1.rockspec
+
+To install current git master from [GitHub][lyaml] (for testing):
+
+ luarocks install http://raw.github.com/gvvaughan/lyaml/master/lyaml-git-1.rockspec
+
+To install without [LuaRocks], clone the sources from the
+[repository][lyaml], and then run the following commands:
+
+```sh
+cd lyaml
+build-aux/luke LYAML_DIR=LIBYAML-INSTALL-PREFIX
+sudo build-aux/luke PREFIX=LYAML-INSTALL-PREFIX install
+specl -v1freport spec/*_spec.yaml
+```
+
+The dependencies are listed in the dependencies entry of the file
+[rockspec][L15].
+
+
+Bug reports and code contributions
+----------------------------------
+
+This library is maintained by its users.
+
+Please make bug reports and suggestions as [GitHub Issues][issues].
+Pull requests are especially appreciated.
+
+But first, please check that your issue has not already been reported by
+someone else, and that it is not already fixed by [master][lyaml] in
+preparation for the next release (see Installation section above for how
+to temporarily install master with [LuaRocks][]).
+
+There is no strict coding style, but please bear in mind the following
+points when proposing changes:
+
+0. Follow existing code. There are a lot of useful patterns and avoided
+ traps there.
+
+1. 3-character indentation using SPACES in Lua sources: It makes rogue
+ TABs easier to see, and lines up nicely with 'if' and 'end' keywords.
+
+2. Simple strings are easiest to type using single-quote delimiters,
+ saving double-quotes for where a string contains apostrophes.
+
+3. Save horizontal space by only using SPACEs where the parser requires
+ them.
+
+4. Use vertical space to separate out compound statements to help the
+ coverage reports discover untested lines.
+
+5. Prefer explicit string function calls over object methods, to mitigate
+ issues with monkey-patching in caller environment.
+
+
+[issues]: http://github.com/gvvaughas/lyaml/issues
+[libyaml]: http://pyyaml.org/wiki/LibYAML
+[lua]: http://www.lua.org
+[luarocks]: http://www.luarocks.org
+[lyaml]: http://github.com/gvvaughan/lyaml
+[L15]: http://github.com/gvvaughan/lyaml/blob/master/lyaml-git-1.rockspec#L15
+[yaml.h]: http://pyyaml.org/browser/libyaml/branches/stable/include/yaml.h
+[yaml]: http://yaml.org
+[yaml11]: http://yaml.org/spec/1.1/
diff --git a/build-aux/config.ld.in b/build-aux/config.ld.in
new file mode 100644
index 000000000000..0d55b9e94c30
--- /dev/null
+++ b/build-aux/config.ld.in
@@ -0,0 +1,34 @@
+--[[
+ LYAML binding for Lua 5.1, 5.2, 5.3 & 5.4
+ Copyright (C) 2013-2022 Gary V. Vaughan
+]]
+
+title = '@package@ @version@ Reference'
+project = '@package@ @version@'
+description = [[
+# LYAML binding for Lua
+
+This is a Lua binding for the fast libYAML C library for converting
+between `%YAML 1.1` and Lua tables, with a flexible Lua language
+API to load and save YAML documents.
+
+It works with Lua 5.1 (including LuaJIT), 5.2, 5.3 and 5.4.
+
+## LICENSE
+
+The code is copyright by its respective authors, and released under the
+MIT license (the same license as Lua itself). There is no warranty.
+]]
+
+dir = '../doc'
+
+file = {
+ '../lib/lyaml/init.lua',
+ '../lib/lyaml/explicit.lua',
+ '../lib/lyaml/functional.lua',
+ '../lib/lyaml/implicit.lua',
+}
+
+format = 'markdown'
+backtick_references = false
+sort = false
diff --git a/build-aux/luke b/build-aux/luke
new file mode 100755
index 000000000000..3d61f784cbca
--- /dev/null
+++ b/build-aux/luke
@@ -0,0 +1,672 @@
+#!/usr/bin/env lua
+--[[ minified code follows, see --help text for source location! ]]
+local require=function(modname)if package.loaded[modname]==nil then
+if type(package.preload[modname])~="function"then
+io.stderr:write("module '" .. modname .. "' not found:\n no valid field package.preload['" .. modname .. "']\n")
+return nil
+end
+package.loaded[modname]=package.preload[modname](modname,"package.preload")end
+return package.loaded[modname]end
+package.preload['luke._base']=function()
+local _ENV=require'std.normalize'{}local function fatal(...)local msg=(...)if select('#',...)>1 then
+msg=format(...)end
+stderr:write('luke: fatal: '..msg..'\n')exit(1)end
+return{diagnose=function(predicate,...)if not predicate then
+fatal(...)end
+end,fatal=fatal,}
+end
+package.preload['luke.cli']=function()
+local _ENV=require'std.normalize'{'luke._base','luke.lukefile','luke.platforms','std.functional',}local function version()print[[
+luke (Luke) 0.2.3
+Written by Gary V. Vaughan <gary@gnu.org>, 2014
+
+Copyright (C) 2022, Gary V. Vaughan
+Luke comes with ABSOLUTELY NO WARRANTY.
+You may redistribute copies of Luke under the terms of the MIT license;
+it may be used for any purpose at absolutely no cost, without permission.
+See <https://mit-license.org> for details.
+]]exit(0)end
+local function help()print[[
+Usage: luke [OPTION]... [VAR=VALUE]... [TARGET]
+
+Use the source, Luke!
+
+ --help print this help, then exit
+ --version print version number, then exit
+ --file=FILE use FILE instead of lukefile
+ --value=NAME print the value of variable NAME
+ --quiet without any output
+ --verbose provide more progress output
+
+Each TARGET can be one of the module table keys from lukefile, or:
+
+ all build all targets in lukefile
+ install copy all built targets to $PREFIX
+
+If no TARGET is given, 'all' is implied.
+
+Report bugs to https://github.com/gvvaughan/luke/issues.]]exit(0)end
+local function opterr(...)local msg=(...)if select('#',...)>1 then
+msg=format(...)end
+msg=gsub(msg,'%.$','')stderr:write('luke: error: '..msg..'.\n')stderr:write("luke: try '"..arg[0].." --help' for help.\n")exit(2)end
+local function display(...)return stdout:write(concat{...})end
+local function dump(...)local s=concat(map(list(...),str))if len(s)>0 then
+gsub(concat(map(list(...),str)),'\n*$','\n'):gsub('(.-)\n',function(line)stderr:write(' DEBUG: '..line..'\n')end)end
+end
+local function interpolate_to_substitute(s)return(gsub(s,'%$([%w_]+)','@%1@'))end
+return{parse_arguments=function(args)local r={clidefs={},valreqs={},fname='lukefile',install={},log=nop,targets={},verbose=nop,write=display,}map(args,function(opt)case(opt,{['--debug']=function()r.log=dump
+end,['%-%-file=(.+)']=function(optarg)r.fname=optarg
+end,['%-%-value=(.+)']=function(optarg)r.valreqs[#r.valreqs+1]=optarg
+end,['--quiet']=function()r.write=nop
+end,['--verbose']=function()r.verbose=display
+end,['--help']=help,['--version']=version,['([^-][^=]-)=(.+)']=function(name,value)r.clidefs[name]=value
+end,function(opt)if match(opt,'^-')~=nil then
+opterr("unrecognized option '%s'",opt)end
+append(r.targets,opt)end,})end)return r
+end,validate_arguments=function(parsed)local luke,err=loadluke(parsed.fname)diagnose(luke~=nil,'bad %s: %s',parsed.fname,err)if isempty(luke.modules or{})then
+fatal("no modules table in '%s', nothing to build",parsed.fname)end
+local targets=call(function()if isempty(parsed.targets)or contains(parsed.targets,'all')then
+return except(flatten(parsed.targets,keys(luke.modules)),'all')end
+local r=filter(parsed.targets,function(target)if target~='install'and luke.modules[target]==nil then
+fatal("no rule to make target '%s'",target)end
+return true
+end)assert(len(r)>0,"no build targets specified")return r
+end)local install
+local build=pluck(targets,luke.modules)if contains(targets,'install')then
+install=build or luke.modules
+end
+luke.modules=build
+if isempty(luke.modules)then
+luke.external_dependencies=nil
+end
+luke.substitute=merge(luke.substitute or{},{package=interpolate_to_substitute(luke.package),version=interpolate_to_substitute(luke.version),})luke.variables=merge(luke.variables or{},collect_variables(luke),{LUA_DIR='/usr',LUA_BINDIR='$LUA_DIR/bin',LUA_INCDIR='$LUA_DIR/include/lua$LUAVERSION',LUA_LIBDIR='$LUA_DIR/lib',objdir=platforms[1],package=luke.package,version=luke.version,})return{clidefs=parsed.clidefs,install=install,log=parsed.log,luke=luke,valreqs=parsed.valreqs,verbose=parsed.verbose,write=parsed.write,}end,}
+end
+package.preload['luke.compile']=function()
+local _ENV=require'std.normalize'{'luke._base','luke.environment','std.functional','type.context-manager','type.path',SHELLMETACHARS='[%s%$"]',}local function spawn(env,...)local command=interpolate(env,concat({...},' '))return with(TmpFile(),TmpFile(),function(out,err)local pipe=concat{command,' >',out.filename,' 2>',err.filename,'; printf $?'}return tonumber(slurp(Pipe(pipe))),slurp(File(err.filename)),slurp(File(out.filename))end)end
+local function run(L,env,command)L.write(interpolate(env,concat(command,' ')),'\n')local status,err,out=spawn(env,unpack(command))if status~=0 then
+if L.write==nop then
+stdout:write(concat(command,' ')..'\n')end
+stderr:write(err..'\n')end
+return status,out,err
+end
+local function defines(env,deftables)return zip_with(merge({},unpack(deftables)),function(name,value)local fmt=cond({[int(value)==1]='-D%s'},{[match(value,SHELLMETACHARS)~=nil]="-D%s='%s'"},{[true]='-D%s=%s'})return format(fmt,name,value)end)end
+local function incdirs(...)return map(flatten(...),function(v)return'-I'..v
+end)end
+local function libdirs(...)return map(flatten(...),function(v)return'-L'..v
+end)end
+local function c_module_path(objdir,name)return format('%s/%s.$LIB_EXTENSION',objdir,gsub(name,'%.','/'))end
+local function c_source(module,objdir)local path=gsub(module,'%.','/')local src=c_module_path(objdir,path)return src,(gsub('$INST_LIBDIR/'..path,'/[^/]+$',''))end
+local function lua_source(module,src)local abspath='$INST_LUADIR/'..gsub(module,'%.','/')if match(src,'/init%.lua$')then
+abspath=abspath..'/init'end
+abspath=abspath..'.lua'return src,(gsub(abspath,'/[^/]+%.lua$',''))end
+local function module_to_path(module,sources,objdir)return dropuntil(sources,function(source)return case(source,{['.*%.[ch]']=bind(c_source,{module,objdir}),['(.*%.[ch])%.in']=bind(c_source,{module,objdir}),['.*%.lua']=bind(lua_source,{module}),['(.*%.lua)%.in']=bind(lua_source,{module}),function(src)fatal("unsupported source type '%s'",src)end,})end)end
+return{build_c_module=function(L,env,luke,name)local rules=luke.modules[name]local c_module=c_module_path(luke.variables.objdir,name)local command={'$MAKEDIRS',dirname(c_module)}local status,err,out=spawn(env,unpack(command))if status~=0 then
+stdout:write(concat(command,' ')..'\n')stderr:write(err..'\n')exit(status)end
+return run(L,env,flatten('$CC $CFLAGS $LIBFLAG $PKGFLAGS $CPPFLAGS',defines(env,except(list(rules.defines,luke.defines),nil)),incdirs(rules.incdirs,luke.incdirs),rules.sources,'-o',c_module,'$LDFLAGS',libdirs(rules.libdirs,luke.libdirs),'$LIBS',rules.libraries,luke.libraries))end,c_modules=function(modules)return filter(keys(modules),function(name)return dropuntil(modules[name].sources,bind(match,{[2]='%.[ch]$'}))end)end,incdirs=incdirs,install_modules=function(L,env,luke,modules)return reduce(keys(modules),0,function(status,name)if status==0 then
+local src,dir=module_to_path(name,modules[name].sources,luke.variables.objdir)if not exists(interpolate(env,dir))then
+status=run(L,env,{'$MAKEDIRS',dir})end
+if status==0 then
+status=run(L,env,{'$INSTALL',src,dir..'/'})end
+end
+return status
+end)end,libdirs=libdirs,run_command=run,spawn=spawn,}
+end
+package.preload['luke.configure']=function()
+local _ENV=require'std.normalize'{'luke._base','luke.compile','luke.environment','std.functional','type.context-manager','type.dict',CCPROGS={'cc','gcc','clang'},}local function logspawn(L,env,...)local status,err=spawn(env,...)if status~=0 and err~=''then
+L.log(err)end
+return status
+end
+local function checking(L,...)L.verbose('checking ',concat({...},' '),'... ')end
+local function found_library(L,x)if x==nil or x==''then
+L.verbose'none required'elseif isempty(x)then
+L.verbose'not supported'else
+L.verbose(x)end
+L.verbose'\n'return x
+end
+local function found_prog(L,x)L.verbose(x and'yes\n'or'no\n')return x
+end
+local function found_result(L,x)L.verbose(x==0 and'yes\n'or'no\n')return x~=0 and 0 or 1
+end
+local function bindirs(...)return map(flatten(...),function(v)return v..':'end)end
+local function compile_command(L,env,config,filename)local command=flatten('$CC','-c','$CFLAGS',incdirs(config.incdir),'$CPPFLAGS',filename)L.log(interpolate(env,concat(command,' ')))return unpack(command)end
+local function link_command(L,env,config,a_out,source,lib)local command=flatten('$CC','$CFLAGS',incdirs(config.incdir),'$CPPFLAGS','-o',a_out,source,libdirs(config.libdir),'$LDFLAGS',lib,'$libs',CONFIGENV.libs)L.log(interpolate(env,concat(command,' ')))return unpack(command)end
+local function check_executable_in_path(L,env,config,prog)local PATH=concat(bindirs(config.bindir))..getenv('PATH')local found=dropuntil(gmatch(PATH,'[^:]+'),function(path)local progpath=path..'/'..prog
+return with(File(progpath,'r'),function(h)return h and isfile(h.context)and progpath or nil
+end)end)L.log(found and'found '..found or prog..' not found')return found~=nil
+end
+local function check_header_compile(L,env,config,header,extra_hdrs)return with(CTest(),function(conftest)conftest:write(format('%s\n#include "%s"\n',extra_hdrs,header))return logspawn(L,env,compile_command(L,env,config,conftest.filename))end)end
+local function check_struct_member_compile(L,env,config,structname,member,extra_hdrs)return with(CTest(),function(conftest)conftest:write(format([[
+%s
+int main () {
+static %s aggr;
+if (sizeof aggr.%s)
+ return 0;
+return 0;
+}
+]],extra_hdrs,structname,member))return logspawn(L,env,compile_command(L,env,config,conftest.filename))end)end
+local function try_link(L,env,config,lib,symbol)return with(CTest(),TmpFile(),function(conftest,a_out)conftest:write(format([[
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char %s ();
+int main () {
+return %s ();
+}
+]],symbol,symbol))return logspawn(L,env,link_command(L,env,config,a_out.filename,conftest.filename,lib))end)end
+local function try_compile(L,env,config,headers)return with(CTest(),TmpFile(),function(conftest,a_out)conftest:write(format([[
+%s
+#if !defined %s || %s == -1
+choke me
+#endif
+int
+main()
+{
+return 0;
+}
+]],headers,config.ifdef,config.ifdef))return logspawn(L,env,link_command(L,env,config,a_out.filename,conftest.filename))end)end
+local function check_func_decl(L,env,config,fname,extra_hdrs)return with(CTest(),function(conftest)conftest:write(format([[
+%s
+int
+main()
+{
+#ifndef %s
+(void) %s;
+#endif
+return 0;
+}
+]],extra_hdrs,fname,fname))return logspawn(L,env,compile_command(L,env,config,conftest.filename))end)end
+local function check_func_link(L,env,config,fname)return with(CTest(),TmpFile(),function(conftest,a_out)conftest:write(format([[
+/* Define to an innocous variant, in case <limits.h> declares it.
+ For example, HP-UX 11i <limits,h> declares gettimeofday. */
+#define %s innocuous_%s
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with declaration below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef %s
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+char %s ();
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_%s || defined __stub__%s
+choke me
+#endif
+
+int main () {
+return %s ();
+}
+]],fname,fname,fname,fname,fname,fname,fname))return logspawn(L,env,link_command(L,env,config,a_out.filename,conftest.filename))end)end
+local function add_external_deps(env,config,prefix)if prefix~=nil then
+for k,v in next,{bindir='$%s_BINDIR',incdir='$%s_INCDIR',libdir='$%s_LIBDIR'}do
+local envvar=interpolate(env,format(v,prefix))if envvar~=''then
+config[k]=envvar
+end
+end
+end
+end
+local function format_includes(includes)return map(includes or{},function(include)return format('#include "%s"',include)end)end
+local configure=setmetatable(OrderedDict({checkprog=function(L,env,config)return dropuntil(config.progs,function(prog)checking(L,'for',prog)if found_prog(L,check_executable_in_path(L,env,config,prog))then
+return prog
+end
+end)or fatal('cannot find '..config.checkprog)end},{checkheader=function(L,env,config)checking(L,'for',config.checkheader)local extra_hdrs=concat(format_includes(config.includes),'\n')return found_result(L,check_header_compile(L,env,config,config.checkheader,extra_hdrs))end},{checkdecl=function(L,env,config)checking(L,'whether',config.checkdecl,'is declared')local extra_hdrs=concat(format_includes(config.includes),'\n')return found_result(L,check_func_decl(L,env,config,config.checkdecl,extra_hdrs))end},{checksymbol=function(L,env,config)checking(L,'for library containing',config.checksymbol)if config.ifdef~=nil then
+local headers=concat(format_includes(config.includes),'\n')if try_compile(L,env,config,headers)~=0 then
+return found_library(L,{})end
+end
+local libraries,symbol=config.libraries,config.checksymbol
+local trylibs=reduce(libraries,{''},function(r,lib)append(r,'-l'..lib)end)return dropuntil(trylibs,function(lib)if try_link(L,env,config,lib,symbol)==0 then
+if lib~=''then
+if CONFIGENV.libs~=''then
+CONFIGENV.libs=' '..CONFIGENV.libs
+end
+CONFIGENV.libs=lib..CONFIGENV.libs
+end
+return found_library(L,lib)end
+end)or call(function()L.verbose'\n'fatal("required symbol '%s' not found in any of libc, lib%s",symbol,concat(libraries,', lib'))end)end},{checkfunc=function(L,env,config)checking(L,'for',config.checkfunc)return found_result(L,check_func_link(L,env,config,config.checkfunc))end},{checkmember=function(L,env,config)checking(L,'for',config.checkmember)local extra_hdrs=concat(format_includes(config.includes),'\n')local i=find(config.checkmember,'%.')local structname=sub(config.checkmember,1,i-1)local member=sub(config.checkmember,i+1)return found_result(L,check_struct_member_compile(L,env,config,structname,member,extra_hdrs))end}),{__call=function(self,L,env,config,prefix)return case(type(config),{['number']=function()return str(config)end,['string']=function()return config
+end,['table']=function()return dropuntil(self,function(fname)if config[fname]~=nil then
+add_external_deps(env,config,prefix)return apply(self[fname],list(L,env,config))end
+end)or fatal("unable to configure with keys '%s'",concat(keys(config),"', '"))end,function(type)fatal("unsupported configure type '%s'",type)end,})end,})return{config_compiler=function(L,env)local CC=env.CC
+if CC==nil then
+CC=configure(L,env,{checkprog='C compiler',progs=CCPROGS})env=makeenv(env,{CC=CC})end
+checking(L,interpolate(env,'whether $CC works'))local cm=CTest()local works,err=with(cm,function(conftest)conftest:write('typedef int x;\n')return spawn(env,'$compile',conftest.filename)end)if works~=0 then
+L.verbose'no\n'L.log(interpolate(env,'$compile '..cm.filename))if err and err~=''then
+L.log(err)end
+fatal('could not find a working C compiler')end
+found_prog(L,CC)return env
+end,config_ldoc=function(L,env)local LDOC=env.LDOC
+if LDOC==nil then
+LDOC=configure(L,env,{checkprog='LDocs generator',progs={'ldoc','true'}})env=makeenv(env,{LDOC=LDOC})end
+return env
+end,configure=configure,}
+end
+package.preload['luke.environment']=function()
+local _ENV=require'std.normalize'{'luke.platforms','std.functional',LUAVERSION=string.gsub(_VERSION,'[^0-9%.]+',''),}local env_mt={__index=function(self,varname)return dropuntil(self,function(env)local value=env[varname]if value~=nil then
+self[varname]=value
+return value
+end
+end)end,}local function interpolate_with(pattern,env,s)local r=''while r~=s do
+r=s
+s=gsub(r,pattern,function(varname)return env[varname]or''end)end
+return r
+end
+local function isenv(t)return getmetatable(t)==env_mt
+end
+return{CONFIGENV={compile='$CC -c $CFLAGS $CPPFLAGS',libs='',link='$CC $CFLAGS $CPPFLAGS $LDFLAGS',},DEFAULTENV=filter_platforms{LUAVERSION=LUAVERSION,PREFIX='/usr/local',INST_LIBDIR='$PREFIX/lib/lua/$LUAVERSION',INST_LUADIR='$PREFIX/share/lua/$LUAVERSION',LIB_EXTENSION='so',OBJ_EXTENSION='o',INSTALL='cp',MAKEDIRS='mkdir -p',CFLAGS='-O2',platforms={macosx={LIBFLAG='-fPIC -bundle -undefined dynamic_lookup -all_load',},LIBFLAG='-shared -fPIC',},},SHELLENV=setmetatable({},{__index=function(_,v)return getenv(v)end,}),expand=bind(interpolate_with,{'@([^@]+)@'}),interpolate=bind(interpolate_with,{'%$([%w_]+)'}),makeenv=function(...)local env=reduce(except(list(...),nil),function(r,t)if isenv(t)then
+map(t,bind(append,{r}))else
+append(r,t)end
+end)return setmetatable(env,env_mt)end,}
+end
+package.preload['luke']=function()
+local _ENV=require'std.normalize'{'luke.cli','luke.compile','luke.configure','luke.environment','luke.lukefile','std.functional',}local function run_ldocs(L,env,ldocs)return run_command(L,env,flatten{'$LDOC -c',ldocs.sources,'.'})end
+local function build_modules(L,env)local conf=makeenv(CONFIGENV,env)if not isempty(L.luke.ldocs or{})then
+conf=config_ldoc(L,conf)env=makeenv(env,{LDOC=conf.LDOC})end
+local c=c_modules(L.luke.modules)if not isempty(c)then
+conf=config_compiler(L,conf)env=makeenv(env,{CC=conf.CC})end
+L.luke=run_configs(L,conf,L.luke)local substitute=makeenv(L.clidefs,L.luke.substitute,SHELLENV)L.luke=run_templates(L,substitute,L.luke)local status=dropuntil(c,isnonzero,function(name)return build_c_module(L,env,L.luke,name)end)or 0
+if status==0 and not isempty(L.luke.ldocs or{})then
+status=run_ldocs(L,env,L.luke.ldocs)end
+return status
+end
+return{main=function(args)local L=validate_arguments(parse_arguments(args))local env=makeenv(L.clidefs,L.luke.variables,DEFAULTENV,SHELLENV)local status=0
+if not isempty(L.valreqs)then
+map(L.valreqs,function(name)print(interpolate(env,concat{name,"='$",name,"'"}))end)exit(0)end
+if status==0 and not isempty(L.luke.modules or{})then
+status=build_modules(L,env)end
+if status==0 then
+status=install_modules(L,env,L.luke,L.install)end
+return status
+end,}
+end
+package.preload['luke.lukefile']=function()
+local _ENV=require'std.normalize'{'luke._base','luke.configure','luke.environment','luke.platforms','std.functional','type.context-manager',}local function has_anykey(t,keylist)return any(map(keylist,function(k)return t[k]~=nil
+end))end
+local function isconfig(x)return istable(x)and has_anykey(x,configure)end
+local function collect_configs(luke,modulename,configs)configs=configs or{}for k,v in next,luke do
+if isconfig(v)then
+append(configs,{t=luke,k=k,module=modulename})elseif istable(v)then
+if k=='modules'or k=='external_dependencies'then
+for name,rules in next,v do
+collect_configs(rules,name,configs)end
+else
+collect_configs(v,modulename,configs)end
+end
+end
+return configs
+end
+local function deepcopy(t)return mapvalues(t,function(v)return case(type(v),{['table']=function()return deepcopy(v)end,v,})end)end
+local weighting=setmetatable(copy(configure),{__call=function(self,config)local t=config.t[config.k]for i=1,len(self)do
+if t[self[i]]~=nil then
+return i
+end
+end
+end})local function config_cmp(a,b)return weighting(a)<weighting(b)end
+local function fill_templates(env,src,dest)with(File(dest,'w'),function(cm)for line in lines(src)do
+cm:write(expand(env,line)..'\n')end
+end)return dest
+end
+local function rewrite_template_files(L,env,source)return case(source,{['(.+)%.in']=function(r)L.write('creating '..r..'\n')return fill_templates(env,r..'.in',r)end,source,})end
+local function collect_variables(luke,variables)for k,v in next,luke do
+if k=='external_dependencies'then
+map(keys(v),function(name)local rootdir=concat{'$',name,'_DIR'}variables[name..'_DIR']='/usr'variables[name..'_BINDIR']=rootdir..'/bin'variables[name..'_INCDIR']=rootdir..'/include'variables[name..'_LIBDIR']=rootdir..'/lib'end)elseif istable(v)then
+collect_variables(v,variables)end
+end
+return variables
+end
+local function normalize_configs(config)return cond({[not istable(config)]=config,},{[not isconfig(config)]=function()return mapvalues(config,normalize_configs)end,},{[true]=function()local keymap={include='includes',prog='progs',library='libraries',}return foldkeys(keymap,config,function(a,b)local r=istable(a)and copy(a)or{a}b=istable(b)and b or{b}return reduce(b,r,function(v)append(r,v)end)end)end,})end
+local function normalize_rules(rules)return case(type(rules),{['nil']=nop,['string']=function()return{sources={rules}}end,['table']=function()if len(rules)>0 then
+return{sources=rules}elseif isstring(rules.sources)then
+return merge({sources={rules.sources}},normalize_configs(rules))end
+return normalize_configs(rules)end,function(v)fatal("unsupported rule type '%s'",v)end,})end
+local function unwrap_external_dependencies(luke)if istable(luke.external_dependencies)then
+for prefix,config in next,luke.external_dependencies do
+if istable(config)and next(config)and config.library~=''then
+luke.incdirs=append(luke.incdirs or{},format('$%s_INCDIR',prefix))luke.libdirs=append(luke.libdirs or{},format('$%s_LIBDIR',prefix))luke.libraries=append(luke.libraries or{},config.library)end
+end
+luke.external_dependencies=nil
+end
+return luke
+end
+return{loadluke=function(filename)local content,err=slurp(File(filename))if content==nil then
+return nil,err
+end
+local r={}local chunk,err=loadstring(content,filename,r)if chunk==nil then
+return nil,"Error loading file: "..err
+end
+local ok,err=pcall(chunk)if not ok then
+return nil,"Error running file: "..err
+end
+r=filter_platforms(r)r.external_dependencies=normalize_configs(r.external_dependencies)r.ldocs=normalize_rules(r.ldocs)r.modules=mapvalues(r.modules,normalize_rules)return r
+end,collect_variables=function(luke)return collect_variables(luke,{})end,run_configs=function(L,env,luke)local r=deepcopy(luke)local all_configs=collect_configs(r)sort(all_configs,config_cmp)map(all_configs,function(config)config.t[config.k]=configure(L,env,config.t[config.k],config.module)end)return unwrap_external_dependencies(r)end,run_templates=function(L,env,luke)local r=copy(luke)local rewrite=bind(rewrite_template_files,{L,env})r.modules=mapvalues(r.modules,function(rules)rules.sources=map(rules.sources,rewrite)end)if r.ldocs then
+r.ldocs.sources=map(r.ldocs.sources,rewrite)end
+return r
+end,}
+end
+package.preload['luke.platforms']=function()
+local _ENV=require'std.normalize'{'std.functional',}local CANON={['AIX']=list('aix','unix'),['FreeBSD']=list('freebsd','bsd','unix'),['OpenBSD']=list('openbsd','bsd','unix'),['NetBSD']=list('netbsd','bsd','unix'),['Darwin']=list('macosx','bsd','unix'),['Linux']=list('linux','unix'),['SunOS']=list('solaris','unix'),['^CYGWIN']=list('cygwin','unix'),['^MSYS']=list('msys','cygwin','unix'),['^Windows']=list('win32','windows'),['^MINGW']=list('mingw32','win32','windows'),['^procnto']=list('qnx'),['QNX']=list('qnx'),['Haiku']=list('haiku','unix'),}local ALLPLATFORMS=reduce(values(CANON),function(acc,platforms)map(platforms,function(v)acc[v]=true
+end)end)local function match_uname(canon,uname,x)return match(uname,x)and canon[x]end
+local function toplatforms(canon,uname)local literalkeys,patternkeys=partition(keys(canon),function(k)return sub(k,1,1)~='^'end)return(pluck(literalkeys,canon)or{})[uname]or dropuntil(map(patternkeys,bind(match_uname,{canon,uname})))or list('unix')end
+local supported=toplatforms(CANON,popen('uname -s'):read'*l')local function isplatform(x)return ALLPLATFORMS[x]~=nil
+end
+local function filter_platforms(t,using,predicate)local r,supported,isplatform={},using or supported,predicate or isplatform
+for k,v in next,t do
+if k=='platforms'then
+local matches=filter(supported,bind(get,{v}))local default=except(keys(v),isplatform)merge(r,hoist(matches,v)or pluck(default,v))elseif istable(v)then
+r[k]=filter_platforms(v,supported)else
+r[k]=r[k]or v
+end
+end
+return r
+end
+return{filter_platforms=filter_platforms,platforms=supported,toplatforms=toplatforms,}
+end
+package.preload['std.functional']=function()
+local _ENV=require'std.normalize'{destructure=next,isfile=function(x)return io.type(x)=='file'end,wrap=coroutine.wrap,yield=coroutine.yield,}local function apply(fn,argu)assert(fn~=nil,'cannot apply nil-valued function')if iscallable(fn)then
+return fn(unpack(argu))end
+return fn
+end
+local function call(fn,...)assert(fn~=nil,'cannot call nil-valued function')if iscallable(fn)then
+return fn(...)end
+return fn
+end
+local function wrapnonnil(iterator)return function(...)local r=list(iterator(...))if r[1]~=nil then
+return r
+end
+end
+end
+local function each(seq)if type(seq)=='function'then
+return wrapnonnil(seq)end
+local i,n=0,int(seq.n)or len(seq)return function()if i<n then
+i=i+1
+return list(seq[i])end
+end
+end
+local function eq(x)return function(y)return x==y
+end
+end
+local function isnonnil(x)return x~=nil
+end
+local function mkpredicate(x)return type(x)=='function'and x or eq(x)end
+local function except(seq,predicate)predicate=mkpredicate(predicate)local r={}for valu in each(seq)do
+if not predicate(unpack(valu))then
+r[#r+1]=unpack(valu)end
+end
+return r
+end
+local function visit(x)if type(x)=='table'then
+for valu in each(x)do
+visit(unpack(valu))end
+else
+yield(x)end
+end
+local function flatten(...)local r={}for v in wrap(visit),except(list(...),nil)do
+r[#r+1]=v
+end
+return r
+end
+return{any=function(seq)for valu in each(seq)do
+if unpack(valu)then
+return true
+end
+end
+return false
+end,apply=apply,bind=function(fn,bound)local n=bound.n or maxn(bound)return function(...)local argu,unbound=copy(bound),list(...)local i=1
+for j=1,unbound.n do
+while argu[i]~=nil do
+i=i+1
+end
+argu[i],i=unbound[j],i+1
+end
+bound.n=n>=i and n or i-1
+return apply(fn,argu)end
+end,call=call,case=function(s,branches)if branches[s]~=nil then
+return call(branches[s],s)end
+local DEFAULT=1
+for pattern,fn in next,branches do
+if pattern~=DEFAULT then
+local argu=list(match(s,'^'..pattern..'$'))if argu[1]~=nil then
+return apply(fn,argu)end
+end
+end
+local default=branches[DEFAULT]if iscallable(default)then
+return call(default,s)end
+return default
+end,cond=function(...)for clauseu in each(list(...))do
+local expr,consequence=destructure(unpack(clauseu))if expr then
+return call(consequence,expr)end
+end
+end,contains=function(seq,predicate)if type(predicate)~='function'then
+predicate=eq(predicate)end
+for valu in each(seq)do
+if predicate(unpack(valu))then
+return true
+end
+end
+end,destructure=destructure,dropuntil=function(seq,predicate,block)if block==nil then
+predicate,block=isnonnil,predicate
+end
+if block~=nil then
+for valu in each(seq)do
+local r=list(block(unpack(valu)))if predicate(unpack(r))then
+return unpack(r)end
+end
+else
+for r in each(seq)do
+if predicate(unpack(r))then
+return unpack(r)end
+end
+end
+end,except=except,filter=function(seq,predicate)predicate=mkpredicate(predicate)local r={}for valu in each(seq)do
+if predicate(unpack(valu))then
+r[#r+1]=unpack(valu)end
+end
+return r
+end,flatten=flatten,foldkeys=function(keymap,dict,combinator)local r={}for k,v in next,dict or{}do
+local key=keymap[k]if key then
+r[key]=combinator(v,dict[key])else
+r[k]=r[k]or v
+end
+end
+return r
+end,get=function(dict,key)return(dict or{})[key]end,hoist=function(keylist,dict)local r={}for keyu in each(keylist)do
+merge(r,dict[unpack(keyu)])end
+return next(r)and r or nil
+end,id=function(...)return...end,isempty=function(x)return type(x)=='table'and not next(x)end,isfile=isfile,isfunction=function(x)return type(x)=='function'end,isnil=function(x)return x==nil
+end,isstring=function(x)return type(x)=='string'end,istable=function(x)return type(x)=='table'end,isnonzero=function(x)return x~=0
+end,keys=function(iterable)local r=list()for k in next,iterable or{}do
+append(r,k)end
+return r
+end,map=function(seq,block)local r=list()for valu in each(seq)do
+append(r,block(unpack(valu)))end
+return r
+end,mapvalues=function(iterable,block)local r={}for k,v in next,iterable or{}do
+r[k]=block(v)or v
+end
+return r
+end,nop=function()end,partition=function(seq,block)local r,s=list(),list()for valu in each(seq)do
+append(block(unpack(valu))and r or s,unpack(valu))end
+return r,s
+end,pluck=function(keylist,dict)local r={}for keyu in each(keylist)do
+local key=unpack(keyu)r[key]=dict[key]end
+return next(r)and r or nil
+end,reduce=function(seq,acc,block)if block==nil then
+acc,block={},acc
+end
+for valu in each(seq)do
+acc=block(acc,unpack(valu))or acc
+end
+return acc
+end,values=function(iterable)local r=list()for _,v in next,iterable or{}do
+append(r,v)end
+return r
+end,zip_with=function(iterable,block)local r=list()for k,v in next,iterable or{}do
+append(r,block(k,v))end
+return r
+end,}
+end
+package.preload['std.normalize']=function()
+local ceil=math.ceil
+local concat=table.concat
+local config=package.config
+local getmetatable=getmetatable
+local loadstring=loadstring
+local match=string.match
+local next=next
+local pack=table.pack or function(...)return{n=select('#',...),...}end
+local setfenv=setfenv
+local sort=table.sort
+local tointeger=math.tointeger
+local tonumber=tonumber
+local tostring=tostring
+local type=type
+local unpack=table.unpack or unpack
+local dirsep,pathsep,pathmark,execdir,igmark=match(config,'^([^\n]+)\n([^\n]+)\n([^\n]+)\n([^\n]+)\n([^\n]+)')local function copy(iterable)local r={}for k,v in next,iterable or{}do
+r[k]=v
+end
+return r
+end
+local int=(function(f)if f==nil then
+return function(x)if type(x)=='number'and ceil(x)-x==0.0 then
+return x
+end
+end
+elseif f'1'~=nil then
+return function(x)if type(x)=='number'then
+return tointeger(x)end
+end
+end
+return f
+end)(tointeger)local function iscallable(x)return type(x)=='function'and x or(getmetatable(x)or{}).__call
+end
+local function getmetamethod(x,n)return iscallable((getmetatable(x)or{})[tostring(n)])end
+local function rawlen(x)if type(x)~='table'then
+return#x
+end
+local n=#x
+for i=1,n do
+if x[i]==nil then
+return i-1
+end
+end
+return n
+end
+local function len(x)local m=getmetamethod(x,'__len')return m and m(x)or rawlen(x)end
+if setfenv then
+local _loadstring=loadstring
+loadstring=function(s,filename,env)chunk,err=_loadstring(s,filename)if chunk~=nil and env~=nil then
+setfenv(chunk,env)end
+return chunk,err
+end
+else
+loadstring=function(s,filename,env)return load(s,filename,"t",env)end
+setfenv=function()end
+end
+local function keysort(a,b)if int(a)then
+return int(b)==nil or a<b
+else
+return int(b)==nil and tostring(a)<tostring(b)end
+end
+local function str(x,roots)roots=roots or{}local function stop_roots(x)return roots[x]or str(x,copy(roots))end
+if type(x)~='table'or getmetamethod(x,'__tostring')then
+return tostring(x)else
+local buf={'{'}roots[x]=tostring(x)local n,keys=1,{}for k in next,x do
+keys[n],n=k,n+1
+end
+sort(keys,keysort)local kp
+for _,k in next,keys do
+if kp~=nil and k~=nil then
+buf[#buf+1]=type(kp)=='number'and k~=kp+1 and'; 'or', 'end
+if k==1 or type(k)=='number'and k-1==kp then
+buf[#buf+1]=stop_roots(x[k])else
+buf[#buf+1]=stop_roots(k)..'='..stop_roots(x[k])end
+kp=k
+end
+buf[#buf+1]='}'return concat(buf)end
+end
+return setmetatable({append=function(seq,v)local n=(int(seq.n)or len(seq))+1
+seq.n,seq[n]=n,v
+return seq
+end,arg=arg,assert=assert,char=string.char,close=io.close,concat=concat,copy=copy,dirsep=dirsep,exit=os.exit,find=string.find,format=string.format,getenv=os.getenv,getmetatable=getmetatable,getmetamethod=getmetamethod,gmatch=string.gmatch,gsub=string.gsub,int=int,iscallable=iscallable,len=len,lines=io.lines,list=pack,loadstring=loadstring,match=string.match,maxn=function(iterable)local n=0
+for k,v in next,iterable or{}do
+local i=int(k)if i and i>n then
+n=i
+end
+end
+return n
+end,merge=function(r,...)local argu=pack(...)for i=1,argu.n do
+for k,v in next,argu[i]or{}do
+r[k]=r[k]or v
+end
+end
+return r
+end,next=next,open=io.open,pack=pack,pcall=pcall,pop=function(seq)local n,r=seq.n or len(seq)r,seq[n]=seq[n]if int(seq.n)and seq.n>0 then
+seq.n=seq.n-1
+end
+return r
+end,popen=io.popen,print=print,rawget=rawget,rawset=rawset,rep=string.rep,rm=os.remove,select=select,setmetatable=setmetatable,sort=sort,stderr=io.stderr,stdout=io.stdout,str=str,sub=string.sub,tmpname=os.tmpname,tonumber=tonumber,type=type,unpack=function(seq,i,j)return unpack(seq,int(i)or 1,int(j)or int(seq.n)or len(seq))end,write=io.write,},{__call=function(self,env,level)local userenv,level=copy(self),level or 1
+for name,value in next,env do
+if int(name)and type(value)=='string'then
+for k,v in next,(require(value))do
+userenv[k]=userenv[k]or v
+end
+else
+userenv[name]=value
+end
+end
+setfenv(level+1,userenv)return userenv
+end,})
+end
+package.preload['type.context-manager']=function()
+local _ENV=require'std.normalize'{'std.functional',}local contextmanager_mt={__index=function(self,key)if iscallable(self.context[key])then
+return function(_,...)return self.context[key](self.context,...)end
+end
+if key=='filename'then
+return self[1]end
+end,}local function ContextManager(release,acquire,...)local fh,err=acquire(...)if not fh then
+return nil,err
+end
+local cm={context=fh,release=release,n=select("#",...),...}if cm.context~=nil then
+setmetatable(cm,contextmanager_mt)end
+return cm
+end
+local function context_close(cm)return isfile(cm.context)and close(cm.context)end
+local function with(...)local argu=list(...)local block=pop(argu)local r=list(apply(block,argu))map(argu,function(cm)if cm~=nil then
+cm:release()end
+end)return unpack(r)end
+return{ContextManager=ContextManager,CTest=function()local conftest=tmpname()return ContextManager(function(cm)rm(conftest)rm(gsub(conftest,'^.*/','')..'.o')if context_close(cm)then
+return rm(cm.filename)end
+return false
+end,open,conftest..'.c','w')end,File=function(fname,mode)return ContextManager(context_close,open,fname,mode)end,Pipe=function(cmd,mode)return ContextManager(context_close,popen,cmd,mode)end,TmpFile=function(fname,mode)return ContextManager(function(cm)if context_close(cm)then
+return rm(cm.filename)end
+return false
+end,open,fname or tmpname(),mode or'w')end,slurp=function(cm,...)if not cm then
+return cm,...end
+return with(cm,function(h)return h:read'*a'end)end,with=with,}
+end
+package.preload['type.dict']=function()
+local _ENV=require'std.normalize'{destructure=next,}return{OrderedDict=function(...)local r,argu={},list(...)for i=1,argu.n do
+local k,v=destructure(argu[i])append(r,k)r[k]=v
+end
+return r
+end,}
+end
+package.preload['type.path']=function()
+local _ENV=require'std.normalize'{}local BASENAMEPAT='.*'..dirsep
+local DIRNAMEPAT=dirsep..'[^'..dirsep..']*$'return{basename=function(path)return(gsub(path,BASENAMEPAT,''))end,dirname=function(path)return(gsub(path,DIRNAMEPAT,'',1))end,exists=function(path)local fh=open(path)if fh==nil then
+return false
+end
+close(fh)return true
+end,}
+end
+os.exit(require'luke'.main(arg))
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 000000000000..bf7a957f51bd
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,97 @@
+<!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>lyaml 6.2.8 Reference</title>
+ <link rel="stylesheet" href="ldoc.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>lyaml 6.2.8</h1>
+
+
+
+
+<h2>Modules</h2>
+<ul class="nowrap">
+ <li><a href="modules/lyaml.html">lyaml</a></li>
+ <li><a href="modules/lyaml.explicit.html">lyaml.explicit</a></li>
+ <li><a href="modules/lyaml.functional.html">lyaml.functional</a></li>
+ <li><a href="modules/lyaml.implicit.html">lyaml.implicit</a></li>
+</ul>
+
+</div>
+
+<div id="content">
+
+
+ <h2>
+<h1>LYAML binding for Lua</h1>
+
+<p>This is a Lua binding for the fast libYAML C library for converting
+between <code>%YAML 1.1</code> and Lua tables, with a flexible Lua language
+API to load and save YAML documents.</p>
+
+<p>It works with Lua 5.1 (including LuaJIT), 5.2, 5.3 and 5.4.</p>
+
+<h2>LICENSE</h2>
+
+<p>The code is copyright by its respective authors, and released under the
+MIT license (the same license as Lua itself). There is no warranty.</p>
+
+</h2>
+
+<h2>Modules</h2>
+<table class="module_list">
+ <tr>
+ <td class="name" nowrap><a href="modules/lyaml.html">lyaml</a></td>
+ <td class="summary">
+
+</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="modules/lyaml.explicit.html">lyaml.explicit</a></td>
+ <td class="summary">
+
+</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="modules/lyaml.functional.html">lyaml.functional</a></td>
+ <td class="summary">
+
+</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="modules/lyaml.implicit.html">lyaml.implicit</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>
+<i style="float:right;">Last updated 2022-10-22 17:12:03 </i>
+</div> <!-- id="about" -->
+</div> <!-- id="container" -->
+</body>
+</html>
diff --git a/doc/ldoc.css b/doc/ldoc.css
new file mode 100644
index 000000000000..52c4ad2bd8a2
--- /dev/null
+++ b/doc/ldoc.css
@@ -0,0 +1,303 @@
+/* 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: 20px 0 20px 0;
+}
+h2, h3, h4 { margin: 15px 0 10px 0; }
+h2 { font-size: 1.25em; }
+h3 { font-size: 1.15em; }
+h4 { font-size: 1.06em; }
+
+a:link { font-weight: bold; color: #004080; text-decoration: none; }
+a:visited { font-weight: bold; color: #006699; text-decoration: none; }
+a:link:hover { text-decoration: underline; }
+
+hr {
+ color:#cccccc;
+ background: #00007f;
+ height: 1px;
+}
+
+blockquote { margin-left: 3em; }
+
+ul { list-style-type: disc; }
+
+p.name {
+ font-family: "Andale Mono", monospace;
+ padding-top: 1em;
+}
+
+pre {
+ background-color: rgb(245, 245, 245);
+ border: 1px solid #C0C0C0; /* silver */
+ padding: 10px;
+ margin: 10px 0 10px 0;
+ overflow: auto;
+ font-family: "Andale Mono", monospace;
+}
+
+pre.example {
+ font-size: .85em;
+}
+
+table.index { border: 1px #00007f; }
+table.index td { text-align: left; vertical-align: top; }
+
+#container {
+ margin-left: 1em;
+ margin-right: 1em;
+ background-color: #f0f0f0;
+}
+
+#product {
+ text-align: center;
+ border-bottom: 1px solid #cccccc;
+ background-color: #ffffff;
+}
+
+#product big {
+ font-size: 2em;
+}
+
+#main {
+ background-color: #f0f0f0;
+ border-left: 2px solid #cccccc;
+}
+
+#navigation {
+ float: left;
+ width: 14em;
+ vertical-align: top;
+ background-color: #f0f0f0;
+ overflow: visible;
+}
+
+#navigation h2 {
+ background-color:#e7e7e7;
+ font-size:1.1em;
+ color:#000000;
+ text-align: left;
+ padding:0.2em;
+ border-top:1px solid #dddddd;
+ border-bottom:1px solid #dddddd;
+}
+
+#navigation ul
+{
+ font-size:1em;
+ list-style-type: none;
+ margin: 1px 1px 10px 1px;
+}
+
+#navigation li {
+ text-indent: -1em;
+ display: block;
+ margin: 3px 0px 0px 22px;
+}
+
+#navigation li li a {
+ margin: 0px 3px 0px -1em;
+}
+
+#content {
+ margin-left: 14em;
+ padding: 1em;
+ width: 700px;
+ border-left: 2px solid #cccccc;
+ border-right: 2px solid #cccccc;
+ background-color: #ffffff;
+}
+
+#about {
+ clear: both;
+ padding: 5px;
+ border-top: 2px solid #cccccc;
+ background-color: #ffffff;
+}
+
+@media print {
+ body {
+ font: 12pt "Times New Roman", "TimeNR", Times, serif;
+ }
+ a { font-weight: bold; color: #004080; text-decoration: underline; }
+
+ #main {
+ background-color: #ffffff;
+ border-left: 0px;
+ }
+
+ #container {
+ margin-left: 2%;
+ margin-right: 2%;
+ background-color: #ffffff;
+ }
+
+ #content {
+ padding: 1em;
+ background-color: #ffffff;
+ }
+
+ #navigation {
+ display: none;
+ }
+ pre.example {
+ font-family: "Andale Mono", monospace;
+ font-size: 10pt;
+ page-break-inside: avoid;
+ }
+}
+
+table.module_list {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #cccccc;
+ border-collapse: collapse;
+}
+table.module_list td {
+ border-width: 1px;
+ padding: 3px;
+ border-style: solid;
+ border-color: #cccccc;
+}
+table.module_list td.name { background-color: #f0f0f0; min-width: 200px; }
+table.module_list td.summary { width: 100%; }
+
+
+table.function_list {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #cccccc;
+ border-collapse: collapse;
+}
+table.function_list td {
+ border-width: 1px;
+ padding: 3px;
+ border-style: solid;
+ border-color: #cccccc;
+}
+table.function_list td.name { background-color: #f0f0f0; min-width: 200px; }
+table.function_list td.summary { width: 100%; }
+
+ul.nowrap {
+ overflow:auto;
+ white-space:nowrap;
+}
+
+dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
+dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
+dl.table h3, dl.function h3 {font-size: .95em;}
+
+/* stop sublists from having initial vertical space */
+ul ul { margin-top: 0px; }
+ol ul { margin-top: 0px; }
+ol ol { margin-top: 0px; }
+ul ol { margin-top: 0px; }
+
+/* make the target distinct; helps when we're navigating to a function */
+a:target + * {
+ background-color: #FF9;
+}
+
+
+/* styles for prettification of source */
+pre .comment { color: #558817; }
+pre .constant { color: #a8660d; }
+pre .escape { color: #844631; }
+pre .keyword { color: #aa5050; font-weight: bold; }
+pre .library { color: #0e7c6b; }
+pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
+pre .string { color: #8080ff; }
+pre .number { color: #f8660d; }
+pre .operator { color: #2239a8; font-weight: bold; }
+pre .preprocessor, pre .prepro { color: #a33243; }
+pre .global { color: #800080; }
+pre .user-keyword { color: #800080; }
+pre .prompt { color: #558817; }
+pre .url { color: #272fc2; text-decoration: underline; }
+
diff --git a/doc/modules/lyaml.explicit.html b/doc/modules/lyaml.explicit.html
new file mode 100644
index 000000000000..2667c4e006b2
--- /dev/null
+++ b/doc/modules/lyaml.explicit.html
@@ -0,0 +1,267 @@
+<!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>lyaml 6.2.8 Reference</title>
+ <link rel="stylesheet" href="../ldoc.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>lyaml 6.2.8</h1>
+
+<ul>
+ <li><a href="../index.html">Index</a></li>
+</ul>
+
+<h2>Contents</h2>
+<ul>
+<li><a href="#Functions">Functions</a></li>
+</ul>
+
+
+<h2>Modules</h2>
+<ul class="nowrap">
+ <li><a href="../modules/lyaml.html">lyaml</a></li>
+ <li><strong>lyaml.explicit</strong></li>
+ <li><a href="../modules/lyaml.functional.html">lyaml.functional</a></li>
+ <li><a href="../modules/lyaml.implicit.html">lyaml.implicit</a></li>
+</ul>
+
+</div>
+
+<div id="content">
+
+<h1>Module <code>lyaml.explicit</code></h1>
+<p>
+
+</p>
+<p>
+
+</p>
+
+
+<h2><a href="#Functions">Functions</a></h2>
+<table class="function_list">
+ <tr>
+ <td class="name" nowrap><a href="#bool">bool (value)</a></td>
+ <td class="summary">Parse the value following an explicit <code>!!bool</code> tag.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#float">float (value)</a></td>
+ <td class="summary">Parse the value following an explicit <code>!!float</code> tag.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#int">int (value)</a></td>
+ <td class="summary">Parse the value following an explicit <code>!!int</code> tag.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#null">null ()</a></td>
+ <td class="summary">Parse an explicit <code>!!null</code> tag.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#str">str (value)</a></td>
+ <td class="summary">Parse the value following an explicit <code>!!str</code> tag.</td>
+ </tr>
+</table>
+
+<br/>
+<br/>
+
+
+ <h2 class="section-header "><a name="Functions"></a>Functions</h2>
+
+ <dl class="function">
+ <dt>
+ <a name = "bool"></a>
+ <strong>bool (value)</strong>
+ </dt>
+ <dd>
+ Parse the value following an explicit <code>!!bool</code> tag.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><a class="type" href="../modules/lyaml.explicit.html#bool">bool</a></span>
+ boolean equivalent, if a valid value was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_bool = explicit.bool(tagarg)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "float"></a>
+ <strong>float (value)</strong>
+ </dt>
+ <dd>
+ Parse the value following an explicit <code>!!float</code> tag.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">number</span></span>
+ float equivalent, if a valid value was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_float = explicit.float(tagarg)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "int"></a>
+ <strong>int (value)</strong>
+ </dt>
+ <dd>
+ Parse the value following an explicit <code>!!int</code> tag.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><a class="type" href="../modules/lyaml.explicit.html#int">int</a></span>
+ integer equivalent, if a valid value was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_int = explicit.int(tagarg)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "null"></a>
+ <strong>null ()</strong>
+ </dt>
+ <dd>
+ Parse an explicit <code>!!null</code> tag.
+
+
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">lyaml.null</span></span>
+
+
+
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">null = explicit.null(tagarg)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "str"></a>
+ <strong>str (value)</strong>
+ </dt>
+ <dd>
+ Parse the value following an explicit <code>!!str</code> tag.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span>
+ token
+ </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>
+ <em>value</em> which was a string already
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">tagarg = explicit.str(tagarg)</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>
+<i style="float:right;">Last updated 2022-10-22 17:12:03 </i>
+</div> <!-- id="about" -->
+</div> <!-- id="container" -->
+</body>
+</html>
diff --git a/doc/modules/lyaml.functional.html b/doc/modules/lyaml.functional.html
new file mode 100644
index 000000000000..b003a961ff28
--- /dev/null
+++ b/doc/modules/lyaml.functional.html
@@ -0,0 +1,236 @@
+<!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>lyaml 6.2.8 Reference</title>
+ <link rel="stylesheet" href="../ldoc.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>lyaml 6.2.8</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="#Tables">Tables</a></li>
+</ul>
+
+
+<h2>Modules</h2>
+<ul class="nowrap">
+ <li><a href="../modules/lyaml.html">lyaml</a></li>
+ <li><a href="../modules/lyaml.explicit.html">lyaml.explicit</a></li>
+ <li><strong>lyaml.functional</strong></li>
+ <li><a href="../modules/lyaml.implicit.html">lyaml.implicit</a></li>
+</ul>
+
+</div>
+
+<div id="content">
+
+<h1>Module <code>lyaml.functional</code></h1>
+<p>
+
+</p>
+<p>
+
+</p>
+
+
+<h2><a href="#Functions">Functions</a></h2>
+<table class="function_list">
+ <tr>
+ <td class="name" nowrap><a href="#isnull">isnull (x)</a></td>
+ <td class="summary"><code>lyaml.null</code> predicate.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#iscallable">iscallable (x)</a></td>
+ <td class="summary">Callable predicate.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#anyof">anyof (fns)</a></td>
+ <td class="summary">Compose a function to try each callable with supplied args.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#id">id (...)</a></td>
+ <td class="summary">Return arguments unchanged.</td>
+ </tr>
+</table>
+<h2><a href="#Tables">Tables</a></h2>
+<table class="function_list">
+ <tr>
+ <td class="name" nowrap><a href="#NULL">NULL</a></td>
+ <td class="summary"><code>lyaml.null</code> value.</td>
+ </tr>
+</table>
+
+<br/>
+<br/>
+
+
+ <h2 class="section-header "><a name="Functions"></a>Functions</h2>
+
+ <dl class="function">
+ <dt>
+ <a name = "isnull"></a>
+ <strong>isnull (x)</strong>
+ </dt>
+ <dd>
+ <code>lyaml.null</code> predicate.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">x</span>
+ operand
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">bool</span></span>
+ <code>true</code> if <em>x</em> is <code>lyaml.null</code>.
+ </ol>
+
+
+
+
+</dd>
+ <dt>
+ <a name = "iscallable"></a>
+ <strong>iscallable (x)</strong>
+ </dt>
+ <dd>
+ Callable predicate.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">x</span>
+ operand
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">bool</span></span>
+ <code>true</code> if <em>x</em> is a function has a __call metamethod
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">r = iscallable(x) <span class="keyword">and</span> x(...)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "anyof"></a>
+ <strong>anyof (fns)</strong>
+ </dt>
+ <dd>
+ Compose a function to try each callable with supplied args.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">fns</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span>
+ list of functions to try
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">function</span></span>
+
+<p>a new function to call <em>...</em> functions, stopping</p>
+<pre><code>and returning the first non-nil result, if any
+</code></pre>
+
+ </ol>
+
+
+
+
+</dd>
+ <dt>
+ <a name = "id"></a>
+ <strong>id (...)</strong>
+ </dt>
+ <dd>
+ Return arguments unchanged.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">...</span>
+ arguments
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <em>...</em>
+ </ol>
+
+
+
+
+</dd>
+</dl>
+ <h2 class="section-header "><a name="Tables"></a>Tables</h2>
+
+ <dl class="function">
+ <dt>
+ <a name = "NULL"></a>
+ <strong>NULL</strong>
+ </dt>
+ <dd>
+ <code>lyaml.null</code> value.
+
+
+
+
+
+
+
+</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 2022-10-22 17:12:03 </i>
+</div> <!-- id="about" -->
+</div> <!-- id="container" -->
+</body>
+</html>
diff --git a/doc/modules/lyaml.html b/doc/modules/lyaml.html
new file mode 100644
index 000000000000..2deb6a4b6be7
--- /dev/null
+++ b/doc/modules/lyaml.html
@@ -0,0 +1,224 @@
+<!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>lyaml 6.2.8 Reference</title>
+ <link rel="stylesheet" href="../ldoc.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>lyaml 6.2.8</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="#Tables">Tables</a></li>
+</ul>
+
+
+<h2>Modules</h2>
+<ul class="nowrap">
+ <li><strong>lyaml</strong></li>
+ <li><a href="../modules/lyaml.explicit.html">lyaml.explicit</a></li>
+ <li><a href="../modules/lyaml.functional.html">lyaml.functional</a></li>
+ <li><a href="../modules/lyaml.implicit.html">lyaml.implicit</a></li>
+</ul>
+
+</div>
+
+<div id="content">
+
+<h1>Module <code>lyaml</code></h1>
+<p>
+
+</p>
+<p>
+
+</p>
+
+
+<h2><a href="#Functions">Functions</a></h2>
+<table class="function_list">
+ <tr>
+ <td class="name" nowrap><a href="#dump">dump (documents[, opts])</a></td>
+ <td class="summary">Dump a list of Lua tables to an equivalent YAML stream.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#load">load (s[, opts])</a></td>
+ <td class="summary">Load a YAML stream into a Lua table.</td>
+ </tr>
+</table>
+<h2><a href="#Tables">Tables</a></h2>
+<table class="function_list">
+ <tr>
+ <td class="name" nowrap><a href="#dumper_opts">dumper_opts</a></td>
+ <td class="summary">Dump options table.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#loader_opts">loader_opts</a></td>
+ <td class="summary">Load options table.</td>
+ </tr>
+</table>
+
+<br/>
+<br/>
+
+
+ <h2 class="section-header "><a name="Functions"></a>Functions</h2>
+
+ <dl class="function">
+ <dt>
+ <a name = "dump"></a>
+ <strong>dump (documents[, opts])</strong>
+ </dt>
+ <dd>
+ Dump a list of Lua tables to an equivalent YAML stream.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">documents</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span>
+ a sequence of Lua tables.
+ </li>
+ <li><span class="parameter">opts</span>
+ <span class="types"><span class="type">dumper_opts</span></span>
+ initialisation options
+ (<em>optional</em>)
+ </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>
+ equivalest YAML stream
+ </ol>
+
+
+
+
+</dd>
+ <dt>
+ <a name = "load"></a>
+ <strong>load (s[, opts])</strong>
+ </dt>
+ <dd>
+ Load a YAML stream into a Lua table.
+
+
+ <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>
+ YAML stream
+ </li>
+ <li><span class="parameter">opts</span>
+ <span class="types"><span class="type">loader_opts</span></span>
+ initialisation options
+ (<em>optional</em>)
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span>
+ Lua table equivalent of stream <em>s</em>
+ </ol>
+
+
+
+
+</dd>
+</dl>
+ <h2 class="section-header "><a name="Tables"></a>Tables</h2>
+
+ <dl class="function">
+ <dt>
+ <a name = "dumper_opts"></a>
+ <strong>dumper_opts</strong>
+ </dt>
+ <dd>
+ Dump options table.
+
+
+ <h3>Fields:</h3>
+ <ul>
+ <li><span class="parameter">anchors</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span>
+ map initial anchor names to values
+ </li>
+ <li><span class="parameter">implicit_scalar</span>
+ <span class="types"><span class="type">function</span></span>
+ parse implicit scalar values
+ </li>
+ </ul>
+
+
+
+
+
+</dd>
+ <dt>
+ <a name = "loader_opts"></a>
+ <strong>loader_opts</strong>
+ </dt>
+ <dd>
+ Load options table.
+
+
+ <h3>Fields:</h3>
+ <ul>
+ <li><span class="parameter">all</span>
+ <span class="types"><span class="type">boolean</span></span>
+ load all documents from the stream
+ </li>
+ <li><span class="parameter">explicit_scalar</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span>
+ map full tag-names to parser functions
+ </li>
+ <li><span class="parameter">implicit_scalar</span>
+ <span class="types"><span class="type">function</span></span>
+ parse implicit scalar values
+ </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 2022-10-22 17:12:03 </i>
+</div> <!-- id="about" -->
+</div> <!-- id="container" -->
+</body>
+</html>
diff --git a/doc/modules/lyaml.implicit.html b/doc/modules/lyaml.implicit.html
new file mode 100644
index 000000000000..935579f705c6
--- /dev/null
+++ b/doc/modules/lyaml.implicit.html
@@ -0,0 +1,533 @@
+<!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>lyaml 6.2.8 Reference</title>
+ <link rel="stylesheet" href="../ldoc.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>lyaml 6.2.8</h1>
+
+<ul>
+ <li><a href="../index.html">Index</a></li>
+</ul>
+
+<h2>Contents</h2>
+<ul>
+<li><a href="#Functions">Functions</a></li>
+</ul>
+
+
+<h2>Modules</h2>
+<ul class="nowrap">
+ <li><a href="../modules/lyaml.html">lyaml</a></li>
+ <li><a href="../modules/lyaml.explicit.html">lyaml.explicit</a></li>
+ <li><a href="../modules/lyaml.functional.html">lyaml.functional</a></li>
+ <li><strong>lyaml.implicit</strong></li>
+</ul>
+
+</div>
+
+<div id="content">
+
+<h1>Module <code>lyaml.implicit</code></h1>
+<p>
+
+</p>
+<p>
+
+</p>
+
+
+<h2><a href="#Functions">Functions</a></h2>
+<table class="function_list">
+ <tr>
+ <td class="name" nowrap><a href="#null">null (value)</a></td>
+ <td class="summary">Parse a null token to a null value.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#bool">bool (value)</a></td>
+ <td class="summary">Parse a boolean token to the equivalent value.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#binary">binary (value)</a></td>
+ <td class="summary">Parse a binary token, such as '0b1010_0111_0100_1010_1110'.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#octal">octal (value)</a></td>
+ <td class="summary">Parse an octal token, such as '012345'.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#decimal">decimal (value)</a></td>
+ <td class="summary">Parse a decimal token, such as '0' or '12345'.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#hexadecimal">hexadecimal (value)</a></td>
+ <td class="summary">Parse a hexadecimal token, such as '0xdeadbeef'.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#sexagesimal">sexagesimal (value)</a></td>
+ <td class="summary">Parse a sexagesimal token, such as '190:20:30'.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#nan">nan (value)</a></td>
+ <td class="summary">Parse a <code>nan</code> token.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#inf">inf (value)</a></td>
+ <td class="summary">Parse a signed <code>inf</code> token.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#float">float (value)</a></td>
+ <td class="summary">Parse a floating point number token, such as '1e-3' or '-0.12'.</td>
+ </tr>
+ <tr>
+ <td class="name" nowrap><a href="#sexfloat">sexfloat (value)</a></td>
+ <td class="summary">Parse a sexagesimal float, such as '190:20:30.15'.</td>
+ </tr>
+</table>
+
+<br/>
+<br/>
+
+
+ <h2 class="section-header "><a name="Functions"></a>Functions</h2>
+
+ <dl class="function">
+ <dt>
+ <a name = "null"></a>
+ <strong>null (value)</strong>
+ </dt>
+ <dd>
+ Parse a null token to a null value.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ lyaml.null, for an empty string or literal ~
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ nil otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_null = implicit.null(token)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "bool"></a>
+ <strong>bool (value)</strong>
+ </dt>
+ <dd>
+ Parse a boolean token to the equivalent value.
+ Treats capilalized, lower and upper-cased variants of true/false,
+ yes/no or on/off tokens as boolean <code>true</code> and <code>false</code> values.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><a class="type" href="../modules/lyaml.implicit.html#bool">bool</a></span>
+ if a valid boolean token was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_bool = implicit.bool(token)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "binary"></a>
+ <strong>binary (value)</strong>
+ </dt>
+ <dd>
+ Parse a binary token, such as '0b1010_0111_0100_1010_1110'.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">int</span></span>
+ integer equivalent, if a valid token was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_int = implicit.binary(value)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "octal"></a>
+ <strong>octal (value)</strong>
+ </dt>
+ <dd>
+ Parse an octal token, such as '012345'.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">int</span></span>
+ integer equivalent, if a valid token was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_int = implicit.octal(value)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "decimal"></a>
+ <strong>decimal (value)</strong>
+ </dt>
+ <dd>
+ Parse a decimal token, such as '0' or '12345'.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">int</span></span>
+ integer equivalent, if a valid token was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_int = implicit.decimal(value)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "hexadecimal"></a>
+ <strong>hexadecimal (value)</strong>
+ </dt>
+ <dd>
+ Parse a hexadecimal token, such as '0xdeadbeef'.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">int</span></span>
+ integer equivalent, if a valid token was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_int = implicit.hexadecimal(value)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "sexagesimal"></a>
+ <strong>sexagesimal (value)</strong>
+ </dt>
+ <dd>
+ Parse a sexagesimal token, such as '190:20:30'.
+ Useful for times and angles.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">int</span></span>
+ integer equivalent, if a valid token was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_int = implicit.sexagesimal(value)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "nan"></a>
+ <strong>nan (value)</strong>
+ </dt>
+ <dd>
+ Parse a <code>nan</code> token.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><a class="type" href="../modules/lyaml.implicit.html#nan">nan</a></span>
+ not-a-number, if a valid token was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_nan = implicit.nan(value)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "inf"></a>
+ <strong>inf (value)</strong>
+ </dt>
+ <dd>
+ Parse a signed <code>inf</code> token.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">number</span></span>
+ plus/minus-infinity, if a valid token was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_inf = implicit.inf(value)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "float"></a>
+ <strong>float (value)</strong>
+ </dt>
+ <dd>
+ Parse a floating point number token, such as '1e-3' or '-0.12'.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">number</span></span>
+ float equivalent, if a valid token was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_float = implicit.float(value)</pre>
+ </ul>
+
+</dd>
+ <dt>
+ <a name = "sexfloat"></a>
+ <strong>sexfloat (value)</strong>
+ </dt>
+ <dd>
+ Parse a sexagesimal float, such as '190:20:30.15'.
+ Useful for times and angles.
+
+
+ <h3>Parameters:</h3>
+ <ul>
+ <li><span class="parameter">value</span>
+ <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span>
+ token
+ </li>
+ </ul>
+
+ <h3>Returns:</h3>
+ <ol>
+
+ <span class="types"><span class="type">number</span></span>
+ float equivalent, if a valid token was recognized
+ </ol>
+ <h3>Or</h3>
+ <ol>
+
+ <span class="types"><span class="type">nil</span></span>
+ otherwise, nil
+ </ol>
+
+
+
+ <h3>Usage:</h3>
+ <ul>
+ <pre class="example">maybe_float = implicit.sexfloat(value)</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>
+<i style="float:right;">Last updated 2022-10-22 17:12:03 </i>
+</div> <!-- id="about" -->
+</div> <!-- id="container" -->
+</body>
+</html>
diff --git a/ext/yaml/emitter.c b/ext/yaml/emitter.c
new file mode 100644
index 000000000000..b71fd14f4c1d
--- /dev/null
+++ b/ext/yaml/emitter.c
@@ -0,0 +1,459 @@
+/*
+ * emitter.c, LibYAML emitter binding for Lua
+ * Written by Gary V. Vaughan, 2013
+ *
+ * Copyright (C) 2013-2022 Gary V. Vaughan
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND 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.
+ */
+
+#include <assert.h>
+
+#include "lyaml.h"
+
+
+typedef struct {
+ yaml_emitter_t emitter;
+
+ /* output accumulator */
+ lua_State *outputL;
+ luaL_Buffer yamlbuff;
+
+ /* error handling */
+ lua_State *errL;
+ luaL_Buffer errbuff;
+ int error;
+} lyaml_emitter;
+
+
+/* Emit a STREAM_START event. */
+static int
+emit_STREAM_START (lua_State *L, lyaml_emitter *emitter)
+{
+ yaml_event_t event;
+ yaml_encoding_t yaml_encoding;
+ const char *encoding = NULL;
+
+ RAWGET_STRDUP (encoding); lua_pop (L, 1);
+
+#define MENTRY(_s) (STREQ (encoding, #_s)) { yaml_encoding = YAML_##_s##_ENCODING; }
+ if (encoding == NULL) { yaml_encoding = YAML_ANY_ENCODING; } else
+ if MENTRY( UTF8 ) else
+ if MENTRY( UTF16LE ) else
+ if MENTRY( UTF16BE ) else
+ {
+ emitter->error++;
+ luaL_addsize (&emitter->errbuff,
+ sprintf (luaL_prepbuffer (&emitter->errbuff),
+ "invalid stream encoding '%s'", encoding));
+ }
+#undef MENTRY
+
+ if (encoding) free ((void *) encoding);
+
+ if (emitter->error != 0)
+ return 0;
+
+ yaml_stream_start_event_initialize (&event, yaml_encoding);
+ return yaml_emitter_emit (&emitter->emitter, &event);
+}
+
+
+/* Emit a STREAM_END event. */
+static int
+emit_STREAM_END (lua_State *L, lyaml_emitter *emitter)
+{
+ yaml_event_t event;
+ yaml_stream_end_event_initialize (&event);
+ return yaml_emitter_emit (&emitter->emitter, &event);
+}
+
+
+/* Emit a DOCUMENT_START event. */
+static int
+emit_DOCUMENT_START (lua_State *L, lyaml_emitter *emitter)
+{
+ yaml_event_t event;
+ yaml_version_directive_t version_directive, *Pversion_directive = NULL;
+ yaml_tag_directive_t *tag_directives_start = NULL, *tag_directives_end = NULL;
+ int implicit = 0;
+
+ RAWGET_PUSHTABLE ("version_directive");
+ if (lua_type (L, -1) == LUA_TTABLE)
+ {
+ RAWGETS_INTEGER (version_directive.major, "major");
+ ERROR_IFNIL ("version_directive missing key 'major'");
+ if (emitter->error == 0)
+ {
+ RAWGETS_INTEGER (version_directive.minor, "minor");
+ ERROR_IFNIL ("version_directive missing key 'minor'");
+ }
+ Pversion_directive = &version_directive;
+ }
+ lua_pop (L, 1); /* pop version_directive rawget */
+
+ RAWGET_PUSHTABLE ("tag_directives");
+ if (lua_type (L, -1) == LUA_TTABLE)
+ {
+ size_t bytes = lua_objlen (L, -1) * sizeof (yaml_tag_directive_t);
+
+ tag_directives_start = (yaml_tag_directive_t *) malloc (bytes);
+ tag_directives_end = tag_directives_start;
+
+ lua_pushnil (L); /* first key */
+ while (lua_next (L, -2) != 0)
+ {
+ RAWGETS_STRDUP (tag_directives_end->handle, "handle");
+ ERROR_IFNIL ("tag_directives item missing key 'handle'");
+ lua_pop (L, 1); /* pop handle */
+
+ RAWGETS_STRDUP (tag_directives_end->prefix, "prefix");
+ ERROR_IFNIL ("tag_directives item missing key 'prefix'");
+ lua_pop (L, 1); /* pop prefix */
+
+ tag_directives_end += 1;
+
+ /* pop tag_directives list elewent, leave key for next iteration */
+ lua_pop (L, 1);
+ }
+ }
+ lua_pop (L, 1); /* pop lua_rawget "tag_directives" result */
+
+ RAWGET_BOOLEAN (implicit); lua_pop (L, 1);
+
+ if (emitter->error != 0)
+ return 0;
+
+ yaml_document_start_event_initialize (&event, Pversion_directive,
+ tag_directives_start, tag_directives_end, implicit);
+ return yaml_emitter_emit (&emitter->emitter, &event);
+}
+
+
+/* Emit a DOCUMENT_END event. */
+static int
+emit_DOCUMENT_END (lua_State *L, lyaml_emitter *emitter)
+{
+ yaml_event_t event;
+ int implicit = 0;
+
+ RAWGET_BOOLEAN (implicit);
+
+ yaml_document_end_event_initialize (&event, implicit);
+ return yaml_emitter_emit (&emitter->emitter, &event);
+}
+
+
+/* Emit a MAPPING_START event. */
+static int
+emit_MAPPING_START (lua_State *L, lyaml_emitter *emitter)
+{
+ yaml_event_t event;
+ yaml_mapping_style_t yaml_style;
+ yaml_char_t *anchor = NULL, *tag = NULL;
+ int implicit = 1;
+ const char *style = NULL;
+
+ RAWGET_STRDUP (style); lua_pop (L, 1);
+
+#define MENTRY(_s) (STREQ (style, #_s)) { yaml_style = YAML_##_s##_MAPPING_STYLE; }
+ if (style == NULL) { yaml_style = YAML_ANY_MAPPING_STYLE; } else
+ if MENTRY( BLOCK ) else
+ if MENTRY( FLOW ) else
+ {
+ emitter->error++;
+ luaL_addsize (&emitter->errbuff,
+ sprintf (luaL_prepbuffer (&emitter->errbuff),
+ "invalid mapping style '%s'", style));
+ }
+#undef MENTRY
+
+ if (style) free ((void *) style);
+
+ RAWGET_YAML_CHARP (anchor); lua_pop (L, 1);
+ RAWGET_YAML_CHARP (tag); lua_pop (L, 1);
+ RAWGET_BOOLEAN (implicit); lua_pop (L, 1);
+
+ yaml_mapping_start_event_initialize (&event, anchor, tag, implicit, yaml_style);
+ return yaml_emitter_emit (&emitter->emitter, &event);
+}
+
+
+/* Emit a MAPPING_END event. */
+static int
+emit_MAPPING_END (lua_State *L, lyaml_emitter *emitter)
+{
+ yaml_event_t event;
+ yaml_mapping_end_event_initialize (&event);
+ return yaml_emitter_emit (&emitter->emitter, &event);
+}
+
+
+/* Emit a SEQUENCE_START event. */
+static int
+emit_SEQUENCE_START (lua_State *L, lyaml_emitter *emitter)
+{
+ yaml_event_t event;
+ yaml_sequence_style_t yaml_style;
+ yaml_char_t *anchor = NULL, *tag = NULL;
+ int implicit = 1;
+ const char *style = NULL;
+
+ RAWGET_STRDUP (style); lua_pop (L, 1);
+
+#define MENTRY(_s) (STREQ (style, #_s)) { yaml_style = YAML_##_s##_SEQUENCE_STYLE; }
+ if (style == NULL) { yaml_style = YAML_ANY_SEQUENCE_STYLE; } else
+ if MENTRY( BLOCK ) else
+ if MENTRY( FLOW ) else
+ {
+ emitter->error++;
+ luaL_addsize (&emitter->errbuff,
+ sprintf (luaL_prepbuffer (&emitter->errbuff),
+ "invalid sequence style '%s'", style));
+ }
+#undef MENTRY
+
+ if (style) free ((void *) style);
+
+ RAWGET_YAML_CHARP (anchor); lua_pop (L, 1);
+ RAWGET_YAML_CHARP (tag); lua_pop (L, 1);
+ RAWGET_BOOLEAN (implicit); lua_pop (L, 1);
+
+ yaml_sequence_start_event_initialize (&event, anchor, tag, implicit, yaml_style);
+ return yaml_emitter_emit (&emitter->emitter, &event);
+}
+
+
+/* Emit a SEQUENCE_END event. */
+static int
+emit_SEQUENCE_END (lua_State *L, lyaml_emitter *emitter)
+{
+ yaml_event_t event;
+ yaml_sequence_end_event_initialize (&event);
+ return yaml_emitter_emit (&emitter->emitter, &event);
+}
+
+
+/* Emit a SCALAR event. */
+static int
+emit_SCALAR (lua_State *L, lyaml_emitter *emitter)
+{
+ yaml_event_t event;
+ yaml_scalar_style_t yaml_style;
+ yaml_char_t *anchor = NULL, *tag = NULL, *value;
+ int length = 0, plain_implicit = 1, quoted_implicit = 1;
+ const char *style = NULL;
+
+ RAWGET_STRDUP (style); lua_pop (L, 1);
+
+#define MENTRY(_s) (STREQ (style, #_s)) { yaml_style = YAML_##_s##_SCALAR_STYLE; }
+ if (style == NULL) { yaml_style = YAML_ANY_SCALAR_STYLE; } else
+ if MENTRY( PLAIN ) else
+ if MENTRY( SINGLE_QUOTED ) else
+ if MENTRY( DOUBLE_QUOTED ) else
+ if MENTRY( LITERAL ) else
+ if MENTRY( FOLDED ) else
+ {
+ emitter->error++;
+ luaL_addsize (&emitter->errbuff,
+ sprintf (luaL_prepbuffer (&emitter->errbuff),
+ "invalid scalar style '%s'", style));
+ }
+#undef MENTRY
+
+ if (style) free ((void *) style);
+
+ RAWGET_YAML_CHARP (anchor); lua_pop (L, 1);
+ RAWGET_YAML_CHARP (tag); lua_pop (L, 1);
+ RAWGET_YAML_CHARP (value); length = lua_objlen (L, -1); lua_pop (L, 1);
+ RAWGET_BOOLEAN (plain_implicit);
+ RAWGET_BOOLEAN (quoted_implicit);
+
+ yaml_scalar_event_initialize (&event, anchor, tag, value, length,
+ plain_implicit, quoted_implicit, yaml_style);
+ return yaml_emitter_emit (&emitter->emitter, &event);
+}
+
+
+/* Emit an ALIAS event. */
+static int
+emit_ALIAS (lua_State *L, lyaml_emitter *emitter)
+{
+ yaml_event_t event;
+ yaml_char_t *anchor;
+
+ RAWGET_YAML_CHARP (anchor);
+
+ yaml_alias_event_initialize (&event, anchor);
+ return yaml_emitter_emit (&emitter->emitter, &event);
+}
+
+
+static int
+emit (lua_State *L)
+{
+ lyaml_emitter *emitter;
+ int yaml_ok = 0;
+ int finalize = 0;
+
+ luaL_argcheck (L, lua_istable (L, 1), 1, "expected table");
+
+ emitter = (lyaml_emitter *) lua_touserdata (L, lua_upvalueindex (1));
+
+ {
+ const char *type;
+
+ RAWGET_STRDUP (type); lua_pop (L, 1);
+
+ if (type == NULL)
+ {
+ emitter->error++;
+ luaL_addstring (&emitter->errbuff, "no type field in event table");
+ }
+#define MENTRY(_s) (STREQ (type, #_s)) { yaml_ok = emit_##_s (L, emitter); }
+ /* Minimize comparisons by putting more common types earlier. */
+ else if MENTRY( SCALAR )
+ else if MENTRY( MAPPING_START )
+ else if MENTRY( MAPPING_END )
+ else if MENTRY( SEQUENCE_START )
+ else if MENTRY( SEQUENCE_END )
+ else if MENTRY( DOCUMENT_START )
+ else if MENTRY( DOCUMENT_END )
+ else if MENTRY( STREAM_START )
+ else if MENTRY( STREAM_END )
+ else if MENTRY( ALIAS )
+#undef MENTRY
+ else
+ {
+ emitter->error++;
+ luaL_addsize (&emitter->errbuff,
+ sprintf (luaL_prepbuffer (&emitter->errbuff),
+ "invalid event type '%s'", type));
+ }
+
+ /* If the stream has finished, finalize the YAML output. */
+ if (type && STREQ (type, "STREAM_END"))
+ finalize = 1;
+
+ if (type) free ((void *) type);
+ }
+
+ /* Copy any yaml_emitter_t errors into the error buffer. */
+ if (!emitter->error && !yaml_ok)
+ {
+ if (emitter->emitter.problem)
+ luaL_addstring (&emitter->errbuff, emitter->emitter.problem);
+ else
+ luaL_addstring (&emitter->errbuff, "LibYAML call failed");
+ emitter->error++;
+ }
+
+ /* Report errors back to the caller as `false, "error message"`. */
+ if (emitter->error != 0)
+ {
+ assert (emitter->error == 1); /* bail on uncaught additional errors */
+ lua_pushboolean (L, 0);
+ luaL_pushresult (&emitter->errbuff);
+ lua_xmove (emitter->errL, L, 1);
+ return 2;
+ }
+
+ /* Return `true, "YAML string"` after accepting a STREAM_END event. */
+ if (finalize)
+ {
+ lua_pushboolean (L, 1);
+ luaL_pushresult (&emitter->yamlbuff);
+ lua_xmove (emitter->outputL, L, 1);
+ return 2;
+ }
+
+ /* Otherwise, just report success to the caller as `true`. */
+ lua_pushboolean (L, 1);
+ return 1;
+}
+
+
+static int
+append_output (void *arg, unsigned char *buff, size_t len)
+{
+ lyaml_emitter *emitter = (lyaml_emitter *) arg;
+ luaL_addlstring (&emitter->yamlbuff, (char *) buff, len);
+ return 1;
+}
+
+
+static int
+emitter_gc (lua_State *L)
+{
+ lyaml_emitter *emitter = (lyaml_emitter *) lua_touserdata (L, 1);
+
+ if (emitter)
+ yaml_emitter_delete (&emitter->emitter);
+
+ return 0;
+}
+
+
+int
+Pemitter (lua_State *L)
+{
+ lyaml_emitter *emitter;
+
+ lua_newtable (L); /* object table */
+
+ /* Create a user datum to store the emitter. */
+ emitter = (lyaml_emitter *) lua_newuserdata (L, sizeof (*emitter));
+ emitter->error = 0;
+
+ /* Initialize the emitter. */
+ if (!yaml_emitter_initialize (&emitter->emitter))
+ {
+ if (!emitter->emitter.problem)
+ emitter->emitter.problem = "cannot initialize emitter";
+ return luaL_error (L, "%s", emitter->emitter.problem);
+ }
+ yaml_emitter_set_unicode (&emitter->emitter, 1);
+ yaml_emitter_set_width (&emitter->emitter, 2);
+ yaml_emitter_set_output (&emitter->emitter, &append_output, emitter);
+
+ /* Set it's metatable, and ensure it is garbage collected properly. */
+ luaL_newmetatable (L, "lyaml.emitter");
+ lua_pushcfunction (L, emitter_gc);
+ lua_setfield (L, -2, "__gc");
+ lua_setmetatable (L, -2);
+
+ /* Set the emit method of object as a closure over the user datum, and
+ return the whole object. */
+ lua_pushcclosure (L, emit, 1);
+ lua_setfield (L, -2, "emit");
+
+ /* Set up a separate thread to collect error messages; save the thread
+ in the returned table so that it's not garbage collected when the
+ function call stack for Pemitter is cleaned up. */
+ emitter->errL = lua_newthread (L);
+ luaL_buffinit (emitter->errL, &emitter->errbuff);
+ lua_setfield (L, -2, "errthread");
+
+ /* Create a thread for the YAML buffer. */
+ emitter->outputL = lua_newthread (L);
+ luaL_buffinit (emitter->outputL, &emitter->yamlbuff);
+ lua_setfield (L, -2, "outputthread");
+
+ return 1;
+}
diff --git a/ext/yaml/lyaml.h b/ext/yaml/lyaml.h
new file mode 100644
index 000000000000..9892a48ea4c4
--- /dev/null
+++ b/ext/yaml/lyaml.h
@@ -0,0 +1,161 @@
+/*
+ * lyaml.h, libyaml parser binding for Lua
+ * Written by Gary V. Vaughan, 2013
+ *
+ * Copyright (C) 2013-2022 Gary V. Vaughan
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND 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.
+ */
+
+#ifndef LYAML_H
+#define LYAML_H 1
+
+#include <yaml.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "lyaml.h"
+
+#if LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504
+# define lua_objlen lua_rawlen
+# define lua_strlen lua_rawlen
+# define luaL_openlib(L,n,l,nup) luaL_setfuncs((L),(l),(nup))
+# define luaL_register(L,n,l) (luaL_newlib(L,l))
+#endif
+
+#ifndef STREQ
+#define STREQ !strcmp
+#endif
+#ifndef STRNEQ
+#define STRNEQ strcmp
+#endif
+
+/* NOTE: Make sure L is in scope before using these macros.
+ lua_pushyamlstr casts away the impedance mismatch between Lua's
+ signed char APIs and libYAML's unsigned char APIs. */
+
+#define lua_pushyamlstr(_s) lua_pushstring (L, (char *)(_s))
+
+#define RAWSET_BOOLEAN(_k, _v) \
+ lua_pushyamlstr (_k); \
+ lua_pushboolean (L, (_v) != 0); \
+ lua_rawset (L, -3)
+
+#define RAWSET_INTEGER(_k, _v) \
+ lua_pushyamlstr (_k); \
+ lua_pushinteger (L, (_v)); \
+ lua_rawset (L, -3)
+
+#define RAWSET_STRING(_k, _v) \
+ lua_pushyamlstr (_k); \
+ lua_pushyamlstr (_v); \
+ lua_rawset (L, -3)
+
+#define RAWSET_EVENTF(_k) \
+ lua_pushstring (L, #_k); \
+ lua_pushyamlstr (EVENTF(_k)); \
+ lua_rawset (L, -3)
+
+
+/* NOTE: Make sure L is in scope before using these macros.
+ The table value at _k is not popped from the stack for strings
+ or tables, so that we can check for an empty table entry with
+ lua_isnil (L, -1), or get the length of a string with
+ lua_objlen (L, -1) before popping. */
+
+#define RAWGET_BOOLEAN(_k) \
+ lua_pushstring (L, #_k); \
+ lua_rawget (L, -2); \
+ if (!lua_isnil (L, -1)) { \
+ _k = lua_toboolean (L, -1); \
+ } \
+ lua_pop (L, 1)
+
+#define RAWGET_INTEGER(_k) \
+ lua_pushstring (L, #_k); \
+ lua_rawget (L, -2); \
+ if (!lua_isnil (L, -1)) { \
+ _k = lua_tointeger (L, -1); \
+ } \
+ lua_pop (L, 1)
+
+#define RAWGET_STRING(_k) \
+ lua_pushstring (L, #_k); \
+ lua_rawget (L, -2); \
+ if (!lua_isnil (L, -1)) { \
+ _k = lua_tostring (L, -1); \
+ } else { _k = NULL; }
+
+#define RAWGET_STRDUP(_k) \
+ lua_pushstring (L, #_k); \
+ lua_rawget (L, -2); \
+ if (!lua_isnil (L, -1)) { \
+ _k = strdup (lua_tostring (L, -1)); \
+ } else { _k = NULL; }
+
+#define RAWGET_YAML_CHARP(_k) \
+ lua_pushstring (L, #_k); \
+ lua_rawget (L, -2); \
+ if (!lua_isnil (L, -1)) { \
+ _k = (yaml_char_t *) lua_tostring (L, -1); \
+ } else { _k = NULL; }
+
+#define RAWGETS_INTEGER(_v, _s) \
+ lua_pushstring (L, _s); \
+ lua_rawget (L, -2); \
+ if (!lua_isnil (L, -1)) { \
+ _v = lua_tointeger (L, -1); \
+ } \
+ lua_pop (L, 1)
+
+#define RAWGETS_STRDUP( _v, _s) \
+ lua_pushstring (L, _s); \
+ lua_rawget (L, -2); \
+ if (!lua_isnil (L, -1)) { \
+ _v = (yaml_char_t *) strdup (lua_tostring (L, -1)); \
+ } else { _v = NULL; }
+
+#define RAWGET_PUSHTABLE(_k) \
+ lua_pushstring (L, _k); \
+ lua_rawget (L, -2); \
+ if ((lua_type (L, -1) != LUA_TTABLE) && !lua_isnil (L, -1)) { \
+ lua_pop (L, 1); \
+ return luaL_error (L, "%s must be a table", _k); \
+ }
+
+#define ERROR_IFNIL(_err) \
+ if (lua_isnil (L, -1)) { \
+ emitter->error++; \
+ luaL_addstring (&emitter->errbuff, _err); \
+ }
+
+
+/* from emitter.c */
+extern int Pemitter (lua_State *L);
+
+/* from parser.c */
+extern void parser_init (lua_State *L);
+extern int Pparser (lua_State *L);
+
+/* from scanner.c */
+extern void scanner_init (lua_State *L);
+extern int Pscanner (lua_State *L);
+
+#endif
diff --git a/ext/yaml/parser.c b/ext/yaml/parser.c
new file mode 100644
index 000000000000..3136abd49045
--- /dev/null
+++ b/ext/yaml/parser.c
@@ -0,0 +1,410 @@
+/*
+ * parser.c, libyaml parser binding for Lua
+ * Written by Gary V. Vaughan, 2013
+ *
+ * Copyright (C) 2013-2022 Gary V. Vaughan
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND 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.
+ */
+
+#include "lyaml.h"
+
+typedef struct {
+ lua_State *L;
+ yaml_parser_t parser;
+ yaml_event_t event;
+ char validevent;
+ int document_count;
+} lyaml_parser;
+
+
+static void
+parser_delete_event (lyaml_parser *parser)
+{
+ if (parser->validevent)
+ {
+ yaml_event_delete (&parser->event);
+ parser->validevent = 0;
+ }
+}
+
+/* With the event result table on the top of the stack, insert
+ a mark entry. */
+static void
+parser_set_mark (lua_State *L, const char *k, yaml_mark_t mark)
+{
+ lua_pushstring (L, k);
+ lua_createtable (L, 0, 3);
+#define MENTRY(_s) RAWSET_INTEGER(#_s, mark._s)
+ MENTRY( index );
+ MENTRY( line );
+ MENTRY( column );
+#undef MENTRY
+ lua_rawset (L, -3);
+}
+
+/* Push a new event table, pre-populated with shared elements. */
+static void
+parser_push_eventtable (lyaml_parser *parser, const char *v, int n)
+{
+ lua_State *L = parser->L;
+
+ lua_createtable (L, 0, n + 3);
+ RAWSET_STRING ("type", v);
+#define MENTRY(_s) parser_set_mark (L, #_s, parser->event._s)
+ MENTRY( start_mark );
+ MENTRY( end_mark );
+#undef MENTRY
+}
+
+static void
+parse_STREAM_START (lyaml_parser *parser)
+{
+#define EVENTF(_f) (parser->event.data.stream_start._f)
+ lua_State *L = parser->L;
+ const char *encoding;
+
+ switch (EVENTF (encoding))
+ {
+#define MENTRY(_s) \
+ case YAML_##_s##_ENCODING: encoding = #_s; break
+
+ MENTRY( ANY );
+ MENTRY( UTF8 );
+ MENTRY( UTF16LE );
+ MENTRY( UTF16BE );
+#undef MENTRY
+
+ default:
+ lua_pushfstring (L, "invalid encoding %d", EVENTF (encoding));
+ lua_error (L);
+ }
+
+ parser_push_eventtable (parser, "STREAM_START", 1);
+ RAWSET_STRING ("encoding", encoding);
+#undef EVENTF
+}
+
+/* With the tag list on the top of the stack, append TAG. */
+static void
+parser_append_tag (lua_State *L, yaml_tag_directive_t tag)
+{
+ lua_createtable (L, 0, 2);
+#define MENTRY(_s) RAWSET_STRING(#_s, tag._s)
+ MENTRY( handle );
+ MENTRY( prefix );
+#undef MENTRY
+ lua_rawseti (L, -2, lua_objlen (L, -2) + 1);
+}
+
+static void
+parse_DOCUMENT_START (lyaml_parser *parser)
+{
+#define EVENTF(_f) (parser->event.data.document_start._f)
+ lua_State *L = parser->L;
+
+ /* increment document count */
+ parser->document_count++;
+
+ parser_push_eventtable (parser, "DOCUMENT_START", 1);
+ RAWSET_BOOLEAN ("implicit", EVENTF (implicit));
+
+ /* version_directive = { major = M, minor = N } */
+ if (EVENTF (version_directive))
+ {
+ lua_pushliteral (L, "version_directive");
+ lua_createtable (L, 0, 2);
+#define MENTRY(_s) RAWSET_INTEGER(#_s, EVENTF (version_directive->_s))
+ MENTRY( major );
+ MENTRY( minor );
+#undef MENTRY
+ lua_rawset (L, -3);
+ }
+
+ /* tag_directives = { {handle = H1, prefix = P1}, ... } */
+ if (EVENTF (tag_directives.start) &&
+ EVENTF (tag_directives.end)) {
+ yaml_tag_directive_t *cur;
+
+ lua_pushliteral (L, "tag_directives");
+ lua_newtable (L);
+ for (cur = EVENTF (tag_directives.start);
+ cur != EVENTF (tag_directives.end);
+ cur = cur + 1)
+ {
+ parser_append_tag (L, *cur);
+ }
+ lua_rawset (L, -3);
+ }
+#undef EVENTF
+}
+
+static void
+parse_DOCUMENT_END (lyaml_parser *parser)
+{
+#define EVENTF(_f) (parser->event.data.document_end._f)
+ lua_State *L = parser->L;
+
+ parser_push_eventtable (parser, "DOCUMENT_END", 1);
+ RAWSET_BOOLEAN ("implicit", EVENTF (implicit));
+#undef EVENTF
+}
+
+static void
+parse_ALIAS (lyaml_parser *parser)
+{
+#define EVENTF(_f) (parser->event.data.alias._f)
+ lua_State *L = parser->L;
+
+ parser_push_eventtable (parser, "ALIAS", 1);
+ RAWSET_EVENTF (anchor);
+#undef EVENTF
+}
+
+static void
+parse_SCALAR (lyaml_parser *parser)
+{
+#define EVENTF(_f) (parser->event.data.scalar._f)
+ lua_State *L = parser->L;
+ const char *style;
+
+ switch (EVENTF (style))
+ {
+#define MENTRY(_s) \
+ case YAML_##_s##_SCALAR_STYLE: style = #_s; break
+
+ MENTRY( ANY );
+ MENTRY( PLAIN );
+ MENTRY( SINGLE_QUOTED );
+ MENTRY( DOUBLE_QUOTED );
+ MENTRY( LITERAL );
+ MENTRY( FOLDED );
+#undef MENTRY
+
+ default:
+ lua_pushfstring (L, "invalid sequence style %d", EVENTF (style));
+ lua_error (L);
+ }
+
+
+ parser_push_eventtable (parser, "SCALAR", 6);
+ RAWSET_EVENTF (anchor);
+ RAWSET_EVENTF (tag);
+ RAWSET_EVENTF (value);
+
+ RAWSET_BOOLEAN ("plain_implicit", EVENTF (plain_implicit));
+ RAWSET_BOOLEAN ("quoted_implicit", EVENTF (quoted_implicit));
+ RAWSET_STRING ("style", style);
+#undef EVENTF
+}
+
+static void
+parse_SEQUENCE_START (lyaml_parser *parser)
+{
+#define EVENTF(_f) (parser->event.data.sequence_start._f)
+ lua_State *L = parser->L;
+ const char *style;
+
+ switch (EVENTF (style))
+ {
+#define MENTRY(_s) \
+ case YAML_##_s##_SEQUENCE_STYLE: style = #_s; break
+
+ MENTRY( ANY );
+ MENTRY( BLOCK );
+ MENTRY( FLOW );
+#undef MENTRY
+
+ default:
+ lua_pushfstring (L, "invalid sequence style %d", EVENTF (style));
+ lua_error (L);
+ }
+
+ parser_push_eventtable (parser, "SEQUENCE_START", 4);
+ RAWSET_EVENTF (anchor);
+ RAWSET_EVENTF (tag);
+ RAWSET_BOOLEAN ("implicit", EVENTF (implicit));
+ RAWSET_STRING ("style", style);
+#undef EVENTF
+}
+
+static void
+parse_MAPPING_START (lyaml_parser *parser)
+{
+#define EVENTF(_f) (parser->event.data.mapping_start._f)
+ lua_State *L = parser->L;
+ const char *style;
+
+ switch (EVENTF (style))
+ {
+#define MENTRY(_s) \
+ case YAML_##_s##_MAPPING_STYLE: style = #_s; break
+
+ MENTRY( ANY );
+ MENTRY( BLOCK );
+ MENTRY( FLOW );
+#undef MENTRY
+
+ default:
+ lua_pushfstring (L, "invalid mapping style %d", EVENTF (style));
+ lua_error (L);
+ }
+
+ parser_push_eventtable (parser, "MAPPING_START", 4);
+ RAWSET_EVENTF (anchor);
+ RAWSET_EVENTF (tag);
+ RAWSET_BOOLEAN ("implicit", EVENTF (implicit));
+ RAWSET_STRING ("style", style);
+#undef EVENTF
+}
+
+static void
+parser_generate_error_message (lyaml_parser *parser)
+{
+ yaml_parser_t *P = &parser->parser;
+ char buf[256];
+ luaL_Buffer b;
+
+ luaL_buffinit (parser->L, &b);
+ luaL_addstring (&b, P->problem ? P->problem : "A problem");
+ snprintf (buf, sizeof (buf), " at document: %d", parser->document_count);
+ luaL_addstring (&b, buf);
+
+ if (P->problem_mark.line || P->problem_mark.column)
+ {
+ snprintf (buf, sizeof (buf), ", line: %lu, column: %lu",
+ (unsigned long) P->problem_mark.line + 1,
+ (unsigned long) P->problem_mark.column + 1);
+ luaL_addstring (&b, buf);
+ }
+ luaL_addstring (&b, "\n");
+
+ if (P->context)
+ {
+ snprintf (buf, sizeof (buf), "%s at line: %lu, column: %lu\n",
+ P->context,
+ (unsigned long) P->context_mark.line + 1,
+ (unsigned long) P->context_mark.column + 1);
+ luaL_addstring (&b, buf);
+ }
+
+ luaL_pushresult (&b);
+}
+
+static int
+event_iter (lua_State *L)
+{
+ lyaml_parser *parser = (lyaml_parser *)lua_touserdata(L, lua_upvalueindex(1));
+ char *str;
+
+ parser_delete_event (parser);
+ if (yaml_parser_parse (&parser->parser, &parser->event) != 1)
+ {
+ parser_generate_error_message (parser);
+ return lua_error (L);
+ }
+
+ parser->validevent = 1;
+
+ lua_newtable (L);
+ lua_pushliteral (L, "type");
+
+ switch (parser->event.type)
+ {
+ /* First the simple events, generated right here... */
+#define MENTRY(_s) \
+ case YAML_##_s##_EVENT: parser_push_eventtable (parser, #_s, 0); break
+ MENTRY( STREAM_END );
+ MENTRY( SEQUENCE_END );
+ MENTRY( MAPPING_END );
+#undef MENTRY
+
+ /* ...then the complex events, generated by a function call. */
+#define MENTRY(_s) \
+ case YAML_##_s##_EVENT: parse_##_s (parser); break
+ MENTRY( STREAM_START );
+ MENTRY( DOCUMENT_START );
+ MENTRY( DOCUMENT_END );
+ MENTRY( ALIAS );
+ MENTRY( SCALAR );
+ MENTRY( SEQUENCE_START );
+ MENTRY( MAPPING_START );
+#undef MENTRY
+
+ case YAML_NO_EVENT:
+ lua_pushnil (L);
+ break;
+ default:
+ lua_pushfstring (L, "invalid event %d", parser->event.type);
+ return lua_error (L);
+ }
+
+ return 1;
+}
+
+static int
+parser_gc (lua_State *L)
+{
+ lyaml_parser *parser = (lyaml_parser *) lua_touserdata (L, 1);
+
+ if (parser)
+ {
+ parser_delete_event (parser);
+ yaml_parser_delete (&parser->parser);
+ }
+ return 0;
+}
+
+void
+parser_init (lua_State *L)
+{
+ luaL_newmetatable(L, "lyaml.parser");
+ lua_pushcfunction(L, parser_gc);
+ lua_setfield(L, -2, "__gc");
+}
+
+int
+Pparser (lua_State *L)
+{
+ lyaml_parser *parser;
+ const unsigned char *str;
+
+ /* requires a single string type argument */
+ luaL_argcheck (L, lua_isstring (L, 1), 1, "must provide a string argument");
+ str = (const unsigned char *) lua_tostring (L, 1);
+
+ /* create a user datum to store the parser */
+ parser = (lyaml_parser *) lua_newuserdata (L, sizeof (*parser));
+ memset ((void *) parser, 0, sizeof (*parser));
+ parser->L = L;
+
+ /* set its metatable */
+ luaL_getmetatable (L, "lyaml.parser");
+ lua_setmetatable (L, -2);
+
+ /* try to initialize the parser */
+ if (yaml_parser_initialize (&parser->parser) == 0)
+ luaL_error (L, "cannot initialize parser for %s", str);
+ yaml_parser_set_input_string (&parser->parser, str, lua_strlen (L, 1));
+
+ /* create and return the iterator function, with the loader userdatum as
+ its sole upvalue */
+ lua_pushcclosure (L, event_iter, 1);
+ return 1;
+}
diff --git a/ext/yaml/scanner.c b/ext/yaml/scanner.c
new file mode 100644
index 000000000000..6d7276517f42
--- /dev/null
+++ b/ext/yaml/scanner.c
@@ -0,0 +1,340 @@
+/*
+ * scanner.c, libyaml scanner binding for Lua
+ * Written by Gary V. Vaughan, 2013
+ *
+ * Copyright (C) 2013-2022 Gary V. Vaughan
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND 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.
+ */
+
+#include "lyaml.h"
+
+
+typedef struct {
+ lua_State *L;
+ yaml_parser_t parser;
+ yaml_token_t token;
+ char validtoken;
+ int document_count;
+} lyaml_scanner;
+
+
+static void
+scanner_delete_token (lyaml_scanner *scanner)
+{
+ if (scanner->validtoken)
+ {
+ yaml_token_delete (&scanner->token);
+ scanner->validtoken = 0;
+ }
+}
+
+/* With the token result table on the top of the stack, insert
+ a mark entry. */
+static void
+scanner_set_mark (lua_State *L, const char *k, yaml_mark_t mark)
+{
+ lua_pushstring (L, k);
+ lua_createtable (L, 0, 3);
+#define MENTRY(_s) RAWSET_INTEGER (#_s, mark._s)
+ MENTRY( index );
+ MENTRY( line );
+ MENTRY( column );
+#undef MENTRY
+ lua_rawset (L, -3);
+}
+
+/* Push a new token table, pre-populated with shared elements. */
+static void
+scanner_push_tokentable (lyaml_scanner *scanner, const char *v, int n)
+{
+ lua_State *L = scanner->L;
+
+ lua_createtable (L, 0, n + 3);
+ RAWSET_STRING ("type", v);
+
+#define MENTRY(_s) scanner_set_mark (L, #_s, scanner->token._s)
+ MENTRY( start_mark );
+ MENTRY( end_mark );
+#undef MENTRY
+}
+
+static void
+scan_STREAM_START (lyaml_scanner *scanner)
+{
+#define EVENTF(_f) (scanner->token.data.stream_start._f)
+ lua_State *L = scanner->L;
+ const char *encoding;
+
+ switch (EVENTF (encoding))
+ {
+#define MENTRY(_s) \
+ case YAML_##_s##_ENCODING: encoding = #_s; break
+ MENTRY( UTF8 );
+ MENTRY( UTF16LE );
+ MENTRY( UTF16BE );
+#undef MENTRY
+
+ default:
+ lua_pushfstring (L, "invalid encoding %d", EVENTF (encoding));
+ lua_error (L);
+ }
+
+ scanner_push_tokentable (scanner, "STREAM_START", 1);
+ RAWSET_STRING ("encoding", encoding);
+#undef EVENTF
+}
+
+static void
+scan_VERSION_DIRECTIVE (lyaml_scanner *scanner)
+{
+#define EVENTF(_f) (scanner->token.data.version_directive._f)
+ lua_State *L = scanner->L;
+
+ scanner_push_tokentable (scanner, "VERSION_DIRECTIVE", 2);
+
+#define MENTRY(_s) RAWSET_INTEGER (#_s, EVENTF (_s))
+ MENTRY( major );
+ MENTRY( minor );
+#undef MENTRY
+#undef EVENTF
+}
+
+static void
+scan_TAG_DIRECTIVE (lyaml_scanner *scanner)
+{
+#define EVENTF(_f) (scanner->token.data.tag_directive._f)
+ lua_State *L = scanner->L;
+
+ scanner_push_tokentable (scanner, "TAG_DIRECTIVE", 2);
+ RAWSET_EVENTF( handle );
+ RAWSET_EVENTF( prefix );
+#undef EVENTF
+}
+
+static void
+scan_ALIAS (lyaml_scanner *scanner)
+{
+#define EVENTF(_f) (scanner->token.data.alias._f)
+ lua_State *L = scanner->L;
+
+ scanner_push_tokentable (scanner, "ALIAS", 1);
+ RAWSET_EVENTF (value);
+#undef EVENTF
+}
+
+static void
+scan_ANCHOR (lyaml_scanner *scanner)
+{
+#define EVENTF(_f) (scanner->token.data.anchor._f)
+ lua_State *L = scanner->L;
+
+ scanner_push_tokentable (scanner, "ANCHOR", 1);
+ RAWSET_EVENTF (value);
+#undef EVENTF
+}
+
+static void
+scan_TAG(lyaml_scanner *scanner)
+{
+#define EVENTF(_f) (scanner->token.data.tag._f)
+ lua_State *L = scanner->L;
+
+ scanner_push_tokentable (scanner, "TAG", 2);
+ RAWSET_EVENTF( handle );
+ RAWSET_EVENTF( suffix );
+#undef EVENTF
+}
+
+static void
+scan_SCALAR (lyaml_scanner *scanner)
+{
+#define EVENTF(_f) (scanner->token.data.scalar._f)
+ lua_State *L = scanner->L;
+ const char *style;
+
+ switch (EVENTF (style))
+ {
+#define MENTRY(_s) \
+ case YAML_##_s##_SCALAR_STYLE: style = #_s; break
+
+ MENTRY( PLAIN );
+ MENTRY( SINGLE_QUOTED );
+ MENTRY( DOUBLE_QUOTED );
+ MENTRY( LITERAL );
+ MENTRY( FOLDED );
+#undef MENTRY
+
+ default:
+ lua_pushfstring (L, "invalid scalar style %d", EVENTF (style));
+ lua_error (L);
+ }
+
+ scanner_push_tokentable (scanner, "SCALAR", 3);
+ RAWSET_EVENTF (value);
+ RAWSET_INTEGER ("length", EVENTF (length));
+ RAWSET_STRING ("style", style);
+#undef EVENTF
+}
+
+static void
+scanner_generate_error_message (lyaml_scanner *scanner)
+{
+ yaml_parser_t *P = &scanner->parser;
+ char buf[256];
+ luaL_Buffer b;
+
+ luaL_buffinit (scanner->L, &b);
+ luaL_addstring (&b, P->problem ? P->problem : "A problem");
+ snprintf (buf, sizeof (buf), " at document: %d", scanner->document_count);
+ luaL_addstring (&b, buf);
+
+ if (P->problem_mark.line || P->problem_mark.column)
+ {
+ snprintf (buf, sizeof (buf), ", line: %lu, column: %lu",
+ (unsigned long) P->problem_mark.line + 1,
+ (unsigned long) P->problem_mark.column + 1);
+ luaL_addstring (&b, buf);
+ }
+ luaL_addstring (&b, "\n");
+
+ if (P->context)
+ {
+ snprintf (buf, sizeof (buf), "%s at line: %lu, column: %lu\n",
+ P->context,
+ (unsigned long) P->context_mark.line + 1,
+ (unsigned long) P->context_mark.column + 1);
+ luaL_addstring (&b, buf);
+ }
+
+ luaL_pushresult (&b);
+}
+
+static int
+token_iter (lua_State *L)
+{
+ lyaml_scanner *scanner = (lyaml_scanner *)lua_touserdata(L, lua_upvalueindex(1));
+ char *str;
+
+ scanner_delete_token (scanner);
+ if (yaml_parser_scan (&scanner->parser, &scanner->token) != 1)
+ {
+ scanner_generate_error_message (scanner);
+ return lua_error (L);
+ }
+
+ scanner->validtoken = 1;
+
+ lua_newtable (L);
+ lua_pushliteral (L, "type");
+
+ switch (scanner->token.type)
+ {
+ /* First the simple tokens, generated right here... */
+#define MENTRY(_s) \
+ case YAML_##_s##_TOKEN: scanner_push_tokentable (scanner, #_s, 0); break
+ MENTRY( STREAM_END );
+ MENTRY( DOCUMENT_START );
+ MENTRY( DOCUMENT_END );
+ MENTRY( BLOCK_SEQUENCE_START );
+ MENTRY( BLOCK_MAPPING_START );
+ MENTRY( BLOCK_END );
+ MENTRY( FLOW_SEQUENCE_START );
+ MENTRY( FLOW_SEQUENCE_END );
+ MENTRY( FLOW_MAPPING_START );
+ MENTRY( FLOW_MAPPING_END );
+ MENTRY( BLOCK_ENTRY );
+ MENTRY( FLOW_ENTRY );
+ MENTRY( KEY );
+ MENTRY( VALUE );
+#undef MENTRY
+
+ /* ...then the complex tokens, generated by a function call. */
+#define MENTRY(_s) \
+ case YAML_##_s##_TOKEN: scan_##_s (scanner); break
+ MENTRY( STREAM_START );
+ MENTRY( VERSION_DIRECTIVE );
+ MENTRY( TAG_DIRECTIVE );
+ MENTRY( ALIAS );
+ MENTRY( ANCHOR );
+ MENTRY( TAG );
+ MENTRY( SCALAR );
+#undef MENTRY
+
+ case YAML_NO_TOKEN:
+ lua_pushnil (L);
+ break;
+ default:
+ lua_pushfstring (L, "invalid token %d", scanner->token.type);
+ return lua_error (L);
+ }
+
+ return 1;
+}
+
+static int
+scanner_gc (lua_State *L)
+{
+ lyaml_scanner *scanner = (lyaml_scanner *) lua_touserdata (L, 1);
+
+ if (scanner)
+ {
+ scanner_delete_token (scanner);
+ yaml_parser_delete (&scanner->parser);
+ }
+ return 0;
+}
+
+void
+scanner_init (lua_State *L)
+{
+ luaL_newmetatable (L, "lyaml.scanner");
+ lua_pushcfunction (L, scanner_gc);
+ lua_setfield (L, -2, "__gc");
+}
+
+int
+Pscanner (lua_State *L)
+{
+ lyaml_scanner *scanner;
+ const unsigned char *str;
+
+ /* requires a single string type argument */
+ luaL_argcheck (L, lua_isstring (L, 1), 1, "must provide a string argument");
+ str = (const unsigned char *) lua_tostring (L, 1);
+
+ /* create a user datum to store the scanner */
+ scanner = (lyaml_scanner *) lua_newuserdata (L, sizeof (*scanner));
+ memset ((void *) scanner, 0, sizeof (*scanner));
+ scanner->L = L;
+
+ /* set its metatable */
+ luaL_getmetatable (L, "lyaml.scanner");
+ lua_setmetatable (L, -2);
+
+ /* try to initialize the scanner */
+ if (yaml_parser_initialize (&scanner->parser) == 0)
+ luaL_error (L, "cannot initialize parser for %s", str);
+ yaml_parser_set_input_string (&scanner->parser, str, lua_strlen (L, 1));
+
+ /* create and return the iterator function, with the loader userdatum as
+ its sole upvalue */
+ lua_pushcclosure (L, token_iter, 1);
+ return 1;
+}
diff --git a/ext/yaml/yaml.c b/ext/yaml/yaml.c
new file mode 100644
index 000000000000..54478610134f
--- /dev/null
+++ b/ext/yaml/yaml.c
@@ -0,0 +1,66 @@
+/*
+ * yaml.c, LibYAML binding for Lua
+ * Written by Andrew Danforth, 2009
+ *
+ * Copyright (C) 2014-2022 Gary V. Vaughan
+ * Copyright (C) 2009 Andrew Danforth
+ *
+ * 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.
+ *
+ * Portions of this software were inspired by Perl's YAML::LibYAML module by
+ * Ingy döt Net <ingy@cpan.org>
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <lualib.h>
+
+#include "lyaml.h"
+
+#define MYNAME "yaml"
+#define MYVERSION MYNAME " library for " LUA_VERSION " / " VERSION
+
+#define LYAML__STR_1(_s) (#_s + 1)
+#define LYAML_STR_1(_s) LYAML__STR_1(_s)
+
+static const luaL_Reg R[] =
+{
+#define MENTRY(_s) {LYAML_STR_1(_s), (_s)}
+ MENTRY( Pemitter ),
+ MENTRY( Pparser ),
+ MENTRY( Pscanner ),
+#undef MENTRY
+ {NULL, NULL}
+};
+
+LUALIB_API int
+luaopen_yaml (lua_State *L)
+{
+ parser_init (L);
+ scanner_init (L);
+
+ luaL_register(L, "yaml", R);
+
+ lua_pushliteral(L, MYVERSION);
+ lua_setfield(L, -2, "version");
+
+ return 1;
+}
diff --git a/lib/lyaml/explicit.lua b/lib/lyaml/explicit.lua
new file mode 100644
index 000000000000..98a38331ead6
--- /dev/null
+++ b/lib/lyaml/explicit.lua
@@ -0,0 +1,120 @@
+-- LYAML parse explicit token values.
+-- Written by Gary V. Vaughan, 2015
+--
+-- Copyright(C) 2015-2022 Gary V. Vaughan
+--
+-- Permission is hereby granted, free of charge, to any person obtaining
+-- a copy of this software and associated documentation files(the
+-- "Software"), to deal in the Software without restriction, including
+-- without limitation the rights to use, copy, modify, merge, publish,
+-- distribute, sublicense, and/or sell copies of the Software, and to
+-- permit persons to whom the Software is furnished to do so, subject to
+-- the following conditions:
+--
+-- The above copyright notice and this permission notice shall be
+-- included in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 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.
+
+--- @module lyaml.explicit
+
+local functional = require 'lyaml.functional'
+local implicit = require 'lyaml.implicit'
+
+local NULL = functional.NULL
+local anyof = functional.anyof
+local id = functional.id
+
+
+local yn = {y=true, Y=true, n=false, N=false}
+
+
+--- Parse the value following an explicit `!!bool` tag.
+-- @function bool
+-- @param value token
+-- @treturn[1] bool boolean equivalent, if a valid value was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_bool = explicit.bool(tagarg)
+local bool = anyof {
+ implicit.bool,
+ function(x) return yn[x] end,
+}
+
+
+--- Return a function that converts integer results to equivalent float.
+-- @tparam function fn token parsing function
+-- @treturn function new function that converts int results to float
+-- @usage maybe_float = maybefloat(implicit.decimal)(tagarg)
+local function maybefloat(fn)
+ return function(...)
+ local r = fn(...)
+ if type(r) == 'number' then
+ return r + 0.0
+ end
+ end
+end
+
+
+--- Parse the value following an explicit `!!float` tag.
+-- @function float
+-- @param value token
+-- @treturn[1] number float equivalent, if a valid value was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_float = explicit.float(tagarg)
+local float = anyof {
+ implicit.float,
+ implicit.nan,
+ implicit.inf,
+ maybefloat(implicit.octal),
+ maybefloat(implicit.decimal),
+ maybefloat(implicit.hexadecimal),
+ maybefloat(implicit.binary),
+ implicit.sexfloat,
+}
+
+
+--- Parse the value following an explicit `!!int` tag.
+-- @function int
+-- @param value token
+-- @treturn[1] int integer equivalent, if a valid value was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_int = explicit.int(tagarg)
+local int = anyof {
+ implicit.octal,
+ implicit.decimal,
+ implicit.hexadecimal,
+ implicit.binary,
+ implicit.sexagesimal,
+}
+
+
+--- Parse an explicit `!!null` tag.
+-- @treturn lyaml.null
+-- @usage null = explicit.null(tagarg)
+local function null()
+ return NULL
+end
+
+
+--- Parse the value following an explicit `!!str` tag.
+-- @function str
+-- @tparam string value token
+-- @treturn string *value* which was a string already
+-- @usage tagarg = explicit.str(tagarg)
+local str = id
+
+
+--- @export
+return {
+ bool = bool,
+ float = float,
+ int = int,
+ null = null,
+ str = str,
+}
diff --git a/lib/lyaml/functional.lua b/lib/lyaml/functional.lua
new file mode 100644
index 000000000000..556e9489505a
--- /dev/null
+++ b/lib/lyaml/functional.lua
@@ -0,0 +1,87 @@
+-- Minimal functional programming utilities.
+-- Written by Gary V. Vaughan, 2015
+--
+-- Copyright(C) 2015-2022 Gary V. Vaughan
+--
+-- Permission is hereby granted, free of charge, to any person obtaining
+-- a copy of this software and associated documentation files(the
+-- "Software"), to deal in the Software without restriction, including
+-- without limitation the rights to use, copy, modify, merge, publish,
+-- distribute, sublicense, and/or sell copies of the Software, and to
+-- permit persons to whom the Software is furnished to do so, subject to
+-- the following conditions:
+--
+-- The above copyright notice and this permission notice shall be
+-- included in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 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.
+
+--- @module lyaml.functional
+
+
+--- `lyaml.null` value.
+-- @table NULL
+local NULL = setmetatable({}, {_type='LYAML null'})
+
+
+--- `lyaml.null` predicate.
+-- @param x operand
+-- @treturn bool `true` if *x* is `lyaml.null`.
+local function isnull(x)
+ return(getmetatable(x) or {})._type == 'LYAML null'
+end
+
+
+--- Callable predicate.
+-- @param x operand
+-- @treturn bool `true` if *x* is a function has a __call metamethod
+-- @usage r = iscallable(x) and x(...)
+local function iscallable(x)
+ if type(x) ~= 'function' then
+ x =(getmetatable(x) or {}).__call
+ end
+ if type(x) == 'function' then
+ return x
+ end
+end
+
+
+--- Compose a function to try each callable with supplied args.
+-- @tparam table fns list of functions to try
+-- @treturn function a new function to call *...* functions, stopping
+-- and returning the first non-nil result, if any
+local function anyof(fns)
+ return function(...)
+ for _, fn in ipairs(fns) do
+ if iscallable(fn) then
+ local r = fn(...)
+ if r ~= nil then
+ return r
+ end
+ end
+ end
+ end
+end
+
+
+--- Return arguments unchanged.
+-- @param ... arguments
+-- @return *...*
+local function id(...)
+ return ...
+end
+
+--- @export
+return {
+ NULL = NULL,
+ anyof = anyof,
+ id = id,
+ iscallable = iscallable,
+ isnull = isnull,
+}
diff --git a/lib/lyaml/implicit.lua b/lib/lyaml/implicit.lua
new file mode 100644
index 000000000000..fe58025b560d
--- /dev/null
+++ b/lib/lyaml/implicit.lua
@@ -0,0 +1,283 @@
+-- LYAML parse implicit type tokens.
+-- Written by Gary V. Vaughan, 2015
+--
+-- Copyright(C) 2015-2022 Gary V. Vaughan
+--
+-- Permission is hereby granted, free of charge, to any person obtaining
+-- a copy of this software and associated documentation files(the
+-- "Software"), to deal in the Software without restriction, including
+-- without limitation the rights to use, copy, modify, merge, publish,
+-- distribute, sublicense, and/or sell copies of the Software, and to
+-- permit persons to whom the Software is furnished to do so, subject to
+-- the following conditions:
+--
+-- The above copyright notice and this permission notice shall be
+-- included in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 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.
+
+--- @module lyaml.implicit
+
+
+local NULL = require 'lyaml.functional'.NULL
+local find = string.find
+local floor = math.floor
+local gsub = string.gsub
+local sub = string.sub
+
+local tointeger = (function(f)
+ if not tointeger then
+ -- No host tointeger implementation, use our own.
+ return function(x)
+ if type(x) == 'number' and x - floor(x) == 0.0 then
+ return x
+ end
+ end
+
+ elseif f '1' ~= nil then
+ -- Don't perform implicit string-to-number conversion!
+ return function(x)
+ if type(x) == 'number' then
+ return tointeger(x)
+ end
+ end
+ end
+
+ -- Host tointeger is good!
+ return f
+end)(math.tointeger)
+
+
+local function int(x)
+ local r = tonumber(x)
+ if r ~= nil then
+ return tointeger(r)
+ end
+end
+
+
+local is_null = {['']=true, ['~']=true, null=true, Null=true, NULL=true}
+
+
+--- Parse a null token to a null value.
+-- @param value token
+-- @return[1] lyaml.null, for an empty string or literal ~
+-- @return[2] nil otherwise, nil
+-- @usage maybe_null = implicit.null(token)
+local function null(value)
+ if is_null[value] then
+ return NULL
+ end
+end
+
+
+local to_bool = {
+ ['true'] = true, True = true, TRUE = true,
+ ['false'] = false, False = false, FALSE = false,
+ yes = true, Yes = true, YES = true,
+ no = false, No = false, NO = false,
+ on = true, On = true, ON = true,
+ off = false, Off = false, OFF = false,
+}
+
+
+--- Parse a boolean token to the equivalent value.
+-- Treats capilalized, lower and upper-cased variants of true/false,
+-- yes/no or on/off tokens as boolean `true` and `false` values.
+-- @param value token
+-- @treturn[1] bool if a valid boolean token was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_bool = implicit.bool(token)
+local function bool(value)
+ return to_bool[value]
+end
+
+
+--- Parse a binary token, such as '0b1010\_0111\_0100\_1010\_1110'.
+-- @tparam string value token
+-- @treturn[1] int integer equivalent, if a valid token was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_int = implicit.binary(value)
+local function binary(value)
+ local r
+ gsub(value, '^([+-]?)0b_*([01][01_]+)$', function(sign, rest)
+ r = 0
+ gsub(rest, '_*(.)', function(digit)
+ r = r * 2 + int(digit)
+ end)
+ if sign == '-' then
+ r = r * -1
+ end
+ end)
+ return r
+end
+
+
+--- Parse an octal token, such as '012345'.
+-- @tparam string value token
+-- @treturn[1] int integer equivalent, if a valid token was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_int = implicit.octal(value)
+local function octal(value)
+ local r
+ gsub(value, '^([+-]?)0_*([0-7][0-7_]*)$', function(sign, rest)
+ r = 0
+ gsub(rest, '_*(.)', function(digit)
+ r = r * 8 + int(digit)
+ end)
+ if sign == '-' then
+ r = r * -1
+ end
+ end)
+ return r
+end
+
+
+--- Parse a decimal token, such as '0' or '12345'.
+-- @tparam string value token
+-- @treturn[1] int integer equivalent, if a valid token was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_int = implicit.decimal(value)
+local function decimal(value)
+ local r
+ gsub(value, '^([+-]?)_*([0-9][0-9_]*)$', function(sign, rest)
+ rest = gsub(rest, '_', '')
+ if rest == '0' or #rest > 1 or sub(rest, 1, 1) ~= '0' then
+ r = int(rest)
+ if sign == '-' then
+ r = r * -1
+ end
+ end
+ end)
+ return r
+end
+
+
+--- Parse a hexadecimal token, such as '0xdeadbeef'.
+-- @tparam string value token
+-- @treturn[1] int integer equivalent, if a valid token was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_int = implicit.hexadecimal(value)
+local function hexadecimal(value)
+ local r
+ gsub(value, '^([+-]?)(0x_*[0-9a-fA-F][0-9a-fA-F_]*)$', function(sign, rest)
+ rest = gsub(rest, '_', '')
+ r = int(rest)
+ if sign == '-' then
+ r = r * -1
+ end
+ end)
+ return r
+end
+
+
+--- Parse a sexagesimal token, such as '190:20:30'.
+-- Useful for times and angles.
+-- @tparam string value token
+-- @treturn[1] int integer equivalent, if a valid token was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_int = implicit.sexagesimal(value)
+local function sexagesimal(value)
+ local r
+ gsub(value, '^([+-]?)([0-9]+:[0-5]?[0-9][:0-9]*)$', function(sign, rest)
+ r = 0
+ gsub(rest, '([0-9]+):?', function(digit)
+ r = r * 60 + int(digit)
+ end)
+ if sign == '-' then
+ r = r * -1
+ end
+ end)
+ return r
+end
+
+
+local isnan = {['.nan']=true, ['.NaN']=true, ['.NAN']=true}
+
+
+--- Parse a `nan` token.
+-- @tparam string value token
+-- @treturn[1] nan not-a-number, if a valid token was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_nan = implicit.nan(value)
+local function nan(value)
+ if isnan[value] then
+ return 0/0
+ end
+end
+
+
+local isinf = {
+ ['.inf'] = math.huge, ['.Inf'] = math.huge, ['.INF'] = math.huge,
+ ['+.inf'] = math.huge, ['+.Inf'] = math.huge, ['+.INF'] = math.huge,
+ ['-.inf'] = -math.huge, ['-.Inf'] = -math.huge, ['-.INF'] = -math.huge,
+}
+
+
+--- Parse a signed `inf` token.
+-- @tparam string value token
+-- @treturn[1] number plus/minus-infinity, if a valid token was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_inf = implicit.inf(value)
+local function inf(value)
+ return isinf[value]
+end
+
+
+--- Parse a floating point number token, such as '1e-3' or '-0.12'.
+-- @tparam string value token
+-- @treturn[1] number float equivalent, if a valid token was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_float = implicit.float(value)
+local function float(value)
+ local r = tonumber((gsub(value, '_', '')))
+ if r and find(value, '[%.eE]') then
+ return r
+ end
+end
+
+
+--- Parse a sexagesimal float, such as '190:20:30.15'.
+-- Useful for times and angles.
+-- @tparam string value token
+-- @treturn[1] number float equivalent, if a valid token was recognized
+-- @treturn[2] nil otherwise, nil
+-- @usage maybe_float = implicit.sexfloat(value)
+local function sexfloat(value)
+ local r
+ gsub(value, '^([+-]?)([0-9]+:[0-5]?[0-9][:0-9]*)(%.[0-9]+)$',
+ function(sign, rest, float)
+ r = 0
+ gsub(rest, '([0-9]+):?', function(digit)
+ r = r * 60 + int(digit)
+ end)
+ r = r + tonumber(float)
+ if sign == '-' then
+ r = r * -1
+ end
+ end
+ )
+ return r
+end
+
+
+--- @export
+return {
+ binary = binary,
+ decimal = decimal,
+ float = float,
+ hexadecimal = hexadecimal,
+ inf = inf,
+ nan = nan,
+ null = null,
+ octal = octal,
+ sexagesimal = sexagesimal,
+ sexfloat = sexfloat,
+ bool = bool,
+}
diff --git a/lib/lyaml/init.lua b/lib/lyaml/init.lua
new file mode 100644
index 000000000000..95e4036ea7c9
--- /dev/null
+++ b/lib/lyaml/init.lua
@@ -0,0 +1,534 @@
+-- Transform between YAML 1.1 streams and Lua table representations.
+-- Written by Gary V. Vaughan, 2013
+--
+-- Copyright(C) 2013-2022 Gary V. Vaughan
+--
+-- Permission is hereby granted, free of charge, to any person obtaining
+-- a copy of this software and associated documentation files(the
+-- "Software"), to deal in the Software without restriction, including
+-- without limitation the rights to use, copy, modify, merge, publish,
+-- distribute, sublicense, and/or sell copies of the Software, and to
+-- permit persons to whom the Software is furnished to do so, subject to
+-- the following conditions:
+--
+-- The above copyright notice and this permission notice shall be
+-- included in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 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.
+--
+-- Portions of this software were inspired by an earlier LibYAML binding
+-- by Andrew Danforth <acd@weirdness.net>
+
+--- @module lyaml
+
+
+local explicit = require 'lyaml.explicit'
+local functional = require 'lyaml.functional'
+local implicit = require 'lyaml.implicit'
+local yaml = require 'yaml'
+
+local NULL = functional.NULL
+local anyof = functional.anyof
+local find = string.find
+local format = string.format
+local gsub = string.gsub
+local id = functional.id
+local isnull = functional.isnull
+local match = string.match
+
+
+local TAG_PREFIX = 'tag:yaml.org,2002:'
+
+
+local function tag(name)
+ return TAG_PREFIX .. name
+end
+
+
+local default = {
+ -- Tag table to lookup explicit scalar conversions.
+ explicit_scalar = {
+ [tag 'bool'] = explicit.bool,
+ [tag 'float'] = explicit.float,
+ [tag 'int'] = explicit.int,
+ [tag 'null'] = explicit.null,
+ [tag 'str'] = explicit.str,
+ },
+ -- Order is important, so we put most likely and fastest nearer
+ -- the top to reduce average number of comparisons and funcalls.
+ implicit_scalar = anyof {
+ implicit.null,
+ implicit.octal, -- subset of decimal, must come earlier
+ implicit.decimal,
+ implicit.float,
+ implicit.bool,
+ implicit.inf,
+ implicit.nan,
+ implicit.hexadecimal,
+ implicit.binary,
+ implicit.sexagesimal,
+ implicit.sexfloat,
+ id,
+ },
+}
+
+
+-- Metatable for Dumper objects.
+local dumper_mt = {
+ __index = {
+ -- Emit EVENT to the LibYAML emitter.
+ emit = function(self, event)
+ return self.emitter.emit(event)
+ end,
+
+ -- Look up an anchor for a repeated document element.
+ get_anchor = function(self, value)
+ local r = self.anchors[value]
+ if r then
+ self.aliased[value], self.anchors[value] = self.anchors[value], nil
+ end
+ return r
+ end,
+
+ -- Look up an already anchored repeated document element.
+ get_alias = function(self, value)
+ return self.aliased[value]
+ end,
+
+ -- Dump ALIAS into the event stream.
+ dump_alias = function(self, alias)
+ return self:emit {
+ type = 'ALIAS',
+ anchor = alias,
+ }
+ end,
+
+ -- Dump MAP into the event stream.
+ dump_mapping = function(self, map)
+ local alias = self:get_alias(map)
+ if alias then
+ return self:dump_alias(alias)
+ end
+
+ self:emit {
+ type = 'MAPPING_START',
+ anchor = self:get_anchor(map),
+ style = 'BLOCK',
+ }
+ for k, v in pairs(map) do
+ self:dump_node(k)
+ self:dump_node(v)
+ end
+ return self:emit {type='MAPPING_END'}
+ end,
+
+ -- Dump SEQUENCE into the event stream.
+ dump_sequence = function(self, sequence)
+ local alias = self:get_alias(sequence)
+ if alias then
+ return self:dump_alias(alias)
+ end
+
+ self:emit {
+ type = 'SEQUENCE_START',
+ anchor = self:get_anchor(sequence),
+ style = 'BLOCK',
+ }
+ for _, v in ipairs(sequence) do
+ self:dump_node(v)
+ end
+ return self:emit {type='SEQUENCE_END'}
+ end,
+
+ -- Dump a null into the event stream.
+ dump_null = function(self)
+ return self:emit {
+ type = 'SCALAR',
+ value = '~',
+ plain_implicit = true,
+ quoted_implicit = true,
+ style = 'PLAIN',
+ }
+ end,
+
+ -- Dump VALUE into the event stream.
+ dump_scalar = function(self, value)
+ local alias = self:get_alias(value)
+ if alias then
+ return self:dump_alias(alias)
+ end
+
+ local anchor = self:get_anchor(value)
+ local itsa = type(value)
+ local style = 'PLAIN'
+ if itsa == 'string' and self.implicit_scalar(value) ~= value then
+ -- take care to round-trip strings that look like scalars
+ style = 'SINGLE_QUOTED'
+ elseif value == math.huge then
+ value = '.inf'
+ elseif value == -math.huge then
+ value = '-.inf'
+ elseif value ~= value then
+ value = '.nan'
+ elseif itsa == 'number' or itsa == 'boolean' then
+ value = tostring(value)
+ elseif itsa == 'string' and find(value, '\n') then
+ style = 'LITERAL'
+ end
+ return self:emit {
+ type = 'SCALAR',
+ anchor = anchor,
+ value = value,
+ plain_implicit = true,
+ quoted_implicit = true,
+ style = style,
+ }
+ end,
+
+ -- Decompose NODE into a stream of events.
+ dump_node = function(self, node)
+ local itsa = type(node)
+ if isnull(node) then
+ return self:dump_null()
+ elseif itsa == 'string' or itsa == 'boolean' or itsa == 'number' then
+ return self:dump_scalar(node)
+ elseif itsa == 'table' then
+ -- Something is only a sequence if its keys start at 1
+ -- and are consecutive integers without any jumps.
+ local prior_key = 0
+ local is_pure_sequence = true
+ local i, v = next(node, nil)
+ while i and is_pure_sequence do
+ if type(i) ~= "number" or (prior_key + 1 ~= i) then
+ is_pure_sequence = false -- breaks the loop
+ else
+ prior_key = i
+ i, v = next(node, prior_key)
+ end
+ end
+ if is_pure_sequence then
+ -- Only sequentially numbered integer keys starting from 1.
+ return self:dump_sequence(node)
+ else
+ -- Table contains non sequential integer keys or mixed keys.
+ return self:dump_mapping(node)
+ end
+ else -- unsupported Lua type
+ error("cannot dump object of type '" .. itsa .. "'", 2)
+ end
+ end,
+
+ -- Dump DOCUMENT into the event stream.
+ dump_document = function(self, document)
+ self:emit {type='DOCUMENT_START'}
+ self:dump_node(document)
+ return self:emit {type='DOCUMENT_END'}
+ end,
+ },
+}
+
+
+-- Emitter object constructor.
+local function Dumper(opts)
+ local anchors = {}
+ for k, v in pairs(opts.anchors) do
+ anchors[v] = k
+ end
+ local object = {
+ aliased = {},
+ anchors = anchors,
+ emitter = yaml.emitter(),
+ implicit_scalar = opts.implicit_scalar,
+ }
+ return setmetatable(object, dumper_mt)
+end
+
+
+--- Dump options table.
+-- @table dumper_opts
+-- @tfield table anchors map initial anchor names to values
+-- @tfield function implicit_scalar parse implicit scalar values
+
+
+--- Dump a list of Lua tables to an equivalent YAML stream.
+-- @tparam table documents a sequence of Lua tables.
+-- @tparam[opt] dumper_opts opts initialisation options
+-- @treturn string equivalest YAML stream
+local function dump(documents, opts)
+ opts = opts or {}
+
+ -- backwards compatibility
+ if opts.anchors == nil and opts.implicit_scalar == nil then
+ opts = {anchors=opts}
+ end
+
+ local dumper = Dumper {
+ anchors = opts.anchors or {},
+ implicit_scalar = opts.implicit_scalar or default.implicit_scalar,
+ }
+
+ dumper:emit {type='STREAM_START', encoding='UTF8'}
+ for _, document in ipairs(documents) do
+ dumper:dump_document(document)
+ end
+ local ok, stream = dumper:emit {type='STREAM_END'}
+ return stream
+end
+
+
+-- We save anchor types that will match the node type from expanding
+-- an alias for that anchor.
+local alias_type = {
+ MAPPING_END = 'MAPPING_END',
+ MAPPING_START = 'MAPPING_END',
+ SCALAR = 'SCALAR',
+ SEQUENCE_END = 'SEQUENCE_END',
+ SEQUENCE_START = 'SEQUENCE_END',
+}
+
+
+-- Metatable for Parser objects.
+local parser_mt = {
+ __index = {
+ -- Return the type of the current event.
+ type = function(self)
+ return tostring(self.event.type)
+ end,
+
+ -- Raise a parse error.
+ error = function(self, errmsg, ...)
+ error(format('%d:%d: ' .. errmsg, self.mark.line,
+ self.mark.column, ...), 0)
+ end,
+
+ -- Save node in the anchor table for reference in future ALIASes.
+ add_anchor = function(self, node)
+ if self.event.anchor ~= nil then
+ self.anchors[self.event.anchor] = {
+ type = alias_type[self.event.type],
+ value = node,
+ }
+ end
+ end,
+
+ -- Fetch the next event.
+ parse = function(self)
+ local ok, event = pcall(self.next)
+ if not ok then
+ -- if ok is nil, then event is a parser error from libYAML
+ self:error(gsub(event, ' at document: .*$', ''))
+ end
+ self.event = event
+ self.mark = {
+ line = self.event.start_mark.line + 1,
+ column = self.event.start_mark.column + 1,
+ }
+ return self:type()
+ end,
+
+ -- Construct a Lua hash table from following events.
+ load_map = function(self)
+ local map = {}
+ self:add_anchor(map)
+ while true do
+ local key = self:load_node()
+ local tag = self.event.tag
+ if tag then
+ tag = match(tag, '^' .. TAG_PREFIX .. '(.*)$')
+ end
+ if key == nil then
+ break
+ end
+ if key == '<<' or tag == 'merge' then
+ tag = self.event.tag or key
+ local node, event = self:load_node()
+ if event == 'MAPPING_END' then
+ for k, v in pairs(node) do
+ if map[k] == nil then
+ map[k] = v
+ end
+ end
+
+ elseif event == 'SEQUENCE_END' then
+ for i, merge in ipairs(node) do
+ if type(merge) ~= 'table' then
+ self:error("invalid '%s' sequence element %d: %s",
+ tag, i, tostring(merge))
+ end
+ for k, v in pairs(merge) do
+ if map[k] == nil then
+ map[k] = v
+ end
+ end
+ end
+
+ else
+ if event == 'SCALAR' then
+ event = tostring(node)
+ end
+ self:error("invalid '%s' merge event: %s", tag, event)
+ end
+ else
+ local value, event = self:load_node()
+ if value == nil then
+ self:error('unexpected %s event', self:type())
+ end
+ map[key] = value
+ end
+ end
+ return map, self:type()
+ end,
+
+ -- Construct a Lua array table from following events.
+ load_sequence = function(self)
+ local sequence = {}
+ self:add_anchor(sequence)
+ while true do
+ local node = self:load_node()
+ if node == nil then
+ break
+ end
+ sequence[#sequence + 1] = node
+ end
+ return sequence, self:type()
+ end,
+
+ -- Construct a primitive type from the current event.
+ load_scalar = function(self)
+ local value = self.event.value
+ local tag = self.event.tag
+ local explicit = self.explicit_scalar[tag]
+
+ -- Explicitly tagged values.
+ if explicit then
+ value = explicit(value)
+ if value == nil then
+ self:error("invalid '%s' value: '%s'", tag, self.event.value)
+ end
+
+ -- Otherwise, implicit conversion according to value content.
+ elseif self.event.style == 'PLAIN' then
+ value = self.implicit_scalar(self.event.value)
+ end
+ self:add_anchor(value)
+ return value, self:type()
+ end,
+
+ load_alias = function(self)
+ local anchor = self.event.anchor
+ local event = self.anchors[anchor]
+ if event == nil then
+ self:error('invalid reference: %s', tostring(anchor))
+ end
+ return event.value, event.type
+ end,
+
+ load_node = function(self)
+ local dispatch = {
+ SCALAR = self.load_scalar,
+ ALIAS = self.load_alias,
+ MAPPING_START = self.load_map,
+ SEQUENCE_START = self.load_sequence,
+ MAPPING_END = function() end,
+ SEQUENCE_END = function() end,
+ DOCUMENT_END = function() end,
+ }
+
+ local event = self:parse()
+ if dispatch[event] == nil then
+ self:error('invalid event: %s', self:type())
+ end
+ return dispatch[event](self)
+ end,
+ },
+}
+
+
+-- Parser object constructor.
+local function Parser(s, opts)
+ local object = {
+ anchors = {},
+ explicit_scalar = opts.explicit_scalar,
+ implicit_scalar = opts.implicit_scalar,
+ mark = {line=0, column=0},
+ next = yaml.parser(s),
+ }
+ return setmetatable(object, parser_mt)
+end
+
+
+--- Load options table.
+-- @table loader_opts
+-- @tfield boolean all load all documents from the stream
+-- @tfield table explicit_scalar map full tag-names to parser functions
+-- @tfield function implicit_scalar parse implicit scalar values
+
+
+--- Load a YAML stream into a Lua table.
+-- @tparam string s YAML stream
+-- @tparam[opt] loader_opts opts initialisation options
+-- @treturn table Lua table equivalent of stream *s*
+local function load(s, opts)
+ opts = opts or {}
+ local documents = {}
+ local all = false
+
+ -- backwards compatibility
+ if opts == true then
+ opts = {all=true}
+ end
+
+ local parser = Parser(s, {
+ explicit_scalar = opts.explicit_scalar or default.explicit_scalar,
+ implicit_scalar = opts.implicit_scalar or default.implicit_scalar,
+ })
+
+ if parser:parse() ~= 'STREAM_START' then
+ error('expecting STREAM_START event, but got ' .. parser:type(), 2)
+ end
+
+ while parser:parse() ~= 'STREAM_END' do
+ local document = parser:load_node()
+ if document == nil then
+ error('unexpected ' .. parser:type() .. ' event')
+ end
+
+ if parser:parse() ~= 'DOCUMENT_END' then
+ error('expecting DOCUMENT_END event, but got ' .. parser:type(), 2)
+ end
+
+ -- save document
+ documents[#documents + 1] = document
+
+ -- reset anchor table
+ parser.anchors = {}
+ end
+
+ return opts.all and documents or documents[1]
+end
+
+
+--[[ ----------------- ]]--
+--[[ Public Interface. ]]--
+--[[ ----------------- ]]--
+
+
+--- @export
+return {
+ dump = dump,
+ load = load,
+
+ --- `lyaml.null` value.
+ -- @table null
+ null = NULL,
+
+ --- Version number from yaml C binding.
+ -- @table _VERSION
+ _VERSION = yaml.version,
+}
diff --git a/lukefile b/lukefile
new file mode 100644
index 000000000000..318281fd78e7
--- /dev/null
+++ b/lukefile
@@ -0,0 +1,47 @@
+--[[
+ LYAML binding for Lua 5.1, 5.2, 5.3 & 5.4
+ Copyright (C) 2013-2022 Gary V. Vaughan
+]]
+
+package = 'lyaml'
+version = '$USER'
+
+defines = {
+ PACKAGE = '"$package"',
+ VERSION = '"$version"',
+ NDEBUG = 1,
+ _FORTIFY_SOURCE = 2,
+ platforms = {
+ aix = {_ALL_SOURCE = 1},
+ bsd = {_BSD_SOURCE = 1},
+ freebsd = {__BSD_VISIBLE = 1},
+ macosx = {_DARWIN_C_SOURCE = 1},
+ },
+}
+
+external_dependencies = {
+ YAML = {
+ library = {checksymbol='yaml_document_initialize', library='yaml'},
+ },
+}
+
+incdirs = {
+ 'ext/include',
+ '$LUA_INCDIR',
+}
+
+ldocs = 'build-aux/config.ld.in'
+
+modules = {
+ ['yaml'] = {
+ 'ext/yaml/yaml.c',
+ 'ext/yaml/emitter.c',
+ 'ext/yaml/parser.c',
+ 'ext/yaml/scanner.c',
+ },
+
+ ['lyaml'] = 'lib/lyaml/init.lua',
+ ['lyaml.explicit'] = 'lib/lyaml/explicit.lua',
+ ['lyaml.functional'] = 'lib/lyaml/functional.lua',
+ ['lyaml.implicit'] = 'lib/lyaml/implicit.lua',
+}
diff --git a/lyaml-6.2.8-1.rockspec b/lyaml-6.2.8-1.rockspec
new file mode 100644
index 000000000000..dce9e45a56cf
--- /dev/null
+++ b/lyaml-6.2.8-1.rockspec
@@ -0,0 +1,59 @@
+local _MODREV, _SPECREV = '6.2.8', '-1'
+
+package = 'lyaml'
+version = _MODREV .. _SPECREV
+
+description = {
+ summary = 'libYAML binding for Lua',
+ detailed = 'Read and write YAML format files with Lua.',
+ homepage = 'http://github.com/gvvaughan/lyaml',
+ license = 'MIT/X11',
+}
+
+source = {
+ url = 'http://github.com/gvvaughan/lyaml/archive/v' .. _MODREV .. '.zip',
+ dir = 'lyaml-' .. _MODREV,
+}
+
+dependencies = {
+ 'lua >= 5.1, < 5.5',
+}
+
+external_dependencies = {
+ YAML = {
+ library = 'yaml',
+ },
+}
+
+build = {
+ type = 'command',
+ build_command = '$(LUA) build-aux/luke'
+ .. ' package="' .. package .. '"'
+ .. ' version="' .. _MODREV .. '"'
+ .. ' PREFIX="$(PREFIX)"'
+ .. ' CFLAGS="$(CFLAGS)"'
+ .. ' LIBFLAG="$(LIBFLAG)"'
+ .. ' LIB_EXTENSION="$(LIB_EXTENSION)"'
+ .. ' OBJ_EXTENSION="$(OBJ_EXTENSION)"'
+ .. ' LUA="$(LUA)"'
+ .. ' LUA_DIR="$(LUADIR)"'
+ .. ' LUA_INCDIR="$(LUA_INCDIR)"'
+ .. ' YAML_DIR="$(YAML_DIR)"'
+ .. ' YAML_INCDIR="$(YAML_INCDIR)"'
+ .. ' YAML_LIBDIR="$(YAML_LIBDIR)"'
+ ,
+ install_command = '$(LUA) build-aux/luke install --quiet'
+ .. ' INST_LIBDIR="$(LIBDIR)"'
+ .. ' INST_LUADIR="$(LUADIR)"'
+ ,
+ copy_directories = {'doc'},
+}
+
+if _MODREV == 'git' then
+ build.copy_directories = nil
+
+ source = {
+ url = 'git://github.com/gvvaughan/lyaml.git',
+ }
+end
+
diff --git a/spec/ext_yaml_emitter_spec.yaml b/spec/ext_yaml_emitter_spec.yaml
new file mode 100644
index 000000000000..385d58f7de3c
--- /dev/null
+++ b/spec/ext_yaml_emitter_spec.yaml
@@ -0,0 +1,239 @@
+# LYAML binding for Lua 5.1, 5.2, 5.3 & 5.4
+# Copyright (C) 2013-2022 Gary V. Vaughan
+
+specify emitting:
+- it diagnoses an invalid event:
+ emitter = yaml.emitter ()
+ expect (emitter.emit "not an event").to_raise "expected table"
+- it can generate an empty stream:
+ pending (github_issue "2")
+ expect (emit {
+ {type = "DOCUMENT_START", implicit = true},
+ {type = "SCALAR", value = ""},
+ {type = "DOCUMENT_END", implicit = true},
+ }).
+ to_equal ""
+
+- describe STREAM_START:
+ - it diagnoses unrecognised encodings:
+ expect (emitevents (yaml.emitter (), {
+ {type = "STREAM_START", encoding = "notexists"},
+ "STREAM_END"})).
+ to_raise "invalid stream encoding 'notexists'"
+ - it accepts an encoding parameter:
+ expect (emitevents (yaml.emitter (), {
+ {type = "STREAM_START", encoding = "UTF16BE"},
+ "STREAM_END"})).
+ to_equal (BOM)
+
+- describe STREAM_END:
+ - it returns the yaml document from the preceding events:
+ expect (emit {"DOCUMENT_START", {type = "SCALAR", value = "woo!"},
+ "DOCUMENT_END"}).
+ to_equal "--- woo!\n...\n"
+
+- describe DOCUMENT_START:
+ - it accepts a version directive parameter:
+ expect (emit {{type = "DOCUMENT_START",
+ version_directive = { major = 1, minor = 1 }},
+ {type = "SCALAR", value = ""},
+ "DOCUMENT_END"}).
+ to_match "^%%YAML 1.1\n---"
+ - it accepts a list of tag directives:
+ expect (emit {{type = "DOCUMENT_START",
+ tag_directives = {{handle = "!",
+ prefix = "tag:ben-kiki.org,2000:app/"}}},
+ {type = "SCALAR", value = ""},
+ "DOCUMENT_END"}).
+ to_contain "%TAG ! tag:ben-kiki.org,2000:app/\n---"
+ expect (emit {
+ {type = "DOCUMENT_START",
+ tag_directives = {{handle = "!",
+ prefix = "tag:ben-kiki.org,2000:app/"},
+ {handle = "!!",
+ prefix = "tag:yaml.org,2002:"}}},
+ {type = "SCALAR", value = ""},
+ "DOCUMENT_END"}).
+ to_contain ("%TAG ! tag:ben-kiki.org,2000:app/\n" ..
+ "%TAG !! tag:yaml.org,2002:\n---")
+ - it accepts an implicit parameter:
+ expect (emit {{type = "DOCUMENT_START", implicit = true},
+ {type = "SCALAR", value = ""}, "DOCUMENT_END"}).
+ not_to_contain "--- \n"
+ pending (github_issue "2")
+ expect (emit {{type = "DOCUMENT_START", implicit = false},
+ {type = "SCALAR", value = ""}, "DOCUMENT_END"}).
+ not_to_contain "---"
+
+- describe DOCUMENT_END:
+ - it accepts an implicit parameter:
+ expect (emit {"DOCUMENT_START", {type = "SCALAR", value = ""},
+ {type = "DOCUMENT_END", implicit = false}}).
+ to_contain "\n..."
+ pending (github_issue "2")
+ expect (emit {"DOCUMENT_START", {type = "SCALAR", value = ""},
+ {type = "DOCUMENT_END", implicit = true}}).
+ not_to_contain "\n..."
+
+- describe MAPPING_START:
+ - it accepts an anchor parameter:
+ expect (emit {"DOCUMENT_START",
+ {type = "MAPPING_START", anchor = "foo"},
+ "MAPPING_END", "DOCUMENT_END"}).
+ to_contain "&foo"
+ - it diagnoses unrecognised styles:
+ expect (emit {"DOCUMENT_START",
+ {type = "MAPPING_START", style = "notexists"},
+ "MAPPING_END", "DOCUMENT_END"}).
+ to_raise "invalid mapping style 'notexists'"
+ - it understands block style: '
+ expect (emit {"DOCUMENT_START",
+ {type = "MAPPING_START", style = "BLOCK"},
+ {type = "SCALAR", value = "foo"}, {type = "SCALAR", value = "bar"},
+ "MAPPING_END", "DOCUMENT_END"}).
+ to_contain "foo: bar\n"'
+ - it understands flow style: '
+ expect (emit {"DOCUMENT_START",
+ {type = "MAPPING_START", style = "FLOW"},
+ {type = "SCALAR", value = "foo"}, {type = "SCALAR", value = "bar"},
+ {type = "SCALAR", value = "baz"}, {type = "SCALAR", value = "qux"},
+ "MAPPING_END", "DOCUMENT_END"}).
+ to_contain "{foo: bar, baz: qux}\n"'
+ - it accepts an explicit tag parameter: '
+ expect (emit {"DOCUMENT_START",
+ {type = "MAPPING_START", style = "FLOW",
+ tag = "tag:yaml.org,2002:map", implicit = false},
+ {type = "SCALAR", value = "foo"}, {type = "SCALAR", value = "bar"},
+ "MAPPING_END", "DOCUMENT_END"}).
+ to_contain "!!map {foo: bar}"'
+ - it accepts an implicit tag parameter: '
+ expect (emit {"DOCUMENT_START",
+ {type = "MAPPING_START", tag = "tag:yaml.org,2002:map", implicit = true},
+ {type = "SCALAR", value = "foo"}, {type = "SCALAR", value = "bar"},
+ "MAPPING_END", "DOCUMENT_END"}).
+ not_to_contain "map"'
+
+- describe MAPPING_END:
+ - it requires no parameters: '
+ expect (emit {"DOCUMENT_START", "MAPPING_START",
+ {type = "SCALAR", value = "foo"}, {type = "SCALAR", value = "bar"},
+ "MAPPING_END", "DOCUMENT_END"}).
+ to_contain "foo: bar\n"'
+
+- describe SEQUENCE_START:
+ - it accepts an anchor parameter:
+ expect (emit {"DOCUMENT_START",
+ {type = "SEQUENCE_START", anchor = "foo"},
+ "SEQUENCE_END", "DOCUMENT_END"}).
+ to_contain "&foo"
+ - it diagnoses unrecognised styles:
+ expect (emit {"DOCUMENT_START",
+ {type = "SEQUENCE_START", style = "notexists"},
+ "SEQUENCE_END", "DOCUMENT_END"}).
+ to_raise "invalid sequence style 'notexists'"
+ - it understands block style:
+ expect (emit {"DOCUMENT_START",
+ {type = "SEQUENCE_START", style = "BLOCK"},
+ {type = "SCALAR", value = "foo"}, {type = "SCALAR", value = "bar"},
+ "SEQUENCE_END", "DOCUMENT_END"}).
+ to_contain "- foo\n- bar\n"
+ - it understands flow style:
+ expect (emit {"DOCUMENT_START",
+ {type = "SEQUENCE_START", style = "FLOW"},
+ {type = "SCALAR", value = "foo"}, {type = "SCALAR", value = "bar"},
+ "SEQUENCE_END", "DOCUMENT_END"}).
+ to_contain "[foo, bar]"
+ - it accepts an explicit tag parameter:
+ expect (emit {"DOCUMENT_START",
+ {type = "SEQUENCE_START", style = "FLOW",
+ tag = "tag:yaml.org,2002:sequence", implicit = false},
+ {type = "SCALAR", value = "foo"}, {type = "SCALAR", value = "bar"},
+ "SEQUENCE_END", "DOCUMENT_END"}).
+ to_contain "!!sequence [foo, bar]\n"
+ - it accepts an implicit tag parameter:
+ expect (emit {"DOCUMENT_START",
+ {type = "SEQUENCE_START", style = "FLOW",
+ tag = "tag:yaml.org,2002:sequence", implicit = true},
+ {type = "SCALAR", value = "foo"}, {type = "SCALAR", value = "bar"},
+ "SEQUENCE_END", "DOCUMENT_END"}).
+ not_to_contain "sequence"
+
+- describe SEQUENCE_END:
+ - it requires no parameters: '
+ expect (emit {"DOCUMENT_START", "SEQUENCE_START",
+ {type = "SCALAR", value = "moo"},
+ "SEQUENCE_END", "DOCUMENT_END"}).
+ to_contain "- moo\n"'
+
+- describe SCALAR:
+ - it diagnoses a missing value parameter:
+ - it accepts a value parameter:
+ expect (emit {"DOCUMENT_START", {type = "SCALAR", value = "boo"},
+ "DOCUMENT_END"}).
+ to_contain "boo"
+ - it diagnoses unrecognised styles:
+ expect (emit {"DOCUMENT_START",
+ {type = "SCALAR", style = "notexists", value = "foo"},
+ "DOCUMENT_END"}).
+ to_raise "invalid scalar style 'notexists'"
+ - it understands plain style:
+ expect (emit {"DOCUMENT_START",
+ {type = "SCALAR", style = "PLAIN", value = "boo"},
+ "DOCUMENT_END"}).
+ to_contain "boo\n"
+ - it understands single quoted style:
+ expect (emit {"DOCUMENT_START",
+ {type = "SCALAR", style = "SINGLE_QUOTED", value = "bar"},
+ "DOCUMENT_END"}).
+ to_contain "'bar'\n"
+ expect (emit {"DOCUMENT_START",
+ {type = "SCALAR", style = "SINGLE_QUOTED", value = "bar'"},
+ "DOCUMENT_END"}).
+ to_contain "'bar'''\n"
+ - it understands double quoted style:
+ expect (emit {"DOCUMENT_START",
+ {type = "SCALAR", style = "DOUBLE_QUOTED", value = "baz"},
+ "DOCUMENT_END"}).
+ to_contain '"baz"\n'
+ expect (emit {"DOCUMENT_START",
+ {type = "SCALAR", style = "DOUBLE_QUOTED", value = '"baz"'},
+ "DOCUMENT_END"}).
+ to_contain ([["\"baz\""]] .. "\n")
+ - it understands literal style:
+ expect (emit {"DOCUMENT_START",
+ {type = "SCALAR", style = "LITERAL", value = "quux"},
+ "DOCUMENT_END"}).
+ to_contain "|-\n quux\n"
+ - it understands folded style:
+ expect (emit {"DOCUMENT_START",
+ {type = "SCALAR", style = "FOLDED", value = "thud"},
+ "DOCUMENT_END"}).
+ to_contain ">-\n thud\n"
+ - it understands plain_implicit:
+ expect (emit {"DOCUMENT_START",
+ {type = "SCALAR", style = "PLAIN", value = "hello", plain_implicit=false},
+ "DOCUMENT_END"}).
+ to_contain "'hello'\n"
+ - it understands quoted_implicit:
+ expect (emit {"DOCUMENT_START",
+ {type = "SCALAR", style = "PLAIN", value = "- world", quoted_implicit=false},
+ "DOCUMENT_END"}).
+ to_contain "! '- world'\n"
+ - it understands tag:
+ expect (emit {"DOCUMENT_START",
+ {type = "SCALAR", style = "PLAIN", value = "bug_squash", tag="tagger", plain_implicit=false, quoted_implicit=false},
+ "DOCUMENT_END"}).
+ to_contain "!<tagger> bug_squash\n"
+
+- describe ALIAS:
+ - it diagnoses missing anchor parameter:
+ - it diagnoses non-alphanumeric anchor characters:
+ expect (emit {"DOCUMENT_START", {type = "ALIAS", anchor = "woo!"},
+ "DOCUMENT_END"}).
+ to_raise "must contain alphanumerical characters only"
+ - it accepts an anchor parameter:
+ expect (emit {"DOCUMENT_START", "SEQUENCE_START",
+ {type = "SCALAR", anchor = "woo", value = "hoo"},
+ {type = "ALIAS", anchor = "woo"},
+ "SEQUENCE_END", "DOCUMENT_END"}).
+ to_contain.all_of {"&woo", "*woo"}
diff --git a/spec/ext_yaml_parser_spec.yaml b/spec/ext_yaml_parser_spec.yaml
new file mode 100644
index 000000000000..2438c42188e0
--- /dev/null
+++ b/spec/ext_yaml_parser_spec.yaml
@@ -0,0 +1,391 @@
+# LYAML binding for Lua 5.1, 5.2, 5.3 & 5.4
+# Copyright (C) 2013-2022 Gary V. Vaughan
+
+specify parsing:
+- it parses empty streams:
+ e = yaml.parser ""
+ expect (e ().type).to_be "STREAM_START"
+ expect (e ().type).to_be "STREAM_END"
+ expect (e ()).to_be (nil)
+ expect (e ()).to_be (nil)
+- it ignores comments: '
+ e = yaml.parser "# A comment\nnon-comment # trailing comment\n"
+ expect (e ().type).to_be "STREAM_START"
+ expect (e ().type).to_be "DOCUMENT_START"
+ expect (e ().value).to_be "non-comment"
+ expect (e ().type).to_be "DOCUMENT_END"'
+
+- describe STREAM_START:
+ - before:
+ e = yaml.parser "# no BOM"
+ - it is the first event:
+ expect (e ().type).to_be "STREAM_START"
+ - it reports event start marker:
+ expect (e ().start_mark).to_equal {line = 0, column = 0, index = 0}
+ - it reports event end marker:
+ expect (e ().end_mark).to_equal {line = 0, column = 0, index = 0}
+ - it uses UTF-8 by default:
+ expect (e ().encoding).to_be "UTF8"
+ - it recognizes UTF-16 BOM:
+ e = yaml.parser (BOM .. " BOM")
+ expect (e ().encoding).to_match "UTF16[BL]E"
+
+- describe STREAM_END:
+ - before:
+ for t in yaml.parser "nothing to see" do ev = t end
+ - it is the last event:
+ expect (ev.type).to_be "STREAM_END"
+ - it reports event start marker:
+ expect (ev.start_mark).to_equal {line = 1, column = 0, index = 14}
+ - it reports event end marker:
+ expect (ev.end_mark).to_equal {line = 1, column = 0, index = 14}
+
+- describe DOCUMENT_START:
+ - before:
+ e = consume (1, "---")
+ - it recognizes document start marker:
+ expect (filter (e (), "type", "implicit")).
+ to_equal {type = "DOCUMENT_START", implicit = false}
+ - it reports implicit document start:
+ e = consume (1, "foo")
+ expect (e ().implicit).to_be (true)
+ - it reports event start marker:
+ expect (e ().start_mark).to_equal {line = 0, column = 0, index = 0}
+ - it reports event end marker:
+ expect (e ().end_mark).to_equal {line = 0, column = 3, index = 3}
+
+ - context parser directives:
+ - it can recognize document versions:
+ e = consume (1, "%YAML 1.1\n---")
+ expect (e ().version_directive).to_equal {major = 1, minor = 1}
+ - it can diagnose missing document start:
+ e = consume (1, "%YAML 1.1\n")
+ expect (e ()).to_error "expected <document start>"
+ - it can diagnose multiple versions:
+ e = consume (1, "%YAML 1.1\n%YAML 1.1\n---")
+ expect (e ()).to_error "duplicate %YAML directive"
+ - it can diagnose too-new versions:
+ e = consume (1, "%YAML 2.0\n---")
+ expect (e ()).to_error "incompatible YAML document"
+ - it warns of newer minor versions:
+ pending (github_issue "1")
+ e = consume (1, "%YAML 1.9\n---")
+ expect (e ()).
+ to_error "attempting parsing of newer minor document version"
+
+ - it can recognize primary tag handles:
+ e = consume (1, "%TAG ! tag:ben-kiki.org,2000:app/\n---")
+ expect (e ().tag_directives).
+ to_equal {{handle = "!", prefix = "tag:ben-kiki.org,2000:app/"}}
+ - it can recognize secondary tag handles:
+ e = consume (1, "%TAG !! tag:yaml.org,2002:\n---")
+ expect (e ().tag_directives).
+ to_equal {{handle = "!!", prefix = "tag:yaml.org,2002:"}}
+ - it can recognize named tag handles:
+ e = consume (1, "%TAG !o! tag:ben-kiki.org,2000:\n---")
+ expect (e ().tag_directives).
+ to_equal {{handle = "!o!", prefix = "tag:ben-kiki.org,2000:"}}
+ - it can concatenate multiple tag handles:
+ e = consume (1, "%TAG ! !\n" ..
+ "%TAG !! tag:yaml.org,2002:\n" ..
+ "%TAG !o! tag:ben-kiki.org,2000:\n" ..
+ "---")
+ expect (e ().tag_directives).to_contain.
+ all_of {{handle = "!", prefix = "!"},
+ {handle = "!!", prefix = "tag:yaml.org,2002:"},
+ {handle = "!o!", prefix = "tag:ben-kiki.org,2000:"}}
+ - it can diagnose missing document start:
+ e = consume (1, "%TAG ! !\n")
+ expect (e ()).to_error "expected <document start>"
+
+- describe DOCUMENT_END:
+ - before:
+ e = consume (3, "foo\n...")
+ - it recognizes the document end marker:
+ expect (filter (e (), "type", "implicit")).
+ to_equal {type = "DOCUMENT_END", implicit = false}
+ - it reports an implicit document end marker:
+ e = consume (3, "foo\n")
+ expect (filter (e (), "type", "implicit")).
+ to_equal {type = "DOCUMENT_END", implicit = true}
+ - it reports event start marker:
+ expect (e ().start_mark).to_equal {line = 1, column = 0, index = 4}
+ - it reports event end marker:
+ expect (e ().end_mark).to_equal {line = 1, column = 3, index = 7}
+
+- describe ALIAS:
+ - before:
+ e = consume (10, "---\n" ..
+ "hr:\n" ..
+ "- Mark McGwire\n" ..
+ "- &SS Sammy Sosa\n" ..
+ "rbi:\n" ..
+ "- *SS\n" ..
+ "- Ken Griffey")
+ - it recognizes an alias event:
+ expect (filter (e (), "type", "anchor")).
+ to_equal {type = "ALIAS", anchor = "SS"}
+ - it reports event start marker:
+ expect (e ().start_mark).to_equal {line = 5, column = 2, index = 47}
+ - it reports event end marker:
+ expect (e ().end_mark).to_equal {line = 5, column = 5, index = 50}
+
+- describe SCALAR:
+ - before:
+ e = consume (6, "---\n" ..
+ "hr:\n" ..
+ "- Mark McGwire\n" ..
+ "- &SS Sammy Sosa\n" ..
+ "rbi:\n" ..
+ "- *SS\n" ..
+ "- Ken Griffey")
+ - it recognizes a scalar event:
+ expect (filter (e (), "type", "value")).
+ to_equal {type = "SCALAR", value = "Sammy Sosa"}
+ - it records anchors:
+ expect (e ().anchor).to_be "SS"
+ - it reports event start marker:
+ expect (e ().start_mark).to_equal {line = 3, column = 2, index = 25}
+ - it reports event end marker:
+ expect (e ().end_mark).to_equal {line = 3, column = 16, index = 39}
+
+ - context with quoting style:
+ - context plain style:
+ - before:
+ e = consume (2, "---\n" ..
+ " Mark McGwire's\n" ..
+ " year was crippled\n" ..
+ " by a knee injury.\n")
+ - it ignores line-breaks and indentation:
+ expect (e ().value).
+ to_be "Mark McGwire's year was crippled by a knee injury."
+ - it recognizes implicit plain style:
+ e = consume (2, "---\n" ..
+ " Mark McGwire's\n" ..
+ " year was crippled\n" ..
+ " by a knee injury.\n")
+ expect (e ().plain_implicit).to_be (true)
+ - it recognizes explicit plain style:
+ e = consume (2, "|\n" ..
+ " Mark McGwire's\n" ..
+ " year was crippled\n" ..
+ " by a knee injury.\n")
+ expect (e ().plain_implicit).to_be (false)
+ - it recognizes implicit quoted style:
+ e = consume (2, "|\n" ..
+ " Mark McGwire's\n" ..
+ " year was crippled\n" ..
+ " by a knee injury.\n")
+ expect (e ().quoted_implicit).to_be (true)
+ - it recognizes explicit quoted style:
+ e = consume (2, "'\n" ..
+ " Mark McGwire's\n" ..
+ " year was crippled\n" ..
+ " by a knee injury.'\n")
+ expect (e ().plain_implicit).to_be (false)
+ - context folded style:
+ - it preserves blank lines and deeper indentation:
+ e = consume (2, ">\n" ..
+ " Sammy Sosa completed another\n" ..
+ " fine season with great stats.\n" ..
+ "\n" ..
+ " 63 Home Runs\n" ..
+ " 0.288 Batting Average\n" ..
+ "\n" ..
+ " What a year!\n")
+ expect (e ().value).
+ to_be ("Sammy Sosa completed another fine season with great stats.\n" ..
+ "\n" ..
+ " 63 Home Runs\n" ..
+ " 0.288 Batting Average\n" ..
+ "\n" ..
+ "What a year!\n")
+ - context literal style:
+ - it removes indentation but preserves all line-breaks:
+ e = consume (2, [[# ASCII Art]] .. "\n" ..
+ [[--- |]] .. "\n" ..
+ [[ \//||\/||]] .. "\n" ..
+ [[ // || ||__]] .. "\n")
+ expect (e ().value).
+ to_be ([[\//||\/||]] .. "\n" ..
+ [[// || ||__]] .. "\n")
+
+ - context single quoted style:
+ - it folds line breaks:
+ e = consume (2, [['This quoted scalar]] .. "\n" ..
+ [[ spans two lines.']])
+ expect (e ().value).
+ to_be "This quoted scalar spans two lines."
+ - it does not process escape sequences:
+ # Lua [[ quoting makes sure libyaml sees all the quotes.
+ e = consume (2, [['"Howdy!"\t\u263A']])
+ expect (e ().value).to_be [["Howdy!"\t\u263A]]
+
+ # Note that we have to single quote the Lua snippets to prevent
+ # libyaml from interpreting the bytes as the spec file is read, so
+ # that the raw strings get correctly passed to the Lua compiler.
+ - context double quoted style:
+ - it folds line breaks: '
+ e = consume (4, [[quoted: "This quoted scalar]] .. "\n" ..
+ [[ spans two lines\n"]])
+ expect (e ().value).
+ to_be "This quoted scalar spans two lines\n"'
+ - it recognizes unicode escape sequences: '
+ e = consume (4, [[unicode: "Sosa did fine.\u263A"]])
+ expect (e ().value).to_be "Sosa did fine.\226\152\186"'
+ - it recognizes control escape sequences: '
+ e = consume (4, [[control: "\b1998\t1999\t2000\n"]])
+ expect (e ().value).to_be "\b1998\t1999\t2000\n"'
+ - it recognizes hexadecimal escape sequences: '
+ e = consume (4, [[hexesc: "\x41\x42\x43 is ABC"]])
+ expect (e ().value).to_be "ABC is ABC"'
+
+ - context indentation determines scope: '
+ e = consume (4, "name: Mark McGwire\n" ..
+ "accomplishment: >\n" ..
+ " Mark set a major league\n" ..
+ " home run record in 1998.\n" ..
+ "stats: |\n" ..
+ " 65 Home Runs\n" ..
+ " 0.278 Batting Average\n")
+ expect (e ().value).to_be "Mark McGwire"
+ expect (e ().value).to_be "accomplishment"
+ expect (e ().value).
+ to_be "Mark set a major league home run record in 1998.\n"
+ expect (e ().value).to_be "stats"
+ expect (e ().value).to_be "65 Home Runs\n0.278 Batting Average\n"'
+
+ - context with tag:
+ - it recognizes local tags: '
+ e = consume (4, "application specific tag: !something |\n" ..
+ " The semantics of the tag\n" ..
+ " above may be different for\n" ..
+ " different documents.")
+ expect (e ().tag).to_be "!something"'
+ - it recognizes global tags: '
+ e = consume (4, "picture: !!binary |\n" ..
+ " R0lGODlhDAAMAIQAAP//9/X\n" ..
+ " 17unp5WZmZgAAAOfn515eXv\n" ..
+ " Pz7Y6OjuDg4J+fn5OTk6enp\n" ..
+ " 56enmleECcgggoBADs=")
+ expect (e ().tag).to_be "tag:yaml.org,2002:binary"'
+ - it resolves %TAG declarations: '
+ e = consume (5, "%TAG ! tag:clarkevans.com,2002:\n" ..
+ "---\n" ..
+ "shape:\n" ..
+ "- !circle\n" ..
+ " center: &ORIGIN {x: 73, y: 129}\n" ..
+ " radius: 7")
+ expect (e ().tag).to_be "tag:clarkevans.com,2002:circle"'
+
+- describe SEQUENCE_START:
+ - before: '
+ e = consume (4, "fubar: &FOO\n" ..
+ " - foo\n" ..
+ " - bar\n")'
+ - it recognizes a sequence start event:
+ expect (e ().type).to_be "SEQUENCE_START"
+ - it records anchors:
+ expect (e ().anchor).to_be "FOO"
+ - it reports event start marker:
+ expect (e ().start_mark).to_equal {line = 0, column = 7, index = 7}
+ - it reports event end marker:
+ expect (e ().end_mark).to_equal {line = 1, column = 2, index = 14}
+
+ - context with tag:
+ - it recognizes local tags: '
+ e = consume (2, "--- !something\n" ..
+ "- foo\n")
+ expect (filter (e (), "type", "tag")).
+ to_equal {type = "SEQUENCE_START", tag = "!something"}'
+ - it recognizes global tags: '
+ e = consume (2, "--- !!omap\n" ..
+ "- Mark McGwire: 65\n" ..
+ "- Sammy Sosa: 63\n" ..
+ "- Ken Griffy: 58\n")
+ expect (filter (e (), "type", "tag")).
+ to_equal {type = "SEQUENCE_START",
+ tag = "tag:yaml.org,2002:omap"}'
+ - it resolves %TAG declarations: '
+ e = consume (2, "%TAG ! tag:clarkevans.com,2002:\n" ..
+ "--- !shape\n" ..
+ "- !circle\n" ..
+ " center: &ORIGIN {x: 73, y: 129}\n" ..
+ " radius: 7\n")
+ expect (filter (e (), "type", "tag")).
+ to_equal {type = "SEQUENCE_START",
+ tag = "tag:clarkevans.com,2002:shape"}'
+
+ - context with style:
+ - it recognizes block style:
+ e = consume (2, "- first\n- second")
+ expect (filter (e (), "type", "style")).
+ to_equal {type = "SEQUENCE_START", style = "BLOCK"}
+ - it recognizes flow style:
+ e = consume (2, "[first, second]")
+ expect (filter (e (), "type", "style")).
+ to_equal {type = "SEQUENCE_START", style = "FLOW"}
+
+- describe SEQUENCE_END:
+ - before:
+ e = consume (5, "- foo\n- bar\n")
+ - it recognizes a sequence end event:
+ expect (e ().type).to_equal "SEQUENCE_END"
+ - it reports event start marker:
+ expect (e ().start_mark).to_equal {line = 2, column = 0, index = 12}
+ - it reports event end marker:
+ expect (e ().end_mark).to_equal {line = 2, column = 0, index = 12}
+
+- describe MAPPING_START:
+ - before: 'e = consume (3, "- &FUBAR\n foo: bar\n")'
+ - it recognizes a mapping start event:
+ expect (e ().type).to_be "MAPPING_START"
+ - it records anchors:
+ expect (e ().anchor).to_be "FUBAR"
+ - it reports event start marker:
+ expect (e ().start_mark).to_equal {line = 0, column = 2, index = 2}
+ - it reports event end marker:
+ expect (e ().end_mark).to_equal {line = 1, column = 2, index = 11}
+
+ - context with tag:
+ - it recognizes local tags: '
+ e = consume (2, "--- !something\nfoo: bar\n")
+ expect (filter (e (), "type", "tag")).
+ to_equal {type = "MAPPING_START", tag = "!something"}'
+ - it recognizes global tags: '
+ e = consume (2, "--- !!set\n" ..
+ "? Mark McGwire\n" ..
+ "? Sammy Sosa\n" ..
+ "? Ken Griffy\n")
+ expect (filter (e (), "type", "tag")).
+ to_equal {type = "MAPPING_START",
+ tag = "tag:yaml.org,2002:set"}'
+ - it resolves %TAG declarations: '
+ e = consume (3, "%TAG ! tag:clarkevans.com,2002:\n" ..
+ "--- !shape\n" ..
+ "- !circle\n" ..
+ " center: &ORIGIN {x: 73, y: 129}\n" ..
+ " radius: 7\n")
+ expect (filter (e (), "type", "tag")).
+ to_equal {type = "MAPPING_START",
+ tag = "tag:clarkevans.com,2002:circle"}'
+
+ - context with style:
+ - it recognizes block style: '
+ e = consume (2, "foo: bar\nbaz:\n quux")
+ expect (filter (e (), "type", "style")).
+ to_equal {type = "MAPPING_START", style = "BLOCK"}'
+ - it recognizes flow style: '
+ e = consume (2, "{foo: bar, baz: quux}")
+ expect (filter (e (), "type", "style")).
+ to_equal {type = "MAPPING_START", style = "FLOW"}'
+
+
+- describe MAPPING_END:
+ - before: 'e = consume (5, "foo: bar\n")'
+ - it recognizes the mapping end event:
+ expect (e ().type).to_equal "MAPPING_END"
+ - it reports event start marker:
+ expect (e ().start_mark).to_equal {line = 1, column = 0, index = 9}
+ - it reports event end marker:
+ expect (e ().end_mark).to_equal {line = 1, column = 0, index = 9}
diff --git a/spec/ext_yaml_scanner_spec.yaml b/spec/ext_yaml_scanner_spec.yaml
new file mode 100644
index 000000000000..4d8e633d2cc2
--- /dev/null
+++ b/spec/ext_yaml_scanner_spec.yaml
@@ -0,0 +1,380 @@
+# LYAML binding for Lua 5.1, 5.2, 5.3 & 5.4
+# Copyright (C) 2013-2022 Gary V. Vaughan
+
+before:
+ function consume (n, str)
+ local k = yaml.scanner (str)
+ for n = 1, n do k () end
+ return k
+ end
+
+specify scanning:
+- it scans empty streams:
+ k = yaml.scanner ""
+ expect (k ().type).to_be "STREAM_START"
+ expect (k ().type).to_be "STREAM_END"
+ expect (k ()).to_be (nil)
+ expect (k ()).to_be (nil)
+- it ignores comments: '
+ k = yaml.scanner "# A comment\nnon-comment # trailing comment\n"
+ expect (k ().type).to_be "STREAM_START"
+ expect (k ().value).to_be "non-comment"
+ expect (k ().type).to_be "STREAM_END"'
+
+- describe STREAM_START:
+ - before:
+ k = yaml.scanner "# no BOM"
+ - it is the first token:
+ expect (k ().type).to_be "STREAM_START"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 0, column = 0, index = 0}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 0, column = 0, index = 0}
+ - it uses UTF-8 by default:
+ expect (k ().encoding).to_be "UTF8"
+ - it recognizes UTF-16 BOM:
+ k = yaml.scanner (BOM .. " BOM")
+ expect (k ().encoding).to_match "UTF16[BL]E"
+
+- describe STREAM_END:
+ - before:
+ for t in yaml.scanner "nothing to see" do k = t end
+ - it is the last token:
+ expect (k.type).to_be "STREAM_END"
+ - it reports token start marker:
+ expect (k.start_mark).to_equal {line = 1, column = 0, index = 14}
+ - it reports token end marker:
+ expect (k.end_mark).to_equal {line = 1, column = 0, index = 14}
+
+- describe VERSION_DIRECTIVE:
+ - before:
+ k = consume (1, "%YAML 1.0")
+ - it can recognize document versions:
+ expect (filter (k (), "type", "major", "minor")).
+ to_equal {type = "VERSION_DIRECTIVE", major = 1, minor = 0}
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 0, column = 0, index = 0}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 0, column = 9, index = 9}
+
+- describe TAG_DIRECTIVE:
+ - it can recognize primary tag handles:
+ k = consume (1, "%TAG ! tag:ben-kiki.org,2000:app/")
+ expect (filter (k (), "handle", "prefix")).
+ to_equal {handle = "!", prefix = "tag:ben-kiki.org,2000:app/"}
+ - it can recognize secondary tag handles:
+ k = consume (1, "%TAG !! tag:yaml.org,2002:")
+ expect (filter (k (), "handle", "prefix")).
+ to_equal {handle = "!!", prefix = "tag:yaml.org,2002:"}
+ - it can recognize named tag handles:
+ k = consume (1, "%TAG !o! tag:ben-kiki.org,2000:\n---")
+ expect (filter (k (), "handle", "prefix")).
+ to_equal {handle = "!o!", prefix = "tag:ben-kiki.org,2000:"}
+
+- describe DOCUMENT_START:
+ - before:
+ k = consume (1, "---")
+ - it recognizes document start marker:
+ expect (k ().type).to_be "DOCUMENT_START"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 0, column = 0, index = 0}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 0, column = 3, index = 3}
+
+- describe DOCUMENT_END:
+ - before:
+ k = consume (2, "foo\n...")
+ - it recognizes the document end marker:
+ expect (k ().type).to_be "DOCUMENT_END"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 1, column = 0, index = 4}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 1, column = 3, index = 7}
+
+- describe ALIAS:
+ - before:
+ k = consume (15, "---\n" ..
+ "hr:\n" ..
+ "- Mark McGwire\n" ..
+ "- &SS Sammy Sosa\n" ..
+ "rbi:\n" ..
+ "- *SS\n" ..
+ "- Ken Griffey")
+ - it recognizes an alias token:
+ expect (filter (k (), "type", "value")).
+ to_equal {type = "ALIAS", value = "SS"}
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 5, column = 2, index = 47}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 5, column = 5, index = 50}
+
+- describe ANCHOR:
+ - before:
+ k = consume (9, "---\n" ..
+ "hr:\n" ..
+ "- Mark McGwire\n" ..
+ "- &SS Sammy Sosa\n" ..
+ "rbi:\n" ..
+ "- *SS\n" ..
+ "- Ken Griffey")
+ - it recognizes an anchor token:
+ expect (filter (k (), "type", "value")).
+ to_equal {type = "ANCHOR", value = "SS"}
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 3, column = 2, index = 25}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 3, column = 5, index = 28}
+
+- describe SCALAR:
+ - before:
+ k = consume (10, "---\n" ..
+ "hr:\n" ..
+ "- Mark McGwire\n" ..
+ "- &SS Sammy Sosa\n" ..
+ "rbi:\n" ..
+ "- *SS\n" ..
+ "- Ken Griffey")
+ - it recognizes a scalar token:
+ expect (filter (k (), "type", "value")).
+ to_equal {type = "SCALAR", value = "Sammy Sosa"}
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 3, column = 6, index = 29}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 3, column = 16, index = 39}
+
+ - context with quoting style:
+ - context plain style:
+ - before:
+ k = consume (2, "---\n" ..
+ " Mark McGwire's\n" ..
+ " year was crippled\n" ..
+ " by a knee injury.\n")
+ - it ignores line-breaks and indentation:
+ expect (k ().value).
+ to_be "Mark McGwire's year was crippled by a knee injury."
+ - it recognizes PLAIN style:
+ expect (k ().style).to_be "PLAIN"
+ - context folded style:
+ - before:
+ k = consume (1, ">\n" ..
+ " Sammy Sosa completed another\n" ..
+ " fine season with great stats.\n" ..
+ "\n" ..
+ " 63 Home Runs\n" ..
+ " 0.288 Batting Average\n" ..
+ "\n" ..
+ " What a year!\n")
+ - it preserves blank lines and deeper indentation:
+ expect (k ().value).
+ to_be ("Sammy Sosa completed another fine season with great stats.\n" ..
+ "\n" ..
+ " 63 Home Runs\n" ..
+ " 0.288 Batting Average\n" ..
+ "\n" ..
+ "What a year!\n")
+ - it recognizes FOLDED style:
+ expect (k ().style).to_be "FOLDED"
+ - context literal style:
+ - before:
+ k = consume (2, [[# ASCII Art]] .. "\n" ..
+ [[--- |]] .. "\n" ..
+ [[ \//||\/||]] .. "\n" ..
+ [[ // || ||__]] .. "\n")
+ - it removes indentation but preserves all line-breaks:
+ expect (k ().value).
+ to_be ([[\//||\/||]] .. "\n" ..
+ [[// || ||__]] .. "\n")
+ - it recognizes LITERAL style:
+ expect (k ().style).to_be "LITERAL"
+
+ - context single quoted style:
+ - before:
+ k = consume (1, [['This quoted scalar]] .. "\n" ..
+ [[ spans two lines.']])
+ - it folds line breaks:
+ expect (k ().value).
+ to_be "This quoted scalar spans two lines."
+ - it does not process escape sequences:
+ # Lua [[ quoting makes sure libyaml sees all the quotes.
+ k = consume (1, [['"Howdy!"\t\u263A']])
+ expect (k ().value).to_be [["Howdy!"\t\u263A]]
+ - it recognizes LITERAL style:
+ expect (k ().style).to_be "SINGLE_QUOTED"
+
+ # Note that we have to single quote the Lua snippets to prevent
+ # libyaml from interpreting the bytes as the spec file is read, so
+ # that the raw strings get correctly passed to the Lua compiler.
+ - context double quoted style:
+ - it folds line breaks: '
+ k = consume (5, [[quoted: "This quoted scalar]] .. "\n" ..
+ [[ spans two lines\n"]])
+ expect (k ().value).
+ to_be "This quoted scalar spans two lines\n"'
+ - it recognizes unicode escape sequences: '
+ k = consume (5, [[unicode: "Sosa did fine.\u263A"]])
+ expect (k ().value).to_be "Sosa did fine.\226\152\186"'
+ - it recognizes control escape sequences: '
+ k = consume (5, [[control: "\b1998\t1999\t2000\n"]])
+ expect (k ().value).to_be "\b1998\t1999\t2000\n"'
+ - it recognizes hexadecimal escape sequences: '
+ k = consume (5, [[hexesc: "\x41\x42\x43 is ABC"]])
+ expect (k ().value).to_be "ABC is ABC"'
+
+ - context indentation determines scope: '
+ k = consume (5, "name: Mark McGwire\n" ..
+ "accomplishment: >\n" ..
+ " Mark set a major league\n" ..
+ " home run record in 1998.\n" ..
+ "stats: |\n" ..
+ " 65 Home Runs\n" ..
+ " 0.278 Batting Average\n")
+ expect (k ().value).to_be "Mark McGwire"
+ expect (k ().type).to_be "KEY"
+ expect (k ().value).to_be "accomplishment"
+ expect (k ().type).to_be "VALUE"
+ expect (k ().value).
+ to_be "Mark set a major league home run record in 1998.\n"
+ expect (k ().type).to_be "KEY"
+ expect (k ().value).to_be "stats"
+ expect (k ().type).to_be "VALUE"
+ expect (k ().value).to_be "65 Home Runs\n0.278 Batting Average\n"'
+
+- describe TAG:
+ - it recognizes local tags: '
+ k = consume (5, "application specific tag: !something |\n" ..
+ " The semantics of the tag\n" ..
+ " above may be different for\n" ..
+ " different documents.")
+ expect (filter (k (), "type", "handle", "suffix")).
+ to_equal {type = "TAG", handle = "!", suffix = "something"}'
+ - it recognizes global tags: '
+ k = consume (5, "picture: !!binary |\n" ..
+ " R0lGODlhDAAMAIQAAP//9/X\n" ..
+ " 17unp5WZmZgAAAOfn515eXv\n" ..
+ " Pz7Y6OjuDg4J+fn5OTk6enp\n" ..
+ " 56enmleECcgggoBADs=")
+ expect (filter (k (), "type", "handle", "suffix")).
+ to_equal {type = "TAG", handle = "!!", suffix = "binary"}'
+
+- describe BLOCK_SEQUENCE_START:
+ - before: '
+ k = consume (5, "fubar:\n" ..
+ " - foo\n" ..
+ " - bar\n")'
+ - it recognizes a sequence start token:
+ expect (k ().type).to_be "BLOCK_SEQUENCE_START"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 1, column = 2, index = 9}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 1, column = 2, index = 9}
+
+- describe BLOCK_MAPPING_START:
+ - before: 'k = consume (3, "-\n foo: bar\n-")'
+ - it recognizes a mapping start token:
+ expect (k ().type).to_be "BLOCK_MAPPING_START"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 1, column = 2, index = 4}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 1, column = 2, index = 4}
+
+- describe BLOCK_ENTRY:
+ - before: 'k = consume (2, "-\n foo: bar\n-")'
+ - it recognizes a sequence block entry token: '
+ k = consume (8, "fubar:\n" ..
+ " - foo\n" ..
+ " - bar\n")
+ expect (k ().type).to_be "BLOCK_ENTRY"'
+ - it recognizes a mapping block entry token:
+ expect (k ().type).to_be "BLOCK_ENTRY"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 0, column = 0, index = 0}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 0, column = 1, index = 1}
+
+- describe BLOCK_END:
+ - before: 'k = consume (8, "-\n foo: bar\n-")'
+ - it recognizes a sequence block end token: '
+ k = consume (10, "fubar:\n" ..
+ " - foo\n" ..
+ " - bar\n")
+ expect (k ().type).to_be "BLOCK_END"'
+ - it recognizes a mapping block end token:
+ expect (k ().type).to_be "BLOCK_END"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 2, column = 0, index = 13}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 2, column = 0, index = 13}
+
+- describe FLOW_SEQUENCE_START:
+ - before: '
+ k = consume (5, "fubar: [foo, bar]\n")'
+ - it recognizes a sequence start token:
+ expect (k ().type).to_be "FLOW_SEQUENCE_START"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 0, column = 7, index = 7}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 0, column = 8, index = 8}
+
+- describe FLOW_SEQUENCE_END:
+ - before: '
+ k = consume (9, "fubar: [foo, bar]\n")'
+ - it recognizes a sequence end token:
+ expect (k ().type).to_equal "FLOW_SEQUENCE_END"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 0, column = 16, index = 16}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 0, column = 17, index = 17}
+
+- describe FLOW_ENTRY:
+ - before: 'k = consume (6, "{foo: bar, baz: quux}")'
+ - it recognizes a sequence flow entry: '
+ k = consume (6, "[foo: bar, baz: quux]")
+ expect (k ().type).to_be "FLOW_ENTRY"'
+ - it recognizes a mapping flow entry:
+ expect (k ().type).to_be "FLOW_ENTRY"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 0, column = 9, index = 9}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 0, column = 10, index = 10}
+
+- describe FLOW_MAPPING_START:
+ - before: 'k = consume (1, "{foo: bar, baz: quux}")'
+ - it recognizes flow style:
+ expect (k ().type).to_be "FLOW_MAPPING_START"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 0, column = 0, index = 0}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 0, column = 1, index = 1}
+
+- describe FLOW_MAPPING_END:
+ - before: 'k = consume (6, "{foo: bar}\n")'
+ - it recognizes the mapping end token:
+ expect (k ().type).to_equal "FLOW_MAPPING_END"
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 0, column = 9, index = 9}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 0, column = 10, index = 10}
+
+- describe KEY:
+ - before: 'k = consume (2, "{the key: the value, another key: meh}")'
+ - it recognizes a flow mapping key token:
+ expect (k ().type).to_be "KEY"
+ - it recognizes a block mapping key token: '
+ k = consume (2, "the key: the value\nanother key: meh\n")
+ expect (k ().type).to_be "KEY"'
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 0, column = 1, index = 1}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 0, column = 1, index = 1}
+
+- describe VALUE:
+ - before: 'k = consume (4, "{the key: the value, another key: meh}")'
+ - it recognizes a flow mapping value token:
+ expect (k ().type).to_be "VALUE"
+ - it recognizes a block mapping key value: '
+ k = consume (4, "the key: the value\nanother key: meh\n")
+ expect (k ().type).to_be "VALUE"'
+ - it reports token start marker:
+ expect (k ().start_mark).to_equal {line = 0, column = 8, index = 8}
+ - it reports token end marker:
+ expect (k ().end_mark).to_equal {line = 0, column = 9, index = 9}
diff --git a/spec/lib_lyaml_functional_spec.yaml b/spec/lib_lyaml_functional_spec.yaml
new file mode 100644
index 000000000000..cfd8676f0176
--- /dev/null
+++ b/spec/lib_lyaml_functional_spec.yaml
@@ -0,0 +1,121 @@
+# LYAML binding for Lua 5.1, 5.2, 5.3 & 5.4
+# Copyright (C) 2013-2022 Gary V. Vaughan
+
+before:
+ this_module = 'lyaml.functional'
+ global_table = '_G'
+
+ exported_apis = {'NULL', 'anyof', 'id', 'iscallable', 'isnull'}
+
+ M = require(this_module)
+
+ nop = function() end
+
+ fail = function() return nil end
+ pass = function() return false end
+ throw = function() error 'oh noes!' end
+
+ parmlist = pack(
+ nil,
+ false,
+ 42,
+ 'str',
+ io.stderr,
+ {},
+ nop,
+ setmetatable({}, {__call=nop})
+ )
+
+
+specify functional:
+- context when required:
+ - context by name:
+ - it does not touch the global table:
+ expect(show_apis{added_to=global_table, by=this_module}).to_equal{}
+ - it exports the decumented apis:
+ t = {}
+ for k in pairs(M) do t[#t + 1] = k end
+ expect(t).to_contain.a_permutation_of(exported_apis)
+
+
+- describe anyof:
+ - before:
+ f = M.anyof
+
+ - it returns a callable:
+ expect(f{nop}).to_be_callable()
+ expect(f{nop, nop}).to_be_callable()
+ - it returns a lazy function that calls arguments if necessary:
+ expect(f{pass, throw}()).not_to_raise 'any error'
+ expect(f{pass, throw}()).not_to_be(nil)
+ - it silently skips non-callable arguments:
+ expect(f(list({nil, false, true}))()).to_be(nil)
+ expect(f{1, 2, pass, 'pass'}()).not_to_be(nil)
+ - it returns non-nil if any callable returns non-nil:
+ expect(f{pass, pass, fail}()).not_to_be(nil)
+ expect(f{pass, fail}()).not_to_be(nil)
+ expect(f{fail, pass}()).not_to_be(nil)
+ - it returns nil if all callables are nil:
+ expect(f{fail}()).to_be(nil)
+ expect(f{fail, fail}()).to_be(nil)
+ expect(f{fail, fail, fail}()).to_be(nil)
+ - it propagates data to all callables:
+ expect(f{fail, function(...) return select('#', ...) end}(nil)).to_be(1)
+ expect(f{function(...) return select('#', ...) end, fail}(nil, false)).to_be(2)
+ expect(f{function(...) return select('#', ...) end, pass}(nil, false)).to_be(2)
+ - it returns the first non-nil callables result:
+ expect(f{fail, function(...) return ... end}(42)).to_be(42)
+ expect(f{function(...) return ... end, fail}(42)).to_be(42)
+ expect(f{pass, fail}(42)).to_be(false)
+ expect(f{fail, pass}(42)).to_be(false)
+ - it propagates only the first return value:
+ expect(f{fail, function(...) return ... end}(1, 2, 5)).to_be(1)
+ expect(f{function(...) return ... end, fail}(1, 2, 5)).to_be(1)
+ expect(f{function(...) return ... end, pass}(1, 2, 5)).to_be(1)
+
+
+- describe id:
+ - before:
+ f = M.id
+
+ - it returns its own argument:
+ expect(f(false)).to_be(false)
+ expect(f(42)).to_be(42)
+ - it handles nil argumen:
+ expect(f(nil)).to_be(nil)
+ - it handles missing argument:
+ expect(f()).to_be()
+ - it returns multiple arguments:
+ expect(f(nil, 1, fn, false, nil)).to_be(nil, 1, fn, false, nil)
+
+
+- describe iscallable:
+ - before:
+ f = M.iscallable
+
+ - it returns callable for a callable:
+ expect(f(f)).to_be(f)
+ expect(f(setmetatable({}, {__call=f}))).to_be(f)
+ - it returns nil for a non-callable:
+ expect(f()).to_be(nil)
+ expect(f(nil)).to_be(nil)
+ expect(f(false)).to_be(nil)
+ expect(f(true)).to_be(nil)
+ expect(f'str').to_be(nil)
+ expect(f(42)).to_be(nil)
+ expect(f(setmetatable({}, {__index={}}))).to_be(nil)
+ expect(f(setmetatable({}, {__call=42}))).to_be(nil)
+
+
+- describe isnull:
+ - before:
+ NULL = M.NULL
+ f = M.isnull
+
+ - it returns 'true' for a NULL argument:
+ expect(f(NULL)).to_be(true)
+ - it returns 'false' for any argument other than NULL:
+ for i=1,parmlist.n do
+ expect(f(parmlist[i])).to_be(false)
+ end
+
diff --git a/spec/lib_lyaml_spec.yaml b/spec/lib_lyaml_spec.yaml
new file mode 100644
index 000000000000..f6c7e1ef4bed
--- /dev/null
+++ b/spec/lib_lyaml_spec.yaml
@@ -0,0 +1,343 @@
+# LYAML binding for Lua 5.1, 5.2, 5.3 & 5.4
+# Copyright (C) 2013-2022 Gary V. Vaughan
+
+before: |
+ lyaml = require "lyaml"
+
+ -- Always use the new multi-doc capable API.
+ lyaml.legacy = lyaml.load
+ lyaml.load = function (stream) return lyaml.legacy (stream, true) end
+
+specify lyaml:
+- describe dumping:
+ - context streams:
+ - it writes an empty stream:
+ expect (lyaml.dump {}).to_equal ""
+
+ - context documents:
+ - it writes an empty document:
+ expect (lyaml.dump {""}).to_match "^%-%-%-%s*''\n%.%.%.%s*$"
+ - it writes consecutive documents:
+ expect (lyaml.dump {"one", "two"}).
+ to_match "^%-%-%-%s+one%s*\n%.%.%.%s*\n%-%-%-%s+two%s*\n%.%.%.%s*$"
+
+ - context scalars:
+ - it writes null:
+ expect (lyaml.dump {lyaml.null}).to_be "--- ~\n...\n"
+ expect (lyaml.dump {"~"}).to_be "--- '~'\n...\n"
+ - it writes booleans:
+ expect (lyaml.dump {"true"}).to_be "--- 'true'\n...\n"
+ expect (lyaml.dump {"yes"}).to_be "--- 'yes'\n...\n"
+ expect (lyaml.dump {"false"}).to_be "--- 'false'\n...\n"
+ expect (lyaml.dump {"no"}).to_be "--- 'no'\n...\n"
+ expect (lyaml.dump {true}).to_be "--- true\n...\n"
+ expect (lyaml.dump {false}).to_be "--- false\n...\n"
+ - it writes numbers:
+ expect (lyaml.dump {"123"}).to_be "--- '123'\n...\n"
+ expect (lyaml.dump {"12.3"}).to_be "--- '12.3'\n...\n"
+ expect (lyaml.dump {"0/0"}).to_be "--- 0/0\n...\n"
+ expect (lyaml.dump {123}).to_be "--- 123\n...\n"
+ expect (lyaml.dump {12.3}).to_be "--- 12.3\n...\n"
+ expect (lyaml.dump {0/0}).to_be "--- .nan\n...\n"
+ expect (lyaml.dump {math.huge}).to_be "--- .inf\n...\n"
+ expect (lyaml.dump {-math.huge}).to_be "--- -.inf\n...\n"
+ - it writes strings:
+ expect (lyaml.dump {"a string"}).to_be "--- a string\n...\n"
+ expect (lyaml.dump {"'a string'"}).to_be "--- '''a string'''\n...\n"
+ expect (lyaml.dump {"a\nmultiline\nstring"}).to_be "--- |-\n a\n multiline\n string\n...\n"
+ expect (lyaml.dump {""}).to_be "--- ''\n...\n"
+
+ - context sequences:
+ - it writes a sequence:
+ expect (lyaml.dump {{1, 2, 3}}).to_contain "- 1\n- 2\n- 3"
+
+ - context mappings:
+ - it writes a mapping: |
+ expect (lyaml.dump {{a=1, b=2, c=3, d=""}}).
+ to_contain.all_of {"a: 1", "b: 2", "c: 3", "d: ''"}
+ - it writes a mapping of mixed keys: |
+ expect (lyaml.dump {{[1]=1, [2]=2, three="three", four="4", [5]="five"}}).
+ to_contain.all_of {"1: 1", "2: 2", "three: three", "four: '4'", "5: five"}
+ - it writes a mapping of integer keys starting at two: |
+ expect (lyaml.dump {{[2]=2, [3]=3, [4]=4}}).
+ to_contain.all_of {"2: 2", "3: 3", "4: 4"}
+ - it writes a mapping of mixed keys starting at one: |
+ expect (lyaml.dump {{[1]=1, [2]=2, [3]=3, foo="bar"}}).
+ to_contain.all_of {"1: 1", "2: 2", "3: 3", "foo: bar"}
+ - it writes a mapping of mixed keys starting at two: |
+ expect (lyaml.dump {{[2]=2, [3]=3, [4]=4, foo="bar"}}).
+ to_contain.all_of {"2: 2", "3: 3", "4: 4", "foo: bar"}
+ - it writes a table containing nils (jumps in index) as mapping: |
+ expect (lyaml.dump {{1, 2, nil, 3, 4}}).
+ to_contain.all_of {"1: 1", "2: 2", "4: 3", "5: 4"}
+
+ - context anchors and aliases:
+ - before:
+ anchors = {
+ MAP = {["Mark McGwire"] = 65, ["Sammy Sosa"] = 63},
+ SEQ = {"Mark McGwire", "Sammy Sosa"},
+ }
+ - it writes scalar anchors: '
+ anchors = { SS = "Sammy Sosa" }
+ expect (lyaml.dump ({{{anchor = anchors.SS}, {alias = anchors.SS}}}, anchors)).
+ to_contain "- anchor: &SS Sammy Sosa\n- alias: *SS\n"'
+ - it writes sequence anchors: '
+ expect (lyaml.dump ({{{anchor = anchors.SEQ}, {alias = anchors.SEQ}}}, anchors)).
+ to_contain "\n- anchor: &SEQ\n - Mark McGwire\n - Sammy Sosa\n- alias: *SEQ\n"'
+ - it writes mapping anchors: '
+ expect (lyaml.dump ({{{anchor = anchors.MAP}, {alias = anchors.MAP}}}, anchors)).
+ to_match "\n%- anchor: &MAP\n %w+ %w+: %d+\n %w+ %w+: %d+\n%- alias: %*MAP\n"'
+
+
+- describe loading:
+ - before:
+ fn = lyaml.load
+
+ - it loads an empty stream:
+ expect (fn "").to_equal {}
+ - it ignores comments: '
+ expect (fn "# A comment\nnon-comment # trailing comment\n").
+ to_equal { "non-comment" }'
+ - it diagnoses unexpected events: '
+ expect (fn "...").to_error "1:1: did not find expected node content"
+ expect (fn "---\n...\ngarbage\n").
+ to_error "2:1: did not find expected <document start>"
+ expect (fn " *ALIAS").
+ to_error "1:2: invalid reference: ALIAS"'
+
+ - context documents:
+ - it lyaml.loads an empty document:
+ expect (fn "---").to_equal {lyaml.null}
+ expect (fn "---\n").to_equal {lyaml.null}
+ expect (fn "---\n...").to_equal {lyaml.null}
+ expect (fn "---\n...\n").to_equal {lyaml.null}
+ - it lyaml.loads multiple documents:
+ expect (fn "one\n---\ntwo").to_equal {"one", "two"}
+ expect (fn "---\none\n---\ntwo").to_equal {"one", "two"}
+ expect (fn "one\n...\n---\ntwo\n...").to_equal {"one", "two"}
+ expect (fn "---\none\n...\n---\ntwo\n...").to_equal {"one", "two"}
+ - it reports an empty document:
+ expect (fn "---\n---\ntwo\n---").
+ to_equal {lyaml.null, "two", lyaml.null}
+ expect (fn "---\n...\n---\ntwo\n---").
+ to_equal {lyaml.null, "two", lyaml.null}
+ expect (fn "---\n...\n---\ntwo\n...\n---").
+ to_equal {lyaml.null, "two", lyaml.null}
+ expect (fn "---\n...\n---\ntwo\n...\n---\n...").
+ to_equal {lyaml.null, "two", lyaml.null}
+
+ - context version directive:
+ - it recognizes version number:
+ expect (fn "%YAML 1.1\n---").to_equal {lyaml.null}
+ - it diagneses missing document start:
+ expect (fn "%YAML 1.1").
+ to_error "expected <document start>"
+ - it diagnoses unsupported version:
+ expect (fn "%YAML 2.0\n---").
+ to_error "incompatible YAML document"
+
+ - context tag directive:
+ - it recognizes primary tag directive: '
+ expect (fn ("%TAG ! tag:yaml.org,2002:\n" ..
+ "---\n" ..
+ "!bool N")).to_equal {false}'
+ - it recognizes secondary tag directive: '
+ expect (fn ("%TAG !! tag:ben-kiki.org,2000:\n" ..
+ "---\n" ..
+ "!!bool untrue")).to_equal {"untrue"}'
+ - it recognizes named tag directive: '
+ expect (fn ("%TAG !bkk! tag:ben-kiki.org,2000:\n" ..
+ "---\n" ..
+ "!bkk!bool untrue")).to_equal {"untrue"}'
+ - it diagnoses undefined tag handles: '
+ expect (fn ("!bkk!bool untrue")).
+ to_error "undefined tag handle"'
+
+ - context scalars:
+ - it recognizes null: '
+ expect (fn "~").to_equal {lyaml.null}
+ expect (fn "foo: ").to_equal {{foo = lyaml.null}}
+ expect (fn "foo: ~").to_equal {{foo = lyaml.null}}
+ expect (fn "foo: !!null").to_equal {{foo = lyaml.null}}
+ expect (fn "foo: null").to_equal {{foo = lyaml.null}}
+ expect (fn "foo: Null").to_equal {{foo = lyaml.null}}
+ expect (fn "foo: NULL").to_equal {{foo = lyaml.null}}'
+ - it recognizes booleans: '
+ expect (fn "true").to_equal {true}
+ expect (fn "false").to_equal {false}
+ expect (fn "yes").to_equal {true}
+ expect (fn "no").to_equal {false}'
+ - it loads bare y and n as strings:
+ expect (fn "y").to_equal {"y"}
+ expect (fn "n").to_equal {"n"}
+ - it recognizes integers:
+ expect (fn "0b001010011010").to_equal {666}
+ expect (fn "0b0010_1001_1010").to_equal {666}
+ expect (fn "+0b001_010_011_010").to_equal {666}
+ expect (fn "-0b0010_1001_1010").to_equal {-666}
+ expect (fn "0_1232").to_equal {666}
+ expect (fn "-01232").to_equal {-666}
+ expect (fn "666").to_equal {666}
+ expect (fn "0x29a").to_equal {666}
+ expect (fn "-0x29a").to_equal {-666}
+ expect (fn "12_345_678").to_equal {12345678}
+ expect (fn "11:6").to_equal {666}
+ - it recognizes floats:
+ expect (fn "12.3").to_equal {12.3}
+ expect (fn "685.230_15e+03").to_equal {685230.15}
+ expect (fn "685_230.15e+03").to_equal {685230150.0}
+ expect (fn "12_345_678.9").to_equal {12345678.9}
+ expect (fn "11:6.777").to_equal {666.777}
+ expect (fn ".Inf").to_equal {math.huge}
+ expect (fn "-.inf").to_equal {-math.huge}
+ nant = fn ".NaN"
+ expect (nant[1]).not_to_equal (nant[1])
+ - it recognizes strings:
+ expect (fn "a string").to_equal {"a string"}
+ expect (fn "'''a string'''").to_equal {"'a string'"}
+ expect (fn "|-\n a\n multiline\n string").to_equal {"a\nmultiline\nstring"}
+ expect (fn "'yes'").to_equal {"yes"}
+ expect (fn "''").to_equal {""}
+ expect (fn '""').to_equal {""}
+
+ - context global tags:
+ - it recognizes !!null:
+ expect (fn "!!null").to_equal {lyaml.null}
+ - it recognizes !!bool: |
+ expect (fn '!!bool "true"').to_equal {true}
+ expect (fn '!!bool true').to_equal {true}
+ expect (fn '!!bool True').to_equal {true}
+ expect (fn '!!bool TRUE').to_equal {true}
+ expect (fn "!!bool 'false'").to_equal {false}
+ expect (fn '!!bool false').to_equal {false}
+ expect (fn '!!bool False').to_equal {false}
+ expect (fn '!!bool FALSE').to_equal {false}
+ expect (fn '!!bool "yes"').to_equal {true}
+ expect (fn "!!bool 'Yes'").to_equal {true}
+ expect (fn '!!bool YES').to_equal {true}
+ expect (fn '!!bool no').to_equal {false}
+ expect (fn "!!bool 'No'").to_equal {false}
+ expect (fn '!!bool "NO"').to_equal {false}
+ expect (fn '!!bool garbage').
+ to_raise "invalid 'tag:yaml.org,2002:bool' value: 'garbage'"
+ - it loads explicit y and n as booleans:
+ expect (fn '!!bool Y').to_equal {true}
+ expect (fn '!!bool y').to_equal {true}
+ expect (fn '!!bool N').to_equal {false}
+ expect (fn '!!bool n').to_equal {false}
+ - it recognizes !!float: |
+ expect (fn '!!float 42').to_equal {42.0}
+ expect (fn '!!float "42"').to_equal {42.0}
+ expect (fn '!!float +42').to_equal {42.0}
+ expect (fn '!!float 12.3').to_equal {12.3}
+ expect (fn '!!float -3.141592').to_equal {-3.141592}
+ expect (fn '!!float 685_230.15e+03').to_equal {685230150.0}
+ expect (fn '!!float +685.230_15e+03').to_equal {685230.15}
+ expect (fn '!!float 12_345_678.9').to_equal {12345678.9}
+ expect (fn '!!float -0:3:11:6.777').to_equal {-11466.777}
+ expect (fn '!!float .Inf').to_equal {math.huge}
+ expect (fn '!!float -.inf').to_equal {-math.huge}
+ nant = fn '!!float .NaN'
+ expect (nant[1]).not_to_equal (nant[1])
+ expect (fn '!!float garbage').
+ to_raise "invalid 'tag:yaml.org,2002:float' value: 'garbage'"
+ - it recognizes !!int: |
+ expect (fn '!!int 0b0010_1001_1010').to_equal {666}
+ expect (fn '!!int "+0b001_010_011_010"').to_equal {666}
+ expect (fn '!!int -0b0010_1001_1010').to_equal {-666}
+ expect (fn '!!int 0_1232').to_equal {666}
+ expect (fn '!!int "-01232"').to_equal {-666}
+ expect (fn '!!int 666').to_equal {666}
+ expect (fn '!!int 0668').to_equal {668}
+ expect (fn '!!int "0x29a"').to_equal {666}
+ expect (fn '!!int -0x29a').to_equal {-666}
+ expect (fn '!!int 12_345_678').to_equal {12345678}
+ expect (fn '!!int 11:6').to_equal {666}
+ expect (fn '!!int 12.3').
+ to_raise "invalid 'tag:yaml.org,2002:int' value: '12.3'"
+ expect (fn '!!int garbage').
+ to_raise "invalid 'tag:yaml.org,2002:int' value: 'garbage'"
+
+ - context sequences:
+ - it recognizes block sequences:
+ expect (fn "- ~\n- \n- true\n- 42").
+ to_equal {{lyaml.null, lyaml.null, true, 42}}
+ - it recognizes flow sequences:
+ expect (fn "[~, true, 42]").
+ to_equal {{lyaml.null, true, 42}}
+
+ - context anchors and aliases:
+ - it resolves scalar anchors: '
+ expect (fn "anchor: &SS Sammy Sosa\nalias: *SS").
+ to_equal {{anchor = "Sammy Sosa", alias = "Sammy Sosa"}}'
+ - it resolves sequence anchors: '
+ expect (fn "anchor: &SEQ [Mark McGwire, Sammy Sosa]\nalias: *SEQ").
+ to_equal {{anchor = {"Mark McGwire", "Sammy Sosa"},
+ alias = {"Mark McGwire", "Sammy Sosa"}}}'
+ - it resolves mapping anchors: '
+ expect (fn "anchor: &MAP {Mark McGwire: 65, Sammy Sosa: 63}\nalias: *MAP").
+ to_equal {{anchor = {["Mark McGwire"] = 65, ["Sammy Sosa"] = 63},
+ alias = {["Mark McGwire"] = 65, ["Sammy Sosa"] = 63}}}'
+
+ - context a map:
+ - it recognizes block mapping: |
+ expect (fn "'null': ~\nboolean: yes\nnumber: 3.14").
+ to_equal {{null = lyaml.null, boolean = true, number = 3.14}}
+ - it recognizes flow mapping: |
+ expect (fn "{null: null, boolean: yes, number: 3.14}").
+ to_equal {{[lyaml.null] = lyaml.null, boolean = true, number = 3.14}}
+ - context with merge keys:
+ - before: |
+ merge = {x=1, y=2}
+ override = {x=0, z=2}
+ bogus = true
+ YAML = "- &MERGE {x: 1, y: 2}\n" ..
+ "- &OVERRIDE {x: 0, z: 2}\n" ..
+ "- &BOGUS true\n"
+ - it diagnoses invalid merge events: |
+ expect (fn "-\n !!merge : x\n z: 3").
+ to_raise "invalid 'tag:yaml.org,2002:merge' merge event: x"
+ expect (fn "-\n << : x\n z: 3").
+ to_raise "invalid '<<' merge event: x"
+ - it diagnoses invalid merge alias types: |
+ expect (fn (YAML .. "-\n !!merge : *BOGUS")).
+ to_raise "invalid 'tag:yaml.org,2002:merge' merge event: true"
+ expect (fn (YAML .. "-\n << : *BOGUS")).
+ to_raise "invalid '<<' merge event: true"
+ - it diagnoses invalid merge sequence elements: |
+ expect (fn (YAML .. '-\n !!merge : [*MERGE, OVERRIDE]')).
+ to_raise "invalid 'tag:yaml.org,2002:merge' sequence element 2: OVERRIDE"
+ expect (fn (YAML .. '-\n <<: [*MERGE, OVERRIDE]')).
+ to_raise "invalid '<<' sequence element 2: OVERRIDE"
+ - it diagnoses invalid merge sequence alias tyes: |
+ expect (fn (YAML .. '-\n !!merge : [*MERGE, *BOGUS]')).
+ to_raise "invalid 'tag:yaml.org,2002:merge' sequence element 2: true"
+ expect (fn (YAML .. '-\n <<: [*MERGE, *BOGUS]')).
+ to_raise "invalid '<<' sequence element 2: true"
+ - it supports merging bare maps: |
+ expect (fn ("-\n !!merge : {x: 1, y: 2}\n z: 3")).
+ to_equal {{{x=1, y=2, z=3}}}
+ expect (fn "-\n <<: {x: 1, y: 2}\n z: 3").
+ to_equal {{{x=1, y=2, z=3}}}
+ - it supports merging map aliases: |
+ expect (fn (YAML .. "-\n !!merge : *MERGE\n z: 3")).
+ to_equal {{merge, override, bogus, {x=1, y=2, z=3}}}
+ expect (fn (YAML .. "-\n <<: *MERGE\n z: 3")).
+ to_equal {{merge, override, bogus, {x=1, y=2, z=3}}}
+ - it merges sequence of bare maps with decreasing precedence: |
+ expect (fn "-\n !!merge : [{x: 1, y: 2}, {x: 0, z: 2}]\n z: 3").
+ to_equal {{{x=1, y=2, z=3}}}
+ expect (fn "-\n <<: [{x: 1, y: 2}, {x: 0, z: 2}]\n z: 3").
+ to_equal {{{x=1, y=2, z=3}}}
+ - it merges sequence of aliases with decreasing precedence: |
+ expect (fn (YAML .. "-\n !!merge : [*MERGE, *OVERRIDE]\n z: 3")).
+ to_equal {{merge, override, bogus, {x=1, y=2, z=3}}}
+ expect (fn (YAML .. "-\n <<: [*MERGE, *OVERRIDE]\n z: 3")).
+ to_equal {{merge, override, bogus, {x=1, y=2, z=3}}}
+ - it merges a sequence alias with decreasing precedence: |
+ seq = {merge, override}
+ r = {{merge, override, bogus, seq, {x=1, y=2, z=3}}}
+ expect (fn (YAML .. "- &SEQ [*MERGE, *OVERRIDE]\n" ..
+ "-\n !!merge : *SEQ\n z: 3")).to_equal (r)
+ expect (fn (YAML .. "- &SEQ [*MERGE, *OVERRIDE]\n" ..
+ "-\n <<: *SEQ\n z: 3")).to_equal (r)
diff --git a/spec/spec_helper.lua b/spec/spec_helper.lua
new file mode 100644
index 000000000000..1ba7a3a7559f
--- /dev/null
+++ b/spec/spec_helper.lua
@@ -0,0 +1,277 @@
+--[[
+ LYAML binding for Lua 5.1, 5.2, 5.3 & 5.4
+ Copyright (C) 2013-2022 Gary V. Vaughan
+]]
+
+do
+ local std = require 'specl.std'
+ local spawn = require 'specl.shell'.spawn
+ local objdir = spawn('./build-aux/luke --value=objdir').output
+
+
+ package.path = std.package.normalize(
+ './lib/?.lua',
+ './lib/?/init.lua',
+ package.path
+ )
+ package.cpath = std.package.normalize(
+ './' .. objdir:match("^objdir='(.*)'") .. '/?.so',
+ './' .. objdir:match("^objdir='(.*)'") .. '/?.dll',
+ package.cpath
+ )
+end
+
+local hell = require 'specl.shell'
+
+
+yaml = require 'yaml'
+
+BOM = string.char(254, 255) -- UTF-16 Byte Order Mark
+
+-- Allow use of bare 'pack' and 'unpack' even in Lua > 5.2.
+pack = table.pack or function(...) return {n = select('#', ...), ...} end
+unpack = table.unpack or unpack
+list = pack
+
+
+function dump(e)
+ print(std.string.prettytostring(e))
+end
+
+
+function github_issue(n)
+ return 'see http://github.com/gvvaughan/lyaml/issues/' .. tostring(n)
+end
+
+
+-- Output a list of event tables to the given emitter.
+function emitevents(emitter, list)
+ for _, v in ipairs(list) do
+ if type(v) == 'string' then
+ ok, msg = emitter.emit {type=v}
+ elseif type(v) == 'table' then
+ ok, msg = emitter.emit(v)
+ else
+ error 'expected table or string argument'
+ end
+
+ if not ok then
+ error(msg)
+ elseif ok and msg then
+ return msg
+ end
+ end
+end
+
+
+-- Create a new emitter and send STREAM_START, listed events and STREAM_END.
+function emit(list)
+ local emitter = yaml.emitter()
+ emitter.emit {type='STREAM_START'}
+ emitevents(emitter, list)
+ local _, msg = emitter.emit {type='STREAM_END'}
+ return msg
+end
+
+
+-- Create a new parser for STR, and consume the first N events.
+function consume(n, str)
+ local e = yaml.parser(str)
+ for n = 1, n do
+ e()
+ end
+ return e
+end
+
+
+-- Return a new table with only elements of T that have keys listed
+-- in the following arguments.
+function filter(t, ...)
+ local u = {}
+ for _, k in ipairs {...} do
+ u[k] = t[k]
+ end
+ return u
+end
+
+
+function iscallable(x)
+ return type(x) == 'function' or type((getmetatable(x) or {}).__call) == 'function'
+end
+
+
+local function mkscript(code)
+ local f = os.tmpname()
+ local h = io.open(f, 'w')
+ -- TODO: Move this into specl, or expose arguments so that we can
+ -- turn this on and off based on specl `--coverage` arg.
+ h:write "pcall(require, 'luacov')"
+ h:write(code)
+ h:close()
+ return f
+end
+
+
+-- Allow user override of LUA binary used by hell.spawn, falling
+-- back to environment PATH search for 'lua' if nothing else works.
+local LUA = os.getenv 'LUA' or 'lua'
+
+
+--- Run some Lua code with the given arguments and input.
+-- @string code valid Lua code
+-- @tparam[opt={}] string|table arg single argument, or table of
+-- arguments for the script invocation.
+-- @string[opt] stdin standard input contents for the script process
+-- @treturn specl.shell.Process|nil status of resulting process if
+-- execution was successful, otherwise nil
+function luaproc(code, arg, stdin)
+ local f = mkscript(code)
+ if type(arg) ~= 'table' then arg = {arg} end
+ local cmd = {LUA, f, unpack(arg)}
+ -- inject env and stdin keys separately to avoid truncating `...` in
+ -- cmd constructor
+ cmd.env = { LUA_PATH=package.path, LUA_INIT='', LUA_INIT_5_2='' }
+ cmd.stdin = stdin
+ local proc = hell.spawn(cmd)
+ os.remove(f)
+ return proc
+end
+
+
+local function tabulate_output(code)
+ local proc = luaproc(code)
+ if proc.status ~= 0 then return error(proc.errout) end
+ local r = {}
+ proc.output:gsub('(%S*)[%s]*',
+ function(x)
+ if x ~= '' then r[x] = true end
+ end)
+ return r
+end
+
+
+--- Show changes to tables wrought by a require statement.
+-- There are a few modes to this function, controlled by what named
+-- arguments are given. Lists new keys in T1 after `require "import"`:
+--
+-- show_apis {added_to=T1, by=import}
+--
+-- @tparam table argt one of the combinations above
+-- @treturn table a list of keys according to criteria above
+function show_apis(argt)
+ return tabulate_output([[
+ local before, after = {}, {}
+ for k in pairs(]] .. argt.added_to .. [[) do
+ before[k] = true
+ end
+
+ local M = require ']] .. argt.by .. [['
+ for k in pairs(]] .. argt.added_to .. [[) do
+ after[k] = true
+ end
+
+ for k in pairs(after) do
+ if not before[k] then print(k) end
+ end
+ ]])
+end
+
+
+
+--[[ ========= ]]--
+--[[ Call Spy. ]]--
+--[[ ========= ]]--
+
+
+spy = function(fn)
+ return setmetatable({}, {
+ __call = function(self, ...)
+ self[#self + 1] = list(...)
+ return fn(...)
+ end,
+ })
+end
+
+
+do
+ --[[ ================ ]]--
+ --[[ Custom matchers. ]]--
+ --[[ ================ ]]--
+
+ local matchers = require 'specl.matchers'
+ local eqv = require 'specl.std'.operator.eqv
+ local str = require 'specl.std'.string.tostring
+
+ local Matcher, matchers = matchers.Matcher, matchers.matchers
+ local concat = table.concat
+
+
+ matchers.be_called_with = Matcher {
+ function(self, actual, expected)
+ for i,v in ipairs(expected or {}) do
+ if not eqv(actual[i], v) then
+ return false
+ end
+ end
+ return true
+ end,
+
+ actual = 'argmuents',
+
+ format_expect = function(self, expect)
+ return ' arguments (' .. str(expect) .. '), '
+ end,
+ }
+
+ matchers.be_callable = Matcher {
+ function(self, actual, _)
+ return iscallable(actual)
+ end,
+
+ actual = 'callable',
+
+ format_expect = function(self, expect)
+ return ' callable, '
+ end,
+ }
+
+ matchers.be_falsey = Matcher {
+ function(self, actual, _)
+ return not actual and true or false
+ end,
+
+ actual = 'falsey',
+
+ format_expect = function(self, expect)
+ return ' falsey, '
+ end,
+ }
+
+ matchers.be_truthy = Matcher {
+ function(self, actual, _)
+ return actual and true or false
+ end,
+
+ actual = 'truthy',
+
+ format_expect = function(self, expect)
+ return ' truthy, '
+ end,
+ }
+
+ matchers.have_type = Matcher {
+ function(self, actual, expected)
+ return type(actual) == expected or (getmetatable(actual) or {})._type == expected
+ end,
+
+ actual = 'type',
+
+ format_expect = function(self, expect)
+ local article = 'a'
+ if match(expect, '^[aehiou]') then
+ article = 'an'
+ end
+ return concat{' ', article, ' ', expect, ', '}
+ end
+ }
+end