diff --git a/contrib/lyaml/.gitignore b/contrib/lyaml/.gitignore new file mode 100644 index 000000000000..19d7cbb6dd7d --- /dev/null +++ b/contrib/lyaml/.gitignore @@ -0,0 +1,9 @@ +*~ +*.o +*.so +*.src.rock +/ChangeLog +/build-aux/config.ld +/luacov.*.out +/lyaml-*.tar.gz +/TAGS diff --git a/contrib/lyaml/.luacov b/contrib/lyaml/.luacov new file mode 100644 index 000000000000..0aa52d25e0ea --- /dev/null +++ b/contrib/lyaml/.luacov @@ -0,0 +1,8 @@ +modules = { + ['lyaml'] = 'lib/lyaml/init.lua', + ['lyaml.*'] = 'lib', +} + +runreport = true + +tick = true diff --git a/contrib/lyaml/AUTHORS b/contrib/lyaml/AUTHORS new file mode 100644 index 000000000000..1eb673371bf7 --- /dev/null +++ b/contrib/lyaml/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 diff --git a/contrib/lyaml/LICENSE b/contrib/lyaml/LICENSE new file mode 100644 index 000000000000..402e64dfec29 --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/NEWS.md b/contrib/lyaml/NEWS.md new file mode 100644 index 000000000000..32611d83671a --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/README.md b/contrib/lyaml/README.md new file mode 100644 index 000000000000..d1e96845ddfa --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/build-aux/config.ld.in b/contrib/lyaml/build-aux/config.ld.in new file mode 100644 index 000000000000..0d55b9e94c30 --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/build-aux/luke b/contrib/lyaml/build-aux/luke new file mode 100755 index 000000000000..3d61f784cbca --- /dev/null +++ b/contrib/lyaml/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 , 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 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 declares it. + For example, HP-UX 11i declares gettimeofday. */ +#define %s innocuous_%s + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with declaration below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#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)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=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 an 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/contrib/lyaml/doc/index.html b/contrib/lyaml/doc/index.html new file mode 100644 index 000000000000..bf7a957f51bd --- /dev/null +++ b/contrib/lyaml/doc/index.html @@ -0,0 +1,97 @@ + + + + + lyaml 6.2.8 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + +

+

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.

+ + + +

Modules

+ + + + + + + + + + + + + + + + + +
lyaml + +
lyaml.explicit + +
lyaml.functional + +
lyaml.implicit + +
+ +
+
+
+generated by LDoc 1.4.6 +Last updated 2022-10-22 17:12:03 +
+
+ + diff --git a/contrib/lyaml/doc/ldoc.css b/contrib/lyaml/doc/ldoc.css new file mode 100644 index 000000000000..52c4ad2bd8a2 --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/doc/modules/lyaml.explicit.html b/contrib/lyaml/doc/modules/lyaml.explicit.html new file mode 100644 index 000000000000..2667c4e006b2 --- /dev/null +++ b/contrib/lyaml/doc/modules/lyaml.explicit.html @@ -0,0 +1,267 @@ + + + + + lyaml 6.2.8 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module lyaml.explicit

+

+ +

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + +
bool (value)Parse the value following an explicit !!bool tag.
float (value)Parse the value following an explicit !!float tag.
int (value)Parse the value following an explicit !!int tag.
null ()Parse an explicit !!null tag.
str (value)Parse the value following an explicit !!str tag.
+ +
+
+ + +

Functions

+ +
+
+ + bool (value) +
+
+ Parse the value following an explicit !!bool tag. + + +

Parameters:

+
    +
  • value + token +
  • +
+ +

Returns:

+
    + + bool + boolean equivalent, if a valid value was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_bool = explicit.bool(tagarg)
    +
+ +
+
+ + float (value) +
+
+ Parse the value following an explicit !!float tag. + + +

Parameters:

+
    +
  • value + token +
  • +
+ +

Returns:

+
    + + number + float equivalent, if a valid value was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_float = explicit.float(tagarg)
    +
+ +
+
+ + int (value) +
+
+ Parse the value following an explicit !!int tag. + + +

Parameters:

+
    +
  • value + token +
  • +
+ +

Returns:

+
    + + int + integer equivalent, if a valid value was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_int = explicit.int(tagarg)
    +
+ +
+
+ + null () +
+
+ Parse an explicit !!null tag. + + + +

Returns:

+
    + + lyaml.null + + + +
+ + + +

Usage:

+
    +
    null = explicit.null(tagarg)
    +
+ +
+
+ + str (value) +
+
+ Parse the value following an explicit !!str tag. + + +

Parameters:

+
    +
  • value + string + token +
  • +
+ +

Returns:

+
    + + string + value which was a string already +
+ + + +

Usage:

+
    +
    tagarg = explicit.str(tagarg)
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2022-10-22 17:12:03 +
+
+ + diff --git a/contrib/lyaml/doc/modules/lyaml.functional.html b/contrib/lyaml/doc/modules/lyaml.functional.html new file mode 100644 index 000000000000..b003a961ff28 --- /dev/null +++ b/contrib/lyaml/doc/modules/lyaml.functional.html @@ -0,0 +1,236 @@ + + + + + lyaml 6.2.8 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module lyaml.functional

+

+ +

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + + + + + +
isnull (x)lyaml.null predicate.
iscallable (x)Callable predicate.
anyof (fns)Compose a function to try each callable with supplied args.
id (...)Return arguments unchanged.
+

Tables

+ + + + + +
NULLlyaml.null value.
+ +
+
+ + +

Functions

+ +
+
+ + isnull (x) +
+
+ lyaml.null predicate. + + +

Parameters:

+
    +
  • x + operand +
  • +
+ +

Returns:

+
    + + bool + true if x is lyaml.null. +
+ + + + +
+
+ + iscallable (x) +
+
+ Callable predicate. + + +

Parameters:

+
    +
  • x + operand +
  • +
+ +

Returns:

+
    + + bool + true if x is a function has a __call metamethod +
+ + + +

Usage:

+
    +
    r = iscallable(x) and x(...)
    +
+ +
+
+ + anyof (fns) +
+
+ Compose a function to try each callable with supplied args. + + +

Parameters:

+
    +
  • fns + table + list of functions to try +
  • +
+ +

Returns:

+
    + + function + +

    a new function to call ... functions, stopping

    +
    and returning the first non-nil result, if any
    +
    + +
+ + + + +
+
+ + id (...) +
+
+ Return arguments unchanged. + + +

Parameters:

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

Returns:

+
    + + ... +
+ + + + +
+
+

Tables

+ +
+
+ + NULL +
+
+ lyaml.null value. + + + + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2022-10-22 17:12:03 +
+
+ + diff --git a/contrib/lyaml/doc/modules/lyaml.html b/contrib/lyaml/doc/modules/lyaml.html new file mode 100644 index 000000000000..2deb6a4b6be7 --- /dev/null +++ b/contrib/lyaml/doc/modules/lyaml.html @@ -0,0 +1,224 @@ + + + + + lyaml 6.2.8 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module lyaml

+

+ +

+

+ +

+ + +

Functions

+ + + + + + + + + +
dump (documents[, opts])Dump a list of Lua tables to an equivalent YAML stream.
load (s[, opts])Load a YAML stream into a Lua table.
+

Tables

+ + + + + + + + + +
dumper_optsDump options table.
loader_optsLoad options table.
+ +
+
+ + +

Functions

+ +
+
+ + dump (documents[, opts]) +
+
+ Dump a list of Lua tables to an equivalent YAML stream. + + +

Parameters:

+
    +
  • documents + table + a sequence of Lua tables. +
  • +
  • opts + dumper_opts + initialisation options + (optional) +
  • +
+ +

Returns:

+
    + + string + equivalest YAML stream +
+ + + + +
+
+ + load (s[, opts]) +
+
+ Load a YAML stream into a Lua table. + + +

Parameters:

+
    +
  • s + string + YAML stream +
  • +
  • opts + loader_opts + initialisation options + (optional) +
  • +
+ +

Returns:

+
    + + table + Lua table equivalent of stream s +
+ + + + +
+
+

Tables

+ +
+
+ + dumper_opts +
+
+ Dump options table. + + +

Fields:

+
    +
  • anchors + table + map initial anchor names to values +
  • +
  • implicit_scalar + function + parse implicit scalar values +
  • +
+ + + + + +
+
+ + loader_opts +
+
+ Load options table. + + +

Fields:

+
    +
  • all + boolean + load all documents from the stream +
  • +
  • explicit_scalar + table + map full tag-names to parser functions +
  • +
  • implicit_scalar + function + parse implicit scalar values +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2022-10-22 17:12:03 +
+
+ + diff --git a/contrib/lyaml/doc/modules/lyaml.implicit.html b/contrib/lyaml/doc/modules/lyaml.implicit.html new file mode 100644 index 000000000000..935579f705c6 --- /dev/null +++ b/contrib/lyaml/doc/modules/lyaml.implicit.html @@ -0,0 +1,533 @@ + + + + + lyaml 6.2.8 Reference + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module lyaml.implicit

+

+ +

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
null (value)Parse a null token to a null value.
bool (value)Parse a boolean token to the equivalent value.
binary (value)Parse a binary token, such as '0b1010_0111_0100_1010_1110'.
octal (value)Parse an octal token, such as '012345'.
decimal (value)Parse a decimal token, such as '0' or '12345'.
hexadecimal (value)Parse a hexadecimal token, such as '0xdeadbeef'.
sexagesimal (value)Parse a sexagesimal token, such as '190:20:30'.
nan (value)Parse a nan token.
inf (value)Parse a signed inf token.
float (value)Parse a floating point number token, such as '1e-3' or '-0.12'.
sexfloat (value)Parse a sexagesimal float, such as '190:20:30.15'.
+ +
+
+ + +

Functions

+ +
+
+ + null (value) +
+
+ Parse a null token to a null value. + + +

Parameters:

+
    +
  • value + token +
  • +
+ +

Returns:

+
    + + lyaml.null, for an empty string or literal ~ +
+

Or

+
    + + nil otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_null = implicit.null(token)
    +
+ +
+
+ + bool (value) +
+
+ 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. + + +

Parameters:

+
    +
  • value + token +
  • +
+ +

Returns:

+
    + + bool + if a valid boolean token was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_bool = implicit.bool(token)
    +
+ +
+
+ + binary (value) +
+
+ Parse a binary token, such as '0b1010_0111_0100_1010_1110'. + + +

Parameters:

+
    +
  • value + string + token +
  • +
+ +

Returns:

+
    + + int + integer equivalent, if a valid token was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_int = implicit.binary(value)
    +
+ +
+
+ + octal (value) +
+
+ Parse an octal token, such as '012345'. + + +

Parameters:

+
    +
  • value + string + token +
  • +
+ +

Returns:

+
    + + int + integer equivalent, if a valid token was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_int = implicit.octal(value)
    +
+ +
+
+ + decimal (value) +
+
+ Parse a decimal token, such as '0' or '12345'. + + +

Parameters:

+
    +
  • value + string + token +
  • +
+ +

Returns:

+
    + + int + integer equivalent, if a valid token was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_int = implicit.decimal(value)
    +
+ +
+
+ + hexadecimal (value) +
+
+ Parse a hexadecimal token, such as '0xdeadbeef'. + + +

Parameters:

+
    +
  • value + string + token +
  • +
+ +

Returns:

+
    + + int + integer equivalent, if a valid token was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_int = implicit.hexadecimal(value)
    +
+ +
+
+ + sexagesimal (value) +
+
+ Parse a sexagesimal token, such as '190:20:30'. + Useful for times and angles. + + +

Parameters:

+
    +
  • value + string + token +
  • +
+ +

Returns:

+
    + + int + integer equivalent, if a valid token was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_int = implicit.sexagesimal(value)
    +
+ +
+
+ + nan (value) +
+
+ Parse a nan token. + + +

Parameters:

+
    +
  • value + string + token +
  • +
+ +

Returns:

+
    + + nan + not-a-number, if a valid token was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_nan = implicit.nan(value)
    +
+ +
+
+ + inf (value) +
+
+ Parse a signed inf token. + + +

Parameters:

+
    +
  • value + string + token +
  • +
+ +

Returns:

+
    + + number + plus/minus-infinity, if a valid token was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_inf = implicit.inf(value)
    +
+ +
+
+ + float (value) +
+
+ Parse a floating point number token, such as '1e-3' or '-0.12'. + + +

Parameters:

+
    +
  • value + string + token +
  • +
+ +

Returns:

+
    + + number + float equivalent, if a valid token was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_float = implicit.float(value)
    +
+ +
+
+ + sexfloat (value) +
+
+ Parse a sexagesimal float, such as '190:20:30.15'. + Useful for times and angles. + + +

Parameters:

+
    +
  • value + string + token +
  • +
+ +

Returns:

+
    + + number + float equivalent, if a valid token was recognized +
+

Or

+
    + + nil + otherwise, nil +
+ + + +

Usage:

+
    +
    maybe_float = implicit.sexfloat(value)
    +
+ +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2022-10-22 17:12:03 +
+
+ + diff --git a/contrib/lyaml/ext/yaml/emitter.c b/contrib/lyaml/ext/yaml/emitter.c new file mode 100644 index 000000000000..b71fd14f4c1d --- /dev/null +++ b/contrib/lyaml/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 + +#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/contrib/lyaml/ext/yaml/lyaml.h b/contrib/lyaml/ext/yaml/lyaml.h new file mode 100644 index 000000000000..9892a48ea4c4 --- /dev/null +++ b/contrib/lyaml/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 + +#include +#include + +#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/contrib/lyaml/ext/yaml/parser.c b/contrib/lyaml/ext/yaml/parser.c new file mode 100644 index 000000000000..3136abd49045 --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/ext/yaml/scanner.c b/contrib/lyaml/ext/yaml/scanner.c new file mode 100644 index 000000000000..6d7276517f42 --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/ext/yaml/yaml.c b/contrib/lyaml/ext/yaml/yaml.c new file mode 100644 index 000000000000..54478610134f --- /dev/null +++ b/contrib/lyaml/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 + * + */ + +#include +#include + +#include + +#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/contrib/lyaml/lib/lyaml/explicit.lua b/contrib/lyaml/lib/lyaml/explicit.lua new file mode 100644 index 000000000000..98a38331ead6 --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/lib/lyaml/functional.lua b/contrib/lyaml/lib/lyaml/functional.lua new file mode 100644 index 000000000000..556e9489505a --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/lib/lyaml/implicit.lua b/contrib/lyaml/lib/lyaml/implicit.lua new file mode 100644 index 000000000000..fe58025b560d --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/lib/lyaml/init.lua b/contrib/lyaml/lib/lyaml/init.lua new file mode 100644 index 000000000000..95e4036ea7c9 --- /dev/null +++ b/contrib/lyaml/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 + +--- @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/contrib/lyaml/lukefile b/contrib/lyaml/lukefile new file mode 100644 index 000000000000..318281fd78e7 --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/lyaml-6.2.8-1.rockspec b/contrib/lyaml/lyaml-6.2.8-1.rockspec new file mode 100644 index 000000000000..dce9e45a56cf --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/spec/ext_yaml_emitter_spec.yaml b/contrib/lyaml/spec/ext_yaml_emitter_spec.yaml new file mode 100644 index 000000000000..385d58f7de3c --- /dev/null +++ b/contrib/lyaml/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 "! 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/contrib/lyaml/spec/ext_yaml_parser_spec.yaml b/contrib/lyaml/spec/ext_yaml_parser_spec.yaml new file mode 100644 index 000000000000..2438c42188e0 --- /dev/null +++ b/contrib/lyaml/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 " + - 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 " + +- 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/contrib/lyaml/spec/ext_yaml_scanner_spec.yaml b/contrib/lyaml/spec/ext_yaml_scanner_spec.yaml new file mode 100644 index 000000000000..4d8e633d2cc2 --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/spec/lib_lyaml_functional_spec.yaml b/contrib/lyaml/spec/lib_lyaml_functional_spec.yaml new file mode 100644 index 000000000000..cfd8676f0176 --- /dev/null +++ b/contrib/lyaml/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/contrib/lyaml/spec/lib_lyaml_spec.yaml b/contrib/lyaml/spec/lib_lyaml_spec.yaml new file mode 100644 index 000000000000..f6c7e1ef4bed --- /dev/null +++ b/contrib/lyaml/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 " + 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 " + - 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/contrib/lyaml/spec/spec_helper.lua b/contrib/lyaml/spec/spec_helper.lua new file mode 100644 index 000000000000..1ba7a3a7559f --- /dev/null +++ b/contrib/lyaml/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 diff --git a/libexec/flua/Makefile b/libexec/flua/Makefile index 4b4b2c94eca4..a788bb57dcd7 100644 --- a/libexec/flua/Makefile +++ b/libexec/flua/Makefile @@ -1,40 +1,41 @@ .include SUBDIR+= libfreebsd SUBDIR+= libhash SUBDIR+= libjail SUBDIR+= libucl +SUBDIR+= liblyaml LUASRC?= ${SRCTOP}/contrib/lua/src .PATH: ${LUASRC} PROG= flua WARNS?= 3 MAN= # No manpage; this is internal. CWARNFLAGS.gcc+= -Wno-format-nonliteral LIBADD= lua # Entry point SRCS+= lua.c # FreeBSD Extensions .PATH: ${.CURDIR}/modules SRCS+= linit_flua.c SRCS+= lfs.c lposix.c CFLAGS+= -I${SRCTOP}/lib/liblua -I${.CURDIR}/modules -I${LUASRC} CFLAGS+= -DLUA_PROGNAME="\"${PROG}\"" # readline bits; these aren't needed if we're building a bootstrap flua, as we # don't expect that one to see any REPL usage. .if !defined(BOOTSTRAPPING) CFLAGS+= -DLUA_USE_READLINE CFLAGS+= -I${SRCTOP}/lib/libedit -I${SRCTOP}/contrib/libedit LIBADD+= edit LDFLAGS+= -Wl,-E .endif .include diff --git a/libexec/flua/liblyaml/Makefile b/libexec/flua/liblyaml/Makefile new file mode 100644 index 000000000000..e7a89d09bb9e --- /dev/null +++ b/libexec/flua/liblyaml/Makefile @@ -0,0 +1,22 @@ +SHLIB_NAME= yaml.so + +WARNS= 1 +LYAMLSRC?= ${SRCTOP}/contrib/lyaml +.PATH: ${LYAMLSRC}/ext/yaml ${LYAMLSRC}/lib/lyaml +SRCS= emitter.c \ + parser.c \ + scanner.c \ + yaml.c +CFLAGS+= \ + -I${LYAMLSRC}/ext/yaml \ + -I${SRCTOP}/contrib/libyaml/include \ + -DVERSION=\"6.2.8\" +LIBADD+= yaml + +FILES= explicit.lua \ + functional.lua \ + implicit.lua \ + init.lua +FILESDIR= ${SHAREDIR}/flua/lyaml + +.include