pax_global_header00006660000000000000000000000064152660040400014507gustar00rootroot0000000000000052 comment=26689671f631577ddea7f5ebd34a4926d7c1c6de gvvaughan-lyaml-f68df96/000077500000000000000000000000001526600404000152475ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/.github/000077500000000000000000000000001526600404000166075ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/.github/workflows/000077500000000000000000000000001526600404000206445ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/.github/workflows/spec.yml000066400000000000000000000031761526600404000223300ustar00rootroot00000000000000name: spec on: push: branches: [ '*' ] pull_request: branches: [ 'master' ] jobs: test: strategy: fail-fast: false matrix: lua-version: ["5.5", "5.4", "5.3", "5.2", "5.1", "luajit"] runs-on: ubuntu-latest # id-token lets codecov-action authenticate uploads with OIDC. permissions: contents: read id-token: write steps: - uses: actions/checkout@v7 - uses: luarocks/gh-actions-lua@v14 with: luaVersion: ${{ matrix.lua-version }} - uses: luarocks/gh-actions-luarocks@v7 - name: install run: | sudo apt-get update -q -q --yes sudo apt-get install -y libyaml-dev # LuaRocks 3.13.0 crashes writing the namespace of a namespaced # dependency on Lua 5.1 (luarocks/luarocks#1831), so install # gvvaughan/ldoc by hand, before anything else needs an ldoc. luarocks install --server=https://luarocks.org/manifests/gvvaughan ldoc printf gvvaughan > "$(luarocks show --rock-dir ldoc)/rock_namespace" luarocks test --prepare - name: build run: | luarocks make --force - name: test run: | luarocks test - uses: codecov/codecov-action@v7 with: # Pull requests from forks get no OIDC token, but Codecov accepts # tokenless uploads from forks of public repositories. use_oidc: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) }} files: luacov.report.out flags: lua-${{ matrix.lua-version }} fail_ci_if_error: true gvvaughan-lyaml-f68df96/.gitignore000066400000000000000000000001521526600404000172350ustar00rootroot00000000000000*~ *.o *.so *.src.rock /ChangeLog !/doc/lyaml.md /build-aux/config.ld /luacov.*.out /lyaml-*.tar.gz /TAGS gvvaughan-lyaml-f68df96/.luacov000066400000000000000000000001521526600404000165370ustar00rootroot00000000000000modules = { ['lyaml'] = 'lib/lyaml/init.lua', ['lyaml.*'] = 'lib', } runreport = true tick = true gvvaughan-lyaml-f68df96/AUTHORS000066400000000000000000000002411526600404000163140ustar00rootroot00000000000000lyaml is the work of several authors (see git history for contributors). There is an earlier C LibYAML binding Copyright Andrew Danforth gvvaughan-lyaml-f68df96/LICENSE000066400000000000000000000026521526600404000162610ustar00rootroot00000000000000This 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-2026 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. ==================================================================== gvvaughan-lyaml-f68df96/NEWS.md000066400000000000000000000230211526600404000163430ustar00rootroot00000000000000# lyaml NEWS - User visible changes ## Noteworthy changes in release 6.2.10 (2026-10-20) [stable] ### New Features - LYAML has a manual, `doc/lyaml.md`, which is now the home page at . The LDoc reference has moved to . ## Noteworthy changes in release 6.2.9 (2026-07-20) [stable] ### New Features - Initial support for Lua 5.5. ### Bugs Fixed - `luke` now uses the same cpp defines for conftests as will be used for module compilation, otherwise we are potestially testing and compiling with different sets of features. ## Noteworthy changes in release 6.2.8 (2022-10-22) [stable] ### Bugs Fixed - `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] ### Bugs Fixed - Don't skip YAML entries from mixed key Lua tables. ## Noteworthy changes in release 6.2.6 (2020-08-28) [stable] ### Bugs Fixed - `luke` really propagates `LDFLAGS` to module compilation commands. ## Noteworthy changes in release 6.2.5 (2020-04-15) [stable] ### Bugs Fixed - `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] ### Bugs Fixed - `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] ### Bugs Fixed - Remove spurious dependency on `std.normalize` and `std._debug` libraries. ## Noteworthy changes in release 6.2.1 (2018-02-20) [stable] ### Bugs Fixed - `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] ### Bugs Fixed - `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] ### Bugs Fixed - `luke` no longer bombs out with a nil concat error. ## Noteworthy changes in release 6.1.2 (2017-04-30) [stable] ### Bugs Fixed - `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] ### Bugs Fixed - `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 ``` ### Bugs Fixed - 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! gvvaughan-lyaml-f68df96/README.md000066400000000000000000000065001526600404000165270ustar00rootroot00000000000000LYAML ===== Copyright (C) 2013-2026 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.10)](https://github.com/gvvaughan/lyaml/actions) [![codecov.io](https://codecov.io/github/gvvaughan/lyaml/coverage.svg?branch=release-v6.2.10)](https://codecov.io/github/gvvaughan/lyaml?branch=release-v6.2.10) [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. 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 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 https://raw.githubusercontent.com/gvvaughan/lyaml/master/lyaml-dev-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 YAML_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][L19]. Documentation ------------- LYAML includes [comprehensive documentation][github.io], which is written in [doc/lyaml.md][manual]. 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. [github.io]: https://gvvaughan.github.io/lyaml [issues]: https://github.com/gvvaughan/lyaml/issues [libyaml]: https://pyyaml.org/wiki/LibYAML [lua]: https://www.lua.org [luarocks]: https://luarocks.org [lyaml]: https://github.com/gvvaughan/lyaml [manual]: doc/lyaml.md [L19]: https://github.com/gvvaughan/lyaml/blob/master/lyaml-dev-1.rockspec#L19 [yaml]: https://yaml.org [yaml11]: https://yaml.org/spec/1.1/ gvvaughan-lyaml-f68df96/build-aux/000077500000000000000000000000001526600404000171415ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/build-aux/config.ld.in000066400000000000000000000014641526600404000213410ustar00rootroot00000000000000--[[ LYAML binding for Lua 5.1, 5.2, 5.3, 5.4 & 5.5 Copyright (C) 2013-2026 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, 5.4 and 5.5 ## 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/api' file = { '../lib/lyaml/init.lua', '../lib/lyaml/explicit.lua', '../lib/lyaml/functional.lua', '../lib/lyaml/implicit.lua', } format = 'markdown' backtick_references = false sort = false gvvaughan-lyaml-f68df96/build-aux/luke000077500000000000000000002006361526600404000200360ustar00rootroot00000000000000#!/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'luke.normalize'{}local function fatal(...)local msg=(...)if select('#',...)>1 then msg=format(...)end stdout:flush()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'luke.normalize'{'luke._base','luke.compile','luke.lukefile','luke.platforms','luke.functional','luke.version','type.context-manager',}local function version()print(versiontext)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 --dry-run print the commands, without running them --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 release cut a release of the version given by version=VERSION If no TARGET is given, 'all' is implied. Report bugs to https://github.com/gvvaughan/luke/issues.]]exit(0)end local progname=arg[0]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 '"..progname.." --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 local function gitref()local out=slurp(Pipe('git rev-parse --short HEAD 2>/dev/null'))return match(out or'','^(%x+)\n?$')or''end local function abs_top_srcdir()return(gsub(slurp(Pipe('pwd'))or'','\n$',''))end return{parse_arguments=function(args)local r={clidefs={},debug=false,dryrun=false,valreqs={},fname='lukefile',install={},log=nop,spawn=spawn,targets={},verbose=nop,write=display,}map(args,function(opt)case(opt,{['--debug']=function()r.debug=true r.log=dump end,['--dry-run']=function()r.dryrun=true r.spawn=function()return 0,'',''end 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 map({'package','version'},function(key)diagnose(luke[key]~=nil,"no %s in '%s'",key,parsed.fname)end)local release if contains(parsed.targets,'release')then if len(parsed.targets)>1 then fatal('release cannot be combined with other targets')end release=parsed.clidefs.version diagnose(release~=nil,'release needs a version=VERSION argument')case(release,{['%d+']=nop,['%d+%.%d+']=nop,['%d+%.%d+%.%d+']=nop,function()fatal("bad release version '%s': X, X.Y or X.Y.Z expected",release)end,})parsed.targets={}end local sections=script_sections(luke)local installbin=sections.install local rulesets={luke.modules,sections.bin,sections.files,installbin}local function hasrule(target)return target=='install'or any(map(rulesets,function(rules)return ordered_get(rules,target)~=nil end))end local targets=call(function()if isempty(parsed.targets)or contains(parsed.targets,'all')then return except(flatten(parsed.targets,map(rulesets,ordered_keys)),'all')end local unknown=except(parsed.targets,hasrule)diagnose(isempty(unknown),"no rule to make target '%s'",unknown[1])assert(len(parsed.targets)>0,"no build targets specified")return parsed.targets end)local named=except(targets,'install')local build=ordered_pluck(named,luke.modules)local scripts={build=reduce(SCRIPT_SECTIONS,list(),function(entries,section)return reduce(ordered_keys(ordered_pluck(named,sections[section])),entries,function(entries,name)return append(entries,{name=name,section=section})end)end),install={},}local install if contains(targets,'install')then if isempty(named)then install,scripts.install=luke.modules,ordered_keys(installbin)else install,scripts.install=build,ordered_keys(ordered_pluck(named,installbin))end end luke.modules=build local ref,srcdir=gitref(),abs_top_srcdir()luke.substitute=merge(luke.substitute or{},{abs_top_srcdir=srcdir,gitref=ref,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',abs_top_srcdir=srcdir,gitref=ref,objdir=platforms[1],package=luke.package,version=luke.version,})return{clidefs=parsed.clidefs,debug=parsed.debug,dryrun=parsed.dryrun,filled={},install=install,log=parsed.log,luke=luke,release=release,scripts=scripts,spawn=parsed.spawn,valreqs=parsed.valreqs,verbose=parsed.verbose,write=parsed.write,}end,} end package.preload['luke.compile']=function() local _ENV=require'luke.normalize'{'luke._base','luke.environment','luke.functional','type.context-manager','type.path',}local function spawn(env,...)local command=interpolate(env,concat({...},' '))return with(TmpFile(),TmpFile(),function(out,err)local pipe=concat{'{ ',command,'\n} >',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=L.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(...)return map(ordered_merge(...),function(definition)local name,value=destructure(definition)local fmt=cond({[int(value)==1]='-D%s'},{[true]='-D%s=%s'})return format(fmt,name,shellword(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 script_target(name,rules,objdir,installed)if isstring(rules)then return(gsub(rules,'%.in$',''))elseif installed then return format('%s/%s',objdir,name)end return name end local function c_sources(sources)return dropuntil(sources,bind(match,{[2]='%.[ch]$'}))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=ordered_get(luke.modules,name)local c_module=c_module_path(luke.variables.objdir,name)local command={'$MAKEDIRS',dirname(c_module)}local status,err,out=L.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(luke.defines,rules.defines),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(ordered_keys(modules),function(name)return dropuntil(ordered_get(modules,name).sources,bind(match,{[2]='%.[ch]$'}))end)end,defines=defines,incdirs=incdirs,install_modules=function(L,env,luke,modules)return reduce(ordered_keys(modules),0,function(status,name)if status==0 then local src,dir=module_to_path(name,ordered_get(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,build_program=function(L,env,luke,rules,target,sources)if match(target,'/')then local command={'$MAKEDIRS',dirname(target)}local status,err=L.spawn(env,unpack(command))if status~=0 then stdout:write(concat(command,' ')..'\n')stderr:write(err..'\n')return status end end if rules.command~=nil then local names=sorted(keys(rules.env))env=mkenv({sources=concat(sources,' '),target=target},rules.env,env)local exports={}if not isempty(names)then exports=flatten('export',map(names,function(name)return format('%s=%s',name,shellword(interpolate(env,rules.env[name])))end),';')end return run(L,env,flatten(exports,rules.command))end if c_sources(sources)==nil then fatal("no command to build '%s' from %s",target,concat(sources,' '))end return run(L,env,flatten('$CC $CFLAGS $CPPFLAGS',defines(luke.defines,rules.defines),incdirs(rules.incdirs,luke.incdirs),sources,'-o',target,'$LDFLAGS',libdirs(rules.libdirs,luke.libdirs),'$LIBS',rules.libraries,luke.libraries))end,c_program=function(rules)return istable(rules)and rules.command==nil and c_sources(rules.sources)~=nil end,install_scripts=function(L,env,luke,names)if isempty(names)then return 0 end local status=0 if not exists(interpolate(env,'$INST_BINDIR'))then status=run(L,env,{'$MAKEDIRS','$INST_BINDIR'})end return reduce(names,status,function(status,name)local rules=ordered_get(luke.install.bin,name)local src=script_target(name,rules,luke.variables.objdir,true)local dest='$INST_BINDIR/'..name if status==0 then status=run(L,env,{'$INSTALL',src,dest})end if status==0 then status=run(L,env,{'$CHMOD','+x',dest})end return status end)end,libdirs=libdirs,make_executable=function(L,env,path)return run(L,env,{'$CHMOD','+x',path})end,run_command=run,script_target=script_target,spawn=spawn,} end package.preload['luke.configure']=function() local _ENV=require'luke.normalize'{'luke._base','luke.compile','luke.environment','luke.functional','type.context-manager','type.dict',CCPROGS={'cc','gcc','clang'},}local function logspawn(L,env,filename,...)local status,err=spawn(env,...)if status~=0 and err~=''then with(File(filename,'r'),function(h)L.log(h.filename..':')L.log(slurp(h))end)L.log(err)end return status end local function Conftest(L)local cm=CTest(L.debug)if L.debug and cm~=nil then L.log('keeping '..cm.filename..' and '..cm.objfile)end return cm 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,objfile,filename)local command=flatten('$CC','-c','$CFLAGS',defines(L.luke.defines),incdirs(config.incdir),'$CPPFLAGS','-o',objfile,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',defines(L.luke.defines),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(Conftest(L),function(conftest)conftest:write(format('%s\n#include "%s"\n',extra_hdrs,header))conftest:flush()return logspawn(L,env,conftest.filename,compile_command(L,env,config,conftest.objfile,conftest.filename))end)end local function check_struct_member_compile(L,env,config,structname,member,extra_hdrs)return with(Conftest(L),function(conftest)conftest:write(format([[ %s int main () { static %s aggr; if (sizeof aggr.%s) return 0; return 0; } ]],extra_hdrs,structname,member))conftest:flush()return logspawn(L,env,conftest.filename,compile_command(L,env,config,conftest.objfile,conftest.filename))end)end local function try_link(L,env,config,lib,symbol)return with(Conftest(L),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))conftest:flush()return logspawn(L,env,conftest.filename,link_command(L,env,config,a_out.filename,conftest.filename,lib))end)end local function try_compile(L,env,config,headers)return with(Conftest(L),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))conftest:flush()return logspawn(L,env,conftest.filename,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(Conftest(L),function(conftest)conftest:write(format([[ %s int main() { #ifndef %s (void) %s; #endif return 0; } ]],extra_hdrs,fname,fname))conftest:flush()return logspawn(L,env,conftest.filename,compile_command(L,env,config,conftest.objfile,conftest.filename))end)end local function check_func_link(L,env,config,fname)return with(Conftest(L),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))conftest:flush()return logspawn(L,env,conftest.filename,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=mkenv(env,{CC=CC})end checking(L,interpolate(env,'whether $CC works'))local cm=Conftest(L)local works,err=with(cm,function(conftest)conftest:write('typedef int x;\n')conftest:flush()return spawn(env,'$compile','-o',conftest.objfile,conftest.filename)end)if works~=0 then L.verbose'no\n'L.log(interpolate(env,'$compile -o '..cm.objfile..' '..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=mkenv(env,{LDOC=LDOC})end return env end,configure=configure,} end package.preload['luke.environment']=function() local _ENV=require'luke.normalize'{'luke.platforms','luke.functional',LUAVERSION=string.gsub(_VERSION,'[^0-9%.]+',''),SHELLMETACHARS='[%s%$"\'&;|<>()*?`\\]',}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{LUA='lua',LUAVERSION=LUAVERSION,PREFIX='/usr/local',INST_BINDIR='$PREFIX/bin',INST_LIBDIR='$PREFIX/lib/lua/$LUAVERSION',INST_LUADIR='$PREFIX/share/lua/$LUAVERSION',LIB_EXTENSION='so',OBJ_EXTENSION='o',CHMOD='chmod',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,{'@([%w_]+)@'}),interpolate=bind(interpolate_with,{'%$([%w_]+)'}),shellword=function(s)return choose(match(s,SHELLMETACHARS)~=nil,function()return"'"..gsub(s,"'",[['\'']]).."'"end,s)end,mkenv=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.functional']=function() local _ENV=require'luke.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,choose=function(test,consequent,alternative)local leg=alternative if test then leg=consequent end if leg~=nil then return call(leg,test)end end,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 s~=nil and 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)if type(x)~='table'then return false end local k=next(x)return k==nil or(k=='n'and x.n==0 and next(x,k)==nil)end,isfile=isfile,isfunction=function(x)return type(x)=='function'end,islistkey=function(t,k)return type(k)=='number'and int(k)==k and k>=1 and k<=len(t)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=keys,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,ordered_merge=function(...)local at={}return reduce(except(list(...),nil),{},function(r,pairs)reduce(pairs,r,function(r,pair)local k,v=destructure(pair)at[k]=at[k]or#r+1 r[at[k]]={[k]=v}end)end)end,ordered_get=function(om,key)return reduce(om or{},nil,function(_,pair)return pair[key]end)end,ordered_keys=function(om)return reduce(om or{},list(),function(r,pair)return append(r,(destructure(pair)))end)end,ordered_mapvalues=function(om,fn)return reduce(om or{},{},function(r,pair)local k,v=destructure(pair)r[#r+1]={[k]=fn(v)}end)end,ordered_pluck=function(keylist,om)local wanted=reduce(keylist,{},function(r,k)r[k]=true end)return except(om or{},function(pair)return not wanted[(destructure(pair))]end)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=reduce,reversed=function(seq)return reduce(seq,{},function(r,v)return{v,unpack(r)}end)end,sorted_keys=function(iterable)local rank={number=1,string=2}local r=keys(iterable)sort(r,function(a,b)local ra,rb=rank[type(a)]or 3,rank[type(b)]or 3 return ran 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['luke.platforms']=function() local _ENV=require'luke.normalize'{'luke.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 selected(platforms,supported,isplatform)local matches=map(filter(supported,bind(get,{platforms})),bind(get,{platforms}))return choose(isempty(matches),function()return{pluck(except(keys(platforms),isplatform),platforms)}end,function()return matches end)end local function layers(t,using,predicate)local supported,isplatform=using or supported,predicate or isplatform local own=pluck(except(keys(t),'platforms'),t)or{}return{own,unpack(reversed(selected(t.platforms or{},supported,isplatform)))}end local function filter_platforms(t,using,predicate,opaque)local supported,isplatform,opaque=using or supported,predicate or isplatform,opaque or{}local function narrow(v,k)return choose(opaque[k]or not istable(v),function()return v end,function()return filter_platforms(v,supported,isplatform,opaque)end)end local tables=map(layers(t,supported,isplatform),function(layer)return layer.platforms and narrow(layer)or layer end)local r=reduce(tables,{},function(r,layer)map(layer,function(v)r[len(r)+1]=narrow(v)end)end)return reduce(reversed(tables),r,function(r,layer)map(filter(keys(layer),function(k)return not islistkey(layer,k)and r[k]==nil end),function(k)r[k]=narrow(layer[k],k)end)end)end return{filter_platforms=filter_platforms,isplatform=isplatform,layers=layers,platforms=supported,toplatforms=toplatforms,} end package.preload['luke.release']=function() local _ENV=require'luke.normalize'{'luke._base','luke.compile','luke.configure','luke.environment','luke.functional','luke.lukefile','type.context-manager','type.path',date=os.date,}local PLACEHOLDER='\n(## [^\n]*%?%.%?[^\n]*\n)'local MODREV="^(local _MODREV, _SPECREV = ')[^']*(')"local DEVROCKSPEC='^(.-%-)dev(%-%d+%.rockspec)$'local function git(env,...)local status,err,out=spawn(env,'git',...)if status==0 then return(gsub(out,'\n$',''))end end local function dependencies_installed(env,rockspec)local status,err,out=spawn(env,env.LUAROCKS or'luarocks','--lua-version=$LUAVERSION','make --only-deps --only-server=',shellword(rockspec))if status~=0 then stderr:write(match(out or'','^(Missing dependencies.-)\n\n')or err or'','\n')end return status==0 end local function has_placeholder(text)local header,body=match('\n'..text,'\n(## [^\n]*\n)(.*)')if header==nil or match('\n'..header,PLACEHOLDER)==nil then return false end return match(match(body,'^(.-)\n## ')or body,'%S')~=nil end local function add_release_header(text,version,date)local header=format('## Noteworthy changes in release %s (%s) [stable]',version,date)return(gsub(text,PLACEHOLDER..'\n*','\n%1\n\n'..header..'\n\n',1))end local function release_news(text,version)local header='\n(## Noteworthy changes in release '..gsub(version,'%p','%%%0')..' %(.*)'local news=match('\n'..text,header)if news~=nil then return(gsub(match(news,'^(.-)\n## ')or news,'%s*$',''))end end local function rockspec_fields(text)local r={}local chunk=text and loadstring(text,'rockspec',r)if chunk==nil or not pcall(chunk)then return{}end return r end local function announcement(package,version,news,rockspec)local fields=rockspec_fields(rockspec)local description=istable(fields.description)and fields.description or{}package=isstring(fields.package)and fields.package or package local lines=flatten(isstring(description.summary)and{description.summary,''}or{},format('I am happy to announce release %s of %s.',version,package),'',isstring(description.homepage)and{format("%s's home page is at %s",package,description.homepage),''}or{},news and{news,''}or{},rockspec and{'Install it with LuaRocks, using:','',format(' luarocks install %s %s',package,version)}or{})return package,match(concat(lines,'\n'),'^(.-)%s*$')..'\n'end local function remove_placeholder(text)return(gsub(text,PLACEHOLDER..'\n?','\n',1))end local function run(L,env,...)local command=concat({...},' ')L.write(interpolate(env,command),'\n')local status,err=L.spawn(env,command)if status~=0 then stderr:write(err,'\n')end return status end local function tag(L,env,version,notes)local filename=tmpname()with(File(filename,'w'),function(h)h:write(notes)end)local status=run(L,env,'git tag -a --cleanup=verbatim','v'..version,'-F',filename)rm(filename)return status end local function edit(L,path,fn,dest)dest=dest or path L.write((dest==path and'updating 'or'creating ')..dest,'\n')if not L.dryrun then local content=fn(slurp(File(path)))with(File(dest,'w'),function(h)h:write(content)end)end return 0 end local function replace(text,substitute,pairs)return reduce(pairs,text,function(r,pair)local find,replacement=destructure(pair)return(gsub(r,(gsub(find,'%p','%%%0')),(gsub(expand(substitute,replacement),'%%','%%%%'))))end)end local function rockspec_name(rockspec,version)return(gsub(rockspec,DEVROCKSPEC,'%1'..version..'%2'))end local function set_modrev(text,version)return(gsub(text,MODREV,'%1'..version..'%2',1))end local function copy_command(source,worktree,path,optional)local dest=worktree..'/'..path local from,to=shellword(source),shellword(dest)local copy=format("git -C %s rm -r --quiet --ignore-unmatch -- %s ':(exclude)*.in'"..' && if test -d %s; then $MAKEDIRS %s && cp -R %s/. %s; else $MAKEDIRS %s && cp %s %s; fi',shellword(worktree),shellword(path),from,to,from,to,shellword(dirname(dest)),from,to)return choose(optional,format('if test -e %s; then %s; fi',from,copy),copy)end local function publish_steps(L,env,substitute,worktree,copies)return flatten(map(copies,function(pair)local path,copy=destructure(pair)local source,optional=copy.source,copy.optional local dest=worktree..'/'..path local template=match(source,'%.in$')~=nil return except(list(choose(optional or not template,function()return bind(run,{L,env,copy_command(source,worktree,path,optional)})end),choose(template,function()return bind(edit,{L,choose(optional,dest,source),bind(expand,{substitute}),(gsub(dest,'%.in$',''))})end)),nil)end),copies.command and bind(run,{L,env,'cd',worktree,'&&',copies.command}))end local function preconditions(env,release,version,main,substitute)local branch,tag='release-v'..version,'v'..version local name=choose(main~=nil,main,'a branch')local checkout=format('git checkout --force %s',name)local checks={{'the working tree is clean',function()return git(env,'status --porcelain')==''end,checkout},{format('%s is checked out',name),function()return main~=nil and git(env,'symbolic-ref --short HEAD')==main end,checkout},{'origin can be fetched',function()return git(env,'fetch --quiet origin')~=nil end},{format('%s is up to date with origin',name),function()return git(env,'rev-parse refs/heads/'..main)==git(env,'rev-parse origin/'..main)end,format('git reset --hard origin/%s',name)},{format('branch %s is unused',branch),function()return git(env,'rev-parse --verify --quiet refs/heads/'..branch)==nil end,'git branch -D '..branch},{format('tag %s is unused',tag),function()return git(env,'rev-parse --verify --quiet refs/tags/'..tag)==nil end,'git tag -d '..tag},}if release.news~=nil then local news=slurp(File(release.news))append(checks,{format('%s has news under a placeholder header',release.news),function()return news~=nil and has_placeholder(news)end})append(checks,{format('%s has no %s release yet',release.news,version),function()return news~=nil and find(news,'release '..version..' (',1,true)==nil end})end map(release.replacements or{},function(entry)local path,pairs=destructure(entry)append(checks,{format('%s exists',path),function()return exists(path)end})reduce(pairs,{},function(before,pair)local text,earlier=destructure(pair),copy(before)append(checks,{format("%s contains '%s'",path,text),function()local content=slurp(File(path))return content~=nil and find(replace(content,substitute,earlier),text,1,true)~=nil end})append(before,pair)end)end)map(branches(release),function(pages)local copies=release[pages]append(checks,{format('%s is not ahead of origin',pages),function()local ref,origin=git(env,'rev-parse refs/heads/'..pages),git(env,'rev-parse origin/'..pages)return origin~=nil and(ref==nil or ref==origin or git(env,'merge-base --is-ancestor',ref,origin)~=nil)end,format('git branch -f %s origin/%s',pages,pages)})append(checks,{format('%s can be checked out in a new worktree',pages),function()local worktrees=git(env,'worktree list --porcelain')return worktrees~=nil and find('\n'..worktrees..'\n','\nbranch refs/heads/'..pages..'\n',1,true)==nil end})map(filter(ordered_keys(copies),function(path)return ordered_get(copies,path).optional and match(path,'%.in$')~=nil end),function(template)append(checks,{format('%s or the release has %s',pages,template),function()return exists(template)or git(env,format('cat-file -e origin/%s:%s',pages,template))~=nil end})end)end)if release.rockspec~=nil then local rockspec=slurp(File(release.rockspec))append(checks,{format('%s is a development rockspec',release.rockspec),function()return match(release.rockspec,DEVROCKSPEC)~=nil end})append(checks,{format('%s sets _MODREV on its first line',release.rockspec),function()return rockspec~=nil and match(rockspec,MODREV)~=nil end})if release.check_dependencies then append(checks,{format('the dependencies of %s are installed',release.rockspec),function()return dependencies_installed(env,release.rockspec)end})end end return checks end local function undo_commands(checks)return reduce(filter(checks,function(check)local _,predicate,undo=unpack(check)return undo~=nil and not predicate()end),{},function(r,check)choose(not contains(r,check[3]),bind(append,{r,check[3]}))end)end local function generated(luke)local templates=map(luke.modules or{},function(pair)local _,rules=destructure(pair)return map(filter(rules.sources or{},function(source)return match(source,'%.in$')~=nil end),function(source)return(gsub(source,'%.in$',''))end)end)return flatten(templates,map(ordered_keys(luke.files),function(name)return script_target(name,ordered_get(luke.files,name))end))end local function ignoring(env,paths)local files={}local listed=git(env,'ls-files --others --ignored --exclude-standard --',unpack(map(paths,shellword)))for file in gmatch(listed or'','[^\n]+')do append(files,shellword(file))end local r={}if not isempty(files)then local verbose=git(env,'check-ignore --no-index --verbose --',unpack(files))for source,n in gmatch(verbose or'','([^\n]-):(%d+):[^\n]*')do if match(source,'^[^/]')and match(source,'^%.git/')==nil and match(source,'%.gitignore$')then r[source]=r[source]or{}r[source][tonumber(n)]=true end end end return r end local function unignore(L,env,paths)repeat local lines=ignoring(env,paths)local sources=sorted(keys(lines))map(sources,function(source)edit(L,source,function(text)local kept,n={},0 for line in gmatch(text,'[^\n]*\n?')do if line~=''then n=n+1 choose(not lines[source][n],bind(append,{kept,line}))end end return concat(kept)end)end)until L.dryrun or isempty(sources)return 0 end return{copy_command=copy_command,release=function(L,env,build)local version,main=L.release,git(env,'symbolic-ref --short HEAD')local substitute=mkenv({version=version},L.clidefs,L.luke.substitute,SHELLENV)local checks=preconditions(env,L.luke.release or{},version,main,substitute)local failed=dropuntil(checks,function(check)local description,predicate=unpack(check)L.verbose('checking whether ',description,'... ')local ok=predicate()L.verbose(ok and'yes\n'or'no\n')if not ok then return description end end)if failed~=nil then fatal('cannot release %s: check failed: %s',version,failed)return 1 end local rules=L.luke.release or{}local news,rockspec=rules.news,rules.rockspec local released=rockspec and rockspec_name(rockspec,version)local added=flatten(rules.add,generated(L.luke),released)local today=date'%Y-%m-%d'local package,notes=announcement((interpolate(env,L.luke.package)),version,news and release_news(add_release_header(slurp(File(news))or'',version,today),version),rockspec and slurp(File(rockspec)))local pages=branches(rules)local worktrees=reduce(pages,{},function(r,branch)r[branch]=tmpname()rm(r[branch])end)local steps=flatten(news and{bind(edit,{L,news,bind(add_release_header,{[2]=version,[3]=today})}),bind(run,{L,env,'git commit',format("-m 'maint: update NEWS.md for %s.'",version),news}),},bind(run,{L,env,'git checkout -b','release-v'..version}),news and bind(edit,{L,news,remove_placeholder}),map(rules.replacements or{},function(entry)local path,pairs=destructure(entry)return bind(edit,{L,path,bind(replace,{[2]=substitute,[3]=pairs})})end),build,rockspec and{bind(edit,{L,rockspec,bind(set_modrev,{[2]=version}),released}),bind(run,{L,env,'git rm --quiet',rockspec}),},not isempty(added)and{bind(unignore,{L,env,added}),bind(run,{L,env,'git add -f',unpack(added)}),}or nil,bind(run,{L,env,format("git commit -a -m 'Release v%s.'",version)}),bind(tag,{L,env,version,notes}),map(pages,function(branch)local worktree=worktrees[branch]return{git(env,'rev-parse refs/heads/'..branch)~=git(env,'rev-parse origin/'..branch)and bind(run,{L,env,'git branch --quiet --force',branch,'origin/'..branch})or{},bind(run,{L,env,'git worktree add --quiet',worktree,branch}),publish_steps(L,env,substitute,worktree,rules[branch]),bind(run,{L,env,'git -C',worktree,"add -A && git -C",worktree,format("commit -m '%s: update to $package %s.'",branch,version)}),bind(run,{L,env,'git worktree remove',worktree}),}end),bind(run,{L,env,'git checkout --quiet',main}))local status=dropuntil(steps,isnonzero,call)or 0 choose(status~=0,function()map(filter(sorted(values(worktrees)),exists),function(worktree)run(L,env,'git worktree remove --force',worktree)end)end)if status==0 then local pushed=flatten(main,'release-v'..version,'v'..version,pages)L.write(format('\nRelease v%s is ready. To publish it:\n\n git push origin %s\n',version,concat(pushed,' ')))local GH=env.GH or configure(L,env,{checkprog='GitHub CLI',progs={'gh','true'}})if GH~='true'then L.write(format(" %s release create v%s --verify-tag --latest --title '[ANN] %s v%s released' --notes-from-tag\n",GH,version,package,version))end if released~=nil then L.write(format(' git checkout release-v%s\n luarocks upload %s\n git checkout %s\n',version,released,main))end else local undo=undo_commands(checks)choose(not isempty(undo),function()L.write(format('\nRelease v%s failed. To undo it before trying again:\n\n%s',version,concat(map(undo,function(command)return' '..command..'\n'end))))end)end return status end,} end package.preload['luke.schema']=function() local _ENV=require'luke.normalize'{'luke._base','luke.configure','luke.functional','luke.platforms','type.path',}local FORMAT=0 local MACRO='^[%a_][%w_]*$'local ORDERED={bin=true,defines=true,external_dependencies=true,files=true,modules=true,}local function bad(path,...)fatal('bad %s: %s',path,format(...))end local function index(path,k)if isstring(k)and match(k,'^[%a_][%w_]*$')then return path==''and k or path..'.'..k end return format(isstring(k)and"%s['%s']"or'%s[%s]',path,str(k))end local function claim(r,name,value,path)choose(r[name]~=nil,bind(bad,{path,"more than one entry for '%s'",name}))r[name]=value end local function mkcheck(kind)return function(x,path)if x~=nil and type(x)~=kind then bad(path,'%s expected, got %s',kind,type(x))end return x end end local function list_of(normalize)return function(x,path)if isstring(x)then x={x}end mkcheck'table'(x,path)if x~=nil then local r={}for i=1,len(x)do r[i]=normalize(x[i],index(path,i))end return r end end end local function map_of(normalize,names)return function(x,path)mkcheck'table'(x,path)if x~=nil then local r={}for k,v in next,x do if names~=nil then names(k,index(path,k))end r[k]=normalize(v,index(path,k))end return r end end end local function fields(schema,others)local rest=choose(others==true,function()return id end,function()return others end)return setmetatable({schema=schema},{__call=function(_,x,path)return mkcheck'table'(x,path)and reduce(keys(x),{},function(r,k)local normalize=schema[k]or rest or bind(bad,{path,"unsupported key '%s'",str(k)})r[k]=normalize(x[k],index(path,k))end)end})end local function keypaths(normalize,path)return sorted(flatten(map(keys(normalize.schema),function(k)local nested,p=normalize.schema[k],index(path or'',k)return choose(istable(nested),function()return{p,keypaths(nested,p)}end,p)end)))end local function pathname(x,path)choose(mkcheck'string'(x,path)=='',bind(bad,{path,'empty path'}))local r=x and match(x,'^/?')..concat(except(gmatch(x,'[^/]+'),'.'),'/')return choose(r=='','.',r)end local function luke_format(x,path)choose(x==nil,bind(bad,{path,'missing; this luke reads format %d, which replaces the lukefiles of luke 0.4 and earlier',FORMAT}))choose(int(mkcheck'number'(x,path))==nil or x<0,bind(bad,{path,'non-negative integer expected, got %s',str(x)}))choose(x~=FORMAT,function()bad(path,'lukefile format %d needs %s luke; this one reads format %d',x,choose(x>FORMAT,'a newer','an older'),FORMAT)end)return x end local function isprobe(x)return istable(x)and contains(configure,function(name)return x[name]~=nil end)end local function definition(x,path)return case(type(x),{['string']=function()local name,eq,value=match(x,'^([%a_][%w_]*)(=?)(.*)$')choose(name==nil or eq==''and value~='',bind(bad,{path,"bad macro definition '%s'",x}))return{[name]=choose(eq=='',1,value)}end,['table']=function()local names=keys(x)choose(names.n~=1,bind(bad,{path,'one definition expected, got %d',names.n}))local name=names[1]choose(isstring(name)and configure[name]~=nil,bind(bad,{path,'probe needs a macro name, such as {HAVE_X = {%s = ...}}',str(name)}))choose(not isstring(name)or match(name,MACRO)==nil,bind(bad,{path,"bad macro name '%s'",str(name)}))local value=x[name]choose(not(isstring(value)or type(value)=='number'or isprobe(value)),bind(bad,{index(path,name),'string, number or probe expected, got %s',type(value)}))return{[name]=value}end,function(kind)bad(path,'string or table expected, got %s',kind)end,})end local function pair_of(normalize,rename)return function(x,path)local names=keys(mkcheck'table'(x,path))choose(names.n~=1,bind(bad,{path,'one entry expected, got %d',names.n}))local name=names[1]choose(not isstring(name),bind(bad,{path,'name expected, got %s',type(name)}))return{[(rename or id)(name,path)]=normalize(x[name],index(path,name))}end end local function ordered_map(entry)local function narrowed(x,path)return entry(istable(x)and filter_platforms(x,nil,nil,ORDERED)or x,path)end local function layer_entries(layer,path)map(filter(sorted_keys(layer),function(k)return k~='platforms'and not islistkey(layer,k)end),function(k)bad(index(path,k),'list entry expected, such as {%s = ...}',index('',k))end)local r=list_of(narrowed)(layer,path)reduce(r,{},function(seen,pair)claim(seen,(next(pair)),true,path)end)return r end local function check_layers(x,path)layer_entries(mkcheck'table'(x,path),path)local at=index(path,'platforms')local platforms=mkcheck'table'(x.platforms,at)choose(platforms~=nil,function()local names,others=partition(sorted_keys(platforms),isplatform)map(names,function(name)check_layers(platforms[name],index(at,name))end)check_layers(pluck(others,platforms)or{},at)end)end local function resolve(x,path)return ordered_merge(unpack(map(layers(x),function(layer)return choose(layer.platforms~=nil,function()return resolve(layer,path)end,function()return layer_entries(layer,path)end)end)))end return function(x,path)local t=choose(isstring(x),function()return{x}end,function()return x end)choose(t~=nil,bind(check_layers,{t,path}))return t and resolve(t,path)end end local defines=ordered_map(definition)local function dependency_name(name,path)choose(match(name,MACRO)==nil,bind(bad,{path,"bad dependency name '%s'",name}))return name end local dependencies=ordered_map(pair_of(mkcheck'table',dependency_name))local function rules(x,path)local r=case(type(x),{['nil']=nop,['string']=function()return{sources={x}}end,['table']=function()if len(x)>0 then return{sources=x}elseif isstring(x.sources)then return merge({sources={x.sources}},x)end return x end,function(kind)bad(path,'string or table expected, got %s',kind)end,})return r and merge({defines=defines(r.defines,index(path,'defines')),sources=list_of(pathname)(r.sources,index(path,'sources')),incdirs=list_of(pathname)(r.incdirs,index(path,'incdirs')),libdirs=list_of(pathname)(r.libdirs,index(path,'libdirs')),},r)end local function program(x,path)local r=rules(x,path)if isempty(r.sources or{})then bad(path,'no sources')end mkcheck'string'(r.command,index(path,'command'))map_of(mkcheck'string',mkcheck'string')(r.env,index(path,'env'))return r end local function script(x,path)return case(type(x),{['string']=function()return pathname(x,path)end,['table']=function()return program(x,path)end,function(kind)bad(path,'string or table expected, got %s',kind)end,})end local function mkscripts(mkname)local pair=pair_of(script,pathname)return ordered_map(function(x,path)return case(type(x),{['string']=function()local source=pathname(x,path)return{[mkname(source)]=source}end,['table']=function()choose(x.sources~=nil,bind(bad,{path,'programs need a name, such as {prog = {sources = ...}}'}))return pair(x,path)end,function(kind)bad(path,'string or table expected, got %s',kind)end,})end)end local function filled(path)return(gsub(path,'%.in$',''))end local scripts=mkscripts(function(source)return filled(basename(source))end)local files=mkscripts(filled)local function fills_too(dest,source)return match(dest,'%.in$')~=nil and match(source,'%.in$')~=nil end local copy_pair=pair_of(function(x,path)return{optional=false,source=pathname(x,path)}end,pathname)local function copy_entry(x,path)return case(type(x),{['string']=function()local source=pathname(x,path)return{[source]={optional=true,source=source}}end,['table']=function()return copy_pair(x,path)end,function(kind)bad(path,'string or table expected, got %s',kind)end,})end local function copies(x,path)if mkcheck'table'(x,path)==nil then return nil end local entries=copy(x)entries.command=nil local r=ordered_map(copy_entry)(entries,path)r.command=mkcheck'string'(x.command,index(path,'command'))local written=reduce(ordered_keys(r),{},function(written,dest)written[dest]=true end)map(r or{},function(pair)local dest,copy=destructure(pair)choose(fills_too(dest,copy.source),function()claim(written,filled(dest),true,path)end)end)return r end return{ORDERED=ORDERED,copies=copies,defines=defines,dependencies=dependencies,fields=fields,files=files,keypaths=keypaths,list_of=list_of,luke_format=luke_format,map_of=map_of,mkcheck=mkcheck,ordered_map=ordered_map,pair_of=pair_of,pathname=pathname,rules=rules,scripts=scripts,} end package.preload['luke.version']=function() return{VERSION='0.6',versiontext=[[ luke (Luke) 0.6 Written by Gary V. Vaughan , 2014 Copyright (C) 2026, 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. ]],} end package.preload['type.context-manager']=function() local _ENV=require'luke.normalize'{'luke.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(keep)local conftest=tmpname()local cm,err=ContextManager(function(cm)local closed=context_close(cm)if keep then return closed end rm(conftest)rm(cm.objfile)return closed and rm(cm.filename)end,open,conftest..'.c','w')if cm~=nil then cm.objfile=gsub(conftest,'^.*/','')..'.o'end return cm,err 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'luke.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'luke.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)) gvvaughan-lyaml-f68df96/doc/000077500000000000000000000000001526600404000160145ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/doc/api/000077500000000000000000000000001526600404000165655ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/doc/api/index.html000066400000000000000000000043151526600404000205650ustar00rootroot00000000000000 lyaml 6.2.10 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, 5.4 and 5.5

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.5.0 Last updated 2026-10-20 17:32:32
gvvaughan-lyaml-f68df96/doc/api/ldoc.css000066400000000000000000000143471526600404000202310ustar00rootroot00000000000000/* 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 .function-name { color: #60447f; } 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; } gvvaughan-lyaml-f68df96/doc/api/modules/000077500000000000000000000000001526600404000202355ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/doc/api/modules/lyaml.explicit.html000066400000000000000000000133011526600404000240570ustar00rootroot00000000000000 lyaml 6.2.10 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:

Returns:

    string value which was a string already

Usage:

    tagarg = explicit.str(tagarg)
generated by LDoc 1.5.0 Last updated 2026-10-20 17:32:32
gvvaughan-lyaml-f68df96/doc/api/modules/lyaml.functional.html000066400000000000000000000111221526600404000243770ustar00rootroot00000000000000 lyaml 6.2.10 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

NULL lyaml.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.5.0 Last updated 2026-10-20 17:32:32
gvvaughan-lyaml-f68df96/doc/api/modules/lyaml.html000066400000000000000000000123711526600404000222450ustar00rootroot00000000000000 lyaml 6.2.10 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_opts Dump options table.
loader_opts Load 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.5.0 Last updated 2026-10-20 17:32:32
gvvaughan-lyaml-f68df96/doc/api/modules/lyaml.implicit.html000066400000000000000000000302041526600404000240510ustar00rootroot00000000000000 lyaml 6.2.10 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:

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:

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:

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:

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:

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:

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:

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:

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:

Returns:

    number float equivalent, if a valid token was recognized

Or

    nil otherwise, nil

Usage:

    maybe_float = implicit.sexfloat(value)
generated by LDoc 1.5.0 Last updated 2026-10-20 17:32:32
gvvaughan-lyaml-f68df96/doc/lyaml.md000066400000000000000000000202641526600404000174600ustar00rootroot00000000000000--- layout: default --- # LYAML [LYAML] binds [LibYAML] to [LuaJIT] and [Lua] 5.1 through 5.5, for reading and writing [YAML 1.1][yaml11]: * [`lyaml.load`](#2-loading) turns a YAML stream into Lua tables, and [`lyaml.dump`](#3-dumping) turns Lua tables back into YAML * [`lyaml.null`](#4-null) keeps YAML's nulls, which Lua tables can't hold as `nil` * the [scalar conversions](#5-scalar-conversions) of numbers, booleans and the rest are yours to change * a [low-level API](#6-the-low-level-api) of LibYAML's scanner, parser and emitter, for when the tables are not enough The [API reference](api/) has every function in detail. ## 1. Installation Install the latest release with [LuaRocks]: {% highlight bash %} luarocks install lyaml {% endhighlight %} LYAML needs LibYAML and its headers, which most systems package as `libyaml-dev` or `libyaml`. If LuaRocks can't find them, say where they are: {% highlight bash %} luarocks install lyaml YAML_DIR=/opt/homebrew {% endhighlight %} Without LuaRocks, build and install it with [Luke], from a clone of the [repository][lyaml]: {% highlight bash %} build-aux/luke YAML_DIR=/opt/homebrew sudo build-aux/luke PREFIX=/usr/local install {% endhighlight %} ## 2. Loading `lyaml.load` returns the first document of a YAML stream as a Lua value: {% highlight lua %} local lyaml = require 'lyaml' lyaml.load 'foo: bar' --> {foo = 'bar'} {% endhighlight %} A mapping becomes a table with those keys, and a sequence a list. With `all = true` in its second argument, `load` returns a list of every document in the stream instead: {% highlight lua %} local stream = [[ --- one ... --- two ... ]] lyaml.load(stream) --> 'one' lyaml.load(stream, {all = true}) --> {'one', 'two'} {% endhighlight %} An alias loads as the table its anchor names, so after {% highlight lua %} local t = lyaml.load 'base: &b {x: 1}\nother: *b' {% endhighlight %} `t.base == t.other`. Anchors are local to the document they are in. `load` raises an error for a stream that LibYAML can't parse, or a value that does not suit its tag, with the line and column where it found the problem: {% highlight lua %} lyaml.load 'a: !!int nope' --> error: 1:4: invalid 'tag:yaml.org,2002:int' value: 'nope' {% endhighlight %} ## 3. Dumping `lyaml.dump` takes a list of documents, and returns a YAML stream with each of them in turn. To dump one table, put it in a list of its own: {% highlight lua %} lyaml.dump { {foo = 'bar'} } --> --- --> foo: bar --> ... lyaml.dump {'one', 'two'} --> --- one --> ... --> --- two --> ... {% endhighlight %} A table whose keys are exactly 1, 2, 3 and so on is dumped as a sequence, and any other table as a mapping, in the order that `pairs` returns its keys. A string with a newline is dumped as a literal block, and a string that would load as something else, such as `'42'` or `'yes'`, is quoted, so that it loads as a string again. `dump` raises an error for a function, userdata or thread. The `anchors` field of the second argument maps anchor names to tables that the documents contain. `dump` writes such a table with its anchor the first time it reaches it, and as an alias after that: {% highlight lua %} local base = {x = 1} lyaml.dump({ {a = base, b = base} }, {anchors = {base = base}}) --> --- --> a: &base --> x: 1 --> b: *base --> ... {% endhighlight %} ## 4. Null A Lua table can't hold a key whose value is `nil`, but a YAML mapping can have a key whose value is null. So `load` returns YAML's null, written `~`, `null` or nothing at all, as `lyaml.null`, and `dump` writes `lyaml.null` as `~`: {% highlight lua %} local t = lyaml.load 'a: ~' t.a == lyaml.null --> true lyaml.dump {t} --> --- --> a: ~ --> ... {% endhighlight %} `lyaml.null` is a value to compare with, not a function to call. ## 5. Scalar Conversions A plain scalar, without quotes or a tag, is converted by the content it has: `1` loads as a number, `yes` as `true`, `~` as `lyaml.null`, and so on. The functions in `lyaml.implicit` each try one conversion, and return `nil` for a value they don't recognise: | Function | Recognises | |----------------|---------------------------------------------| | `null` | `~`, `null`, `Null`, `NULL` and nothing | | `bool` | `true`/`false`, `yes`/`no` and `on`/`off`, in lower, capitalised or upper case | | `octal` | `012` | | `decimal` | `12345` | | `float` | `1e-3`, `-0.12` | | `inf`, `nan` | `.inf`, `-.inf`, `.nan` | | `hexadecimal` | `0xdeadbeef` | | `binary` | `0b1010_0111` | | `sexagesimal` | `190:20:30` | | `sexfloat` | `190:20:30.15` | By default, `load` tries them all, and leaves the value as a string if none of them recognises it. Pass your own function in the `implicit_scalar` field of the second argument to choose differently, such as with `anyof` from `lyaml.functional`, which tries each function in turn and returns the first result that is not `nil`: {% highlight lua %} local functional = require 'lyaml.functional' local implicit = require 'lyaml.implicit' -- Only nulls and decimals; yes, 0x1f and the rest stay strings. local strict = functional.anyof { implicit.null, implicit.decimal, functional.id, } lyaml.load('a: yes\nb: 10', {implicit_scalar = strict}) --> {a = 'yes', b = 10} {% endhighlight %} Pass the same function to `dump` too, so that it quotes only the strings that function would convert when they are loaded again. A scalar with a tag is converted by the function for that tag in the `explicit_scalar` field instead, a table keyed by the full tag name. The default has the functions from `lyaml.explicit` for `tag:yaml.org,2002:bool`, `float`, `int`, `null` and `str`, which raise an error for a value that does not suit the tag. ## 6. The Low-Level API The `yaml` module is the C binding to LibYAML that `lyaml` is built on. `yaml.parser` returns an iterator over the events of a YAML stream, each a table with a `type`, the `start_mark` and `end_mark` of where it is in the stream, and the other fields of that event, such as the `value`, `anchor`, `tag` and `style` of a scalar: {% highlight lua %} local yaml = require 'yaml' for event in yaml.parser 'a: [1, b]' do print(event.type) end --> STREAM_START, DOCUMENT_START, MAPPING_START, SCALAR, --> SEQUENCE_START, SCALAR, SCALAR, SEQUENCE_END, MAPPING_END, --> DOCUMENT_END, STREAM_END {% endhighlight %} That is the "parse" step of the "parse, compose, construct" model of the [YAML 1.1 specification][yaml11], with every detail of the stream that `load` leaves out, such as line and column numbers. `yaml.scanner` returns an iterator over the tokens of the stream instead, one step lower: see LibYAML's [yaml.h] for what each one holds. `yaml.emitter` goes the other way. Its `emit` function takes one event table at a time, and the `STREAM_END` event returns `true` and the stream: {% highlight lua %} local emitter = yaml.emitter() emitter.emit {type = 'STREAM_START'} emitter.emit {type = 'DOCUMENT_START'} emitter.emit {type = 'SCALAR', value = 'hi'} emitter.emit {type = 'DOCUMENT_END'} local ok, stream = emitter.emit {type = 'STREAM_END'} --> stream == '--- hi\n...\n' {% endhighlight %} ## 7. Bugs and Contributions Please report bugs and make suggestions as [GitHub issues][issues], and check that they are not already fixed on [master][lyaml] first. Pull requests are especially welcome. [issues]: https://github.com/gvvaughan/lyaml/issues [libyaml]: https://pyyaml.org/wiki/LibYAML [lua]: https://www.lua.org [luajit]: https://luajit.org [luarocks]: https://luarocks.org [luke]: https://github.com/gvvaughan/luke [lyaml]: https://github.com/gvvaughan/lyaml [yaml.h]: https://github.com/yaml/libyaml/blob/master/include/yaml.h [yaml11]: https://yaml.org/spec/1.1/ gvvaughan-lyaml-f68df96/ext/000077500000000000000000000000001526600404000160475ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/ext/yaml/000077500000000000000000000000001526600404000170115ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/ext/yaml/emitter.c000066400000000000000000000322531526600404000206330ustar00rootroot00000000000000/* * emitter.c, LibYAML emitter binding for Lua * Written by Gary V. Vaughan, 2013 * * Copyright (C) 2013-2026 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; } gvvaughan-lyaml-f68df96/ext/yaml/lyaml.h000066400000000000000000000112551526600404000203040ustar00rootroot00000000000000/* * lyaml.h, libyaml parser binding for Lua * Written by Gary V. Vaughan, 2013 * * Copyright (C) 2013-2026 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 gvvaughan-lyaml-f68df96/ext/yaml/parser.c000066400000000000000000000247121526600404000204570ustar00rootroot00000000000000/* * parser.c, libyaml parser binding for Lua * Written by Gary V. Vaughan, 2013 * * Copyright (C) 2013-2026 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; } gvvaughan-lyaml-f68df96/ext/yaml/scanner.c000066400000000000000000000213251526600404000206110ustar00rootroot00000000000000/* * scanner.c, libyaml scanner binding for Lua * Written by Gary V. Vaughan, 2013 * * Copyright (C) 2013-2026 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; } gvvaughan-lyaml-f68df96/ext/yaml/yaml.c000066400000000000000000000037671526600404000201340ustar00rootroot00000000000000/* * yaml.c, LibYAML binding for Lua * Written by Andrew Danforth, 2009 * * Copyright (C) 2014-2026 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); #if LUA_VERSION_NUM >= 502 luaL_newlib(L, R); #else luaL_register(L, "yaml", R); #endif lua_pushliteral(L, MYVERSION); lua_setfield(L, -2, "version"); return 1; } gvvaughan-lyaml-f68df96/lib/000077500000000000000000000000001526600404000160155ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/lib/lyaml/000077500000000000000000000000001526600404000171335ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/lib/lyaml/explicit.lua000066400000000000000000000066501526600404000214660ustar00rootroot00000000000000-- LYAML parse explicit token values. -- Written by Gary V. Vaughan, 2015 -- -- Copyright(C) 2015-2026 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, } gvvaughan-lyaml-f68df96/lib/lyaml/functional.lua000066400000000000000000000047641526600404000220130ustar00rootroot00000000000000-- Minimal functional programming utilities. -- Written by Gary V. Vaughan, 2015 -- -- Copyright(C) 2015-2026 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, } gvvaughan-lyaml-f68df96/lib/lyaml/implicit.lua000066400000000000000000000172671526600404000214650ustar00rootroot00000000000000-- LYAML parse implicit type tokens. -- Written by Gary V. Vaughan, 2015 -- -- Copyright(C) 2015-2026 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, } gvvaughan-lyaml-f68df96/lib/lyaml/init.lua000066400000000000000000000372071526600404000206120ustar00rootroot00000000000000-- Transform between YAML 1.1 streams and Lua table representations. -- Written by Gary V. Vaughan, 2013 -- -- Copyright(C) 2013-2026 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, } gvvaughan-lyaml-f68df96/lukefile000066400000000000000000000032661526600404000170010ustar00rootroot00000000000000--[[ LYAML binding for Lua 5.1, 5.2, 5.3, 5.4 & 5.5 Copyright (C) 2013-2026 Gary V. Vaughan ]] luke_format = 0 package = 'lyaml' version = '$USER' defines = { {PACKAGE = '"$package"'}, {VERSION = '"$version"'}, 'NDEBUG', '_FORTIFY_SOURCE=2', platforms = { aix = {'_ALL_SOURCE'}, bsd = {'_BSD_SOURCE'}, freebsd = {'__BSD_VISIBLE'}, macosx = {'_DARWIN_C_SOURCE'}, }, } 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'}, } -- `luke release` builds lyaml before committing it, without LuaRocks to -- say where libyaml and lua.h are, so pass them yourself. With -- Homebrew, for example: -- -- build-aux/luke release version=X.Y YAML_DIR=/opt/homebrew \ -- LUA_INCDIR=/opt/homebrew/include/lua5.5 release = { news = 'NEWS.md', rockspec = 'lyaml-dev-1.rockspec', check_dependencies = true, add = {'doc/api/'}, replacements = { {['README.md'] = { {['?branch=master'] = '?branch=release-v@version@'}, }}, }, ['gh-pages'] = { {api = 'doc/api/'}, {['index.md'] = 'doc/lyaml.md'}, '_config.yml.in', }, } gvvaughan-lyaml-f68df96/lyaml-6.2.10-1.rockspec000066400000000000000000000034371526600404000210070ustar00rootroot00000000000000local _MODREV, _SPECREV = '6.2.10', '-1' rockspec_format = '3.0' package = 'lyaml' version = _MODREV .. _SPECREV description = { summary = 'libYAML binding for Lua', detailed = 'Read and write YAML format files with Lua.', homepage = 'https://github.com/gvvaughan/lyaml', license = 'MIT/X11', } source = { url = 'https://github.com/gvvaughan/lyaml/archive/v' .. _MODREV .. '.zip', dir = 'lyaml-' .. _MODREV, } dependencies = { 'lua >= 5.1, < 5.6', } 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'}, } test_dependencies = { 'ansicolors', 'luacov', 'specl >= 16', } test = { type = 'command', command = 'specl -vfreport --coverage spec/*_spec.yaml', } if _MODREV == 'dev' then -- luarocks.org's ldoc fails on Lua 5.5; gvvaughan's works. build_dependencies = {'gvvaughan/ldoc'} build.copy_directories = nil source = { url = 'git+https://github.com/gvvaughan/lyaml.git', } else -- A release commits its LDocs, so don't build them again. build.build_command = build.build_command .. ' LDOC=true' end gvvaughan-lyaml-f68df96/spec/000077500000000000000000000000001526600404000162015ustar00rootroot00000000000000gvvaughan-lyaml-f68df96/spec/ext_yaml_emitter_spec.yaml000066400000000000000000000254631526600404000234640ustar00rootroot00000000000000# LYAML binding for Lua 5.1, 5.2, 5.3, 5.4 & 5.5 # Copyright (C) 2013-2026 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"} gvvaughan-lyaml-f68df96/spec/ext_yaml_parser_spec.yaml000066400000000000000000000407421526600404000233040ustar00rootroot00000000000000# LYAML binding for Lua 5.1, 5.2, 5.3, 5.4 & 5.5 # Copyright (C) 2013-2026 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} gvvaughan-lyaml-f68df96/spec/ext_yaml_scanner_spec.yaml000066400000000000000000000366401526600404000234430ustar00rootroot00000000000000# LYAML binding for Lua 5.1, 5.2, 5.3, 5.4 & 5.5 # Copyright (C) 2013-2026 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} gvvaughan-lyaml-f68df96/spec/lib_lyaml_functional_spec.yaml000066400000000000000000000072431526600404000242730ustar00rootroot00000000000000# LYAML binding for Lua 5.1, 5.2, 5.3, 5.4 & 5.5 # Copyright (C) 2013-2026 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(pack(f(nil, 1, f, false, nil))).to_equal(pack(nil, 1, f, 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 gvvaughan-lyaml-f68df96/spec/lib_lyaml_spec.yaml000066400000000000000000000405411526600404000220470ustar00rootroot00000000000000# LYAML binding for Lua 5.1, 5.2, 5.3, 5.4 & 5.5 # Copyright (C) 2013-2026 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) gvvaughan-lyaml-f68df96/spec/spec_helper.lua000066400000000000000000000147571526600404000212130ustar00rootroot00000000000000--[[ LYAML binding for Lua 5.1, 5.2, 5.3, 5.4 & 5.5 Copyright (C) 2013-2026 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