texlive[60602] Master: yamlvars (24sep21)

commits+karl at tug.org commits+karl at tug.org
Fri Sep 24 23:44:17 CEST 2021


Revision: 60602
          http://tug.org/svn/texlive?view=revision&revision=60602
Author:   karl
Date:     2021-09-24 23:44:17 +0200 (Fri, 24 Sep 2021)
Log Message:
-----------
yamlvars (24sep21)

Modified Paths:
--------------
    trunk/Master/tlpkg/bin/tlpkg-ctan-check
    trunk/Master/tlpkg/libexec/ctan2tds
    trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc

Added Paths:
-----------
    trunk/Master/texmf-dist/doc/lualatex/yamlvars/
    trunk/Master/texmf-dist/doc/lualatex/yamlvars/README.md
    trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.pdf
    trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.tex
    trunk/Master/texmf-dist/tex/lualatex/yamlvars/
    trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.lua
    trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.sty
    trunk/Master/texmf-dist/tex/lualatex/yamlvars/tinyyaml.lua
    trunk/Master/tlpkg/tlpsrc/yamlvars.tlpsrc

Added: trunk/Master/texmf-dist/doc/lualatex/yamlvars/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/yamlvars/README.md	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/lualatex/yamlvars/README.md	2021-09-24 21:44:17 UTC (rev 60602)
@@ -0,0 +1,32 @@
+# YAMLvars -- parse a YAML document and create LaTeX definitons
+
+This LuaLaTeX package provides a YAML parser and some functions to declare and define LaTeX definitions using YAML files. 
+
+
+
+# License
+
+Copyright (C) 2021 Kale Ewasiuk
+
+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.
+
+
+


Property changes on: trunk/Master/texmf-dist/doc/lualatex/yamlvars/README.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.pdf	2021-09-24 21:42:36 UTC (rev 60601)
+++ trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.pdf	2021-09-24 21:44:17 UTC (rev 60602)

Property changes on: trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.tex
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.tex	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.tex	2021-09-24 21:44:17 UTC (rev 60602)
@@ -0,0 +1,264 @@
+% Kale Ewasiuk (kalekje at gmail.com)
+% 2021-09-24
+%
+% Copyright (C) 2021 Kale Ewasiuk
+%
+% 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.
+
+
+\documentclass{article}
+\usepackage{url}
+\usepackage{YAMLvars}
+\setlength{\parindent}{0ex}
+\setlength{\parskip}{0.75em}
+
+\begin{document}
+
+{\noindent\huge\bfseries YAMLvars \LARGE -- a YAML variable parser for LuaLaTeX}\\
+    2021-09-24, Kale Ewasiuk, \url{kalekje at gmail.com}
+
+YAMLvars is a LuaLaTeX-based package to help make definitions or produce LaTeX code using a YAML file.
+This package might be useful for you if you want to batch create docummnts
+by pushing various sets YAML data to a fixed LaTeX template,
+or just find it easier to read document metadata from a YAML file compared to the
+standard title, author, etc. commands.
+
+\section{Package Options}
+\leavevmode\llap{\texttt{useyv}\ \ \ }By default, when you specify a YAML variable, it will be defined using \texttt{gdef}
+(only if it wasn't defined previously).
+If you use this setting, unless otherwise specified, YAML variables will be accessible under
+the \texttt{\textbackslash yv\{<var>\}} command.
+This also allows numbers and symbols in the variable names.
+Note that internally, the variables are stored in the command sequence
+\texttt{yv--<var>}.
+
+\leavevmode\llap{\texttt{parseCLI}\ \ \ }If this option is enabled, any arguments passed to your lualatex compile
+command that end in ``.yaml'' will be used, separated by a space.
+If two yaml files are passed, the first one will be the declaration file,
+and the second will be the parsing file.
+They will be used at the beginning of the document.
+If one yaml file is passed, it will be treated as a parsing file, so you should
+declare the variables somewhere in the preamble.
+This option is offered to help with automation scripts.
+An example is showin in Section \ref{example}.
+
+\leavevmode\llap{\texttt{allowundeclared}\ \ \ }%
+It might be helpful to define something in your YAML parsing doc without declaring it.
+If you want this flexibility, use this setting. Note that eisting definitions will not be overwritten and an error
+will br thrown if the name exists.
+
+\section{Dependencies}
+This package contains the \texttt{tinyyaml} Lua package.
+If you want to use it for other purposes, you can bring it into Lua by either:
+\begin{verbatim}
+\directlua{yaml = YAMLvars.yaml}           or
+\directlua{yaml = require('tinyyaml')}
+\end{verbatim}
+The distribution: \url{https://github.com/peposso/lua-tinyyaml}\\
+The YAML specification: \url{https://yaml.org/spec/}\\
+
+Many of the ``transform'' and ``processing'' functions built-in to this package rely on other packages.
+\texttt{penlight},
+\texttt{xspace},
+\texttt{hyperref} for example.
+
+\section{Declaring variables}
+A declaration file can either be parsed with the command \texttt{declareYAMLvarsFile} command,
+or, if you want to do it \LaTeX, you can put the YAML code in the \texttt{declareYAMLvars} environment.
+It is a declaring YAML docuemnt is (like all YAML) key-value ditionary:
+The top level key is the name of the variable to be defined/used.
+If the value of the top level is a string: it's interpreted as a single transform function to be applied.
+Otherwise, it must
+be a table that contains at least one of the following keys:\\
+\texttt{xfm} (transform, may be a string or list of strings),\\
+\texttt{prc} (processing, must be a single string), or \\
+\texttt{dft} (default value, if being defined. Must be a string).
+
+The default value for variables is the Lua \texttt{nil}.
+YAMLvars will first check if the definition exists, if so, an error will be thrown
+so that we avoid overwriting.
+If the token is available, it is set to a package error, so that if the variable no defined later on, an error will
+tell the user they forgot to set it.
+This will be overwritten when you parse the variables and assign a value to it.
+
+You can change the default \texttt{xfm}, \texttt{prc}, or \texttt{dft} by changing
+the value (in Lua): \texttt{YAMLvars.xfmDefault = ''} etc.
+
+Here is an example of a declaration document.
+\begin{verbatim}
+\begin{declareYAMLvars}
+Location: addxspace                      # sets xfm=addxspace
+People: [arrsortlastnameAZ, list2nl]     # BAD! don't do.
+People:
+  xfm: [arrsortlastnameAZ, list2nl]      # Correct way
+Company:
+  dft: Amazon                            # Change default only
+Revisions:
+  dft: '1 & \today & initial version \\'
+  xfm: [sortZA, list2tab]
+Rhead:
+  prc: setRightHead
+\end{declareYAMLvars}
+\end{verbatim}
+
+
+
+\section{Parsing variables}
+A YAML file to be parsed will contain the variables as the top level keys, similar to declaring.
+The value can be anything you want; as long as you have applied appropriate transform and declaring
+functions to it so that it can be useful. For example, a value specified as a YAML list will first be
+interpreted as a Lua table (with numeric indexes/keys). You could declare a series of transforms functions
+to sort this table, map functions, and convert it to a series of \LaTeX  \texttt{\textbackslash item}s.
+
+Here is an example of a parsing document.
+
+
+\begin{verbatim}
+\begin{declareYAMLvars}
+Location: Planet Earth
+People:                # a YAML list
+  - Some One           # turns into Lua table
+  - No Body
+# company assumed Amazon if not set here
+Rhead: \today
+\end{declareYAMLvars}
+\end{verbatim}
+
+
+ \section{xfm -- Transform Functions}
+These functions accept two arguments: \texttt{(var, val)} where \texttt{var} is the variable (or key) and val is the value.
+The transforms are specified as a list and are iteratively applied to the val.
+Usually, the final \texttt{xfm} function should produce a string so it can be defined.
+
+Hint: if for some reason, your \texttt{xfm} and \texttt{prc} depends on other variables,
+you can access them within the function
+with \texttt{YAMLvars.varsvals}
+
+\subsection{Defining your own transform functions}
+After the package is loaded, you may add your function (somewhere in Lua)
+by adding it to the \texttt{YAMLvars.xfm} table.
+For example, if you wanted to wrap a variable's value with ``xxx'', here's how you could do that.
+\begin{verbatim}
+function myfunction(var, val)
+        return 'xxx'..val..'xxx'
+end
+YAMLvars.xfm['addmyfunction'] = myfunction
+\end{verbatim}
+
+  \section{prc -- Processing Functions}
+Like the transform functions, the processing function must accept \texttt{(var, val)}.
+Only one processing function is applied to the final (var, val) after the transforms are done.
+
+This package includes \texttt{gdef} to set a definition, \texttt{yvdef} to define a variable under the \texttt{yv} command.
+\texttt{title, author, date} to set \texttt{\textbackslash @title, \textbackslash @author, \textbackslash @date},
+respectively
+
+    \section{Example}\label{example}
+
+    Suppose you had a number of bills of sales in yaml format and wanted to produce some nice pdfs.
+    The following code shows how this could be done.
+
+    \pagebreak
+    \vspace*{-5em}
+    \subsection{The main tex template}
+    \begin{verbatim}
+ %% main.tex
+\documentclass{article}
+\usepackage[paperheight=4in,paperwidth=3in,margin=0.25in]{geometry}
+\usepackage[pl,func,extras]{penlight}
+\usepackage[useyv,parseCLI]{YAMLvars}  % using command line option to make files
+\usepackage{hyperref}
+\usepackage{xspace}
+\usepackage{luacode}
+
+\setlength{\parindent}{0ex}
+\setlength{\parskip}{0.75em}
+
+\begin{luacode*} -- adding a custom function, put hfill between k-v pairs
+    function YAMLvars.xfm.kv2hfill(var, val)
+        local t = {}
+        for k, v in pairs(val) do
+            t[#t+1] = k..'\\hfill '..tostring(v)
+        end
+        return t
+    end
+\end{luacode*}
+
+
+%! language = yaml
+\begin{declareYAMLvars}
+Customer: addxspace
+Date: addxspace
+Items:
+    xfm: [kv2hfill, arr2itemize]
+\end{declareYAMLvars}
+
+\begin{document}
+    Bill of sale for: \hfill \yv{Customer}\\
+    Purchased: \hfill \yv{Date}\\
+    \begin{itemize}
+        \item[] ITEM \hfill PRICE
+        \yv{Items}             % the yaml variable
+        \begin{luacode*}
+            totalcost = pl.tablex.reduce('+',
+                pl.tablex.values(YAMLvars.varsvals['Items']), 0)
+            tex.print('\\item[] TOTAL:\\hfill'..tostring(totalcost))
+        \end{luacode*}
+    \end{itemize}
+\end{document}
+    \end{verbatim}
+
+    \subsection{The lua automation script}
+    \begin{verbatim}
+--automate.lua
+for f in io.popen('dir .'):lines() do  -- get all files and info in cwd
+    local i, j = f:find('%S*%.yaml')  --  find fnames
+    if i ~= nil then
+        f = f:sub(i,j) --  extract .yaml file name (no space in fname allowed)
+        os.execute('lualatex -output-format=pdf main.tex '.. f)
+                                    -- compile w/ yaml file as arg
+        local fnew = f:gsub('yaml', 'pdf') -- file name for output pdf
+        os.remove(fnew) -- delete if it exists already
+        os.rename('main.pdf', fnew) -- change main.pdf to same as yaml file name
+    end
+end
+    \end{verbatim}
+
+\subsection{The yaml data files}
+    \begin{verbatim}
+# sale1.yaml
+Customer: Someone Cold
+Date: January 2, 2021
+Items:
+    Toque: 12
+    Mitts: 5.6
+    Boots: 80
+
+# sale2.yaml
+Customer: Someone Warm
+Date: July 1, 2021
+Items:
+    Beer (24 pk): 24
+    Sunscreen: 5
+    Hat: 12
+    \end{verbatim}
+\end{document}
+


Property changes on: trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.tex
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.lua	2021-09-24 21:44:17 UTC (rev 60602)
@@ -0,0 +1,369 @@
+--% Kale Ewasiuk (kalekje at gmail.com)
+--% 2021-09-24
+--%
+--% Copyright (C) 2021 Kale Ewasiuk
+--%
+--% 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.
+
+
+-- tinyyaml license
+--MIT License
+--
+--Copyright (c) 2017 peposso
+--
+--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.
+
+
+
+YAMLvars = {} -- self table
+YAMLvars.xfm = {}
+YAMLvars.prc = {}
+
+YAMLvars.varsvals = {}
+YAMLvars.varspecs = {}
+
+YAMLvars.prcDefault = 'gdef'
+YAMLvars.dftDefault = nil
+YAMLvars.xfmDefault = {}
+
+YAMLvars.allowUndeclared = false
+
+YAMLvars.tabmidrule = 'hline'
+
+YAMLvars.yaml = require('tinyyaml')
+
+
+-- xfm functions (transforms) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+function YAMLvars.xfm.addxspace(var, val)
+    return val .. '\\xspace'
+end
+
+function YAMLvars.xfm.tab2arr(var, val)
+     return pl.array2d.from_table(val)
+end
+
+function YAMLvars.xfm.arrsort2ZA(var, val)
+    return pl.array2d.sortOP(val, pl.operator.strgt)
+end
+
+function YAMLvars.xfm.addrule2arr(var, val)
+     return pl.array2d.map_slice2(_1..'\\\\\\'.. YAMLvars.tabmidrule..' ', val, 1,-1,-2,-1) -- todo make gmidrule
+end
+
+function YAMLvars.xfm.arr2tabular(var, val)
+     return pl.array2d.toTeX(val)..'\\\\'
+end
+
+
+function YAMLvars.xfm.arr2itemize(var, val)
+     return pl.List(val):map('\\item '.._1):join(' ')
+end
+
+
+function YAMLvars.xfm.arrsortAZ(var, val)
+     return pl.List(val):sort(pl.operator.strlt)
+end
+
+function YAMLvars.xfm.arrsortZA(var, val)
+     return pl.List(val):sort(pl.operator.strgt)
+end
+
+local function complastname(a, b)
+    a = a:split(' ')
+    b = b:split(' ')
+    a = a[#a]
+    b = b[#b]
+    return a < b
+end
+
+function YAMLvars.xfm.arrsortlastnameAZ(var, val)
+    val = pl.List(val):sort(complastname)
+    return val
+end
+
+function YAMLvars.xfm.list2nl(var, val)
+    val = pl.array2d.map_slice1(_1..'\\\\', val, 1,-2)
+    return table.concat(val, '')
+    --return pl.tablex.reduce(_1.._2, val, '')
+end
+
+function YAMLvars.xfm.lb2nl(var, val)
+    val, _ = val:gsub('\n','\\\\ ')
+    return val
+end
+
+function YAMLvars.xfm.lowercase(var, val)
+    return val:lower()
+end
+
+
+function YAMLvars.xfm.markdown(var, val)
+     --return '\\begin{markdown} '..val..'\n \\end{markdown}'
+     help_wrt(val, md)
+     return [[begin markdown ..val..
+
+     par end markdown]]
+end
+
+
+
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+
+-- prc functions (processing) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+function YAMLvars.prc.gdef(var, val)
+    token.set_macro(var, val, 'global')
+end
+
+function YAMLvars.prc.yvdef(var, val)
+    token.set_macro('yv--'..var, val, 'global')
+end
+
+function YAMLvars.prc.PDFtitle(var, val)
+    tex.print('\\hypersetup{pdftitle={'..val..'}}')
+    --tex.print('\\setPDFtitle{'..val..'}')
+end
+
+function YAMLvars.prc.PDFauthor(var, val)
+    tex.print('\\hypersetup{pdfauthor={'..val..'}}')
+        --tex.print('\\setPDFauthor{'..val..'}')
+end
+
+
+function YAMLvars.prc.setheader(val, rl)
+    local _, count = string.gsub(val, '\\\\', '')
+    if count == 0 then
+        val = '{\\ }\\\\'..val
+    end
+    val = '\\setstretch{0.8}'..val
+    tex.print('\\'..rl..'ohead{'..val..'}')
+end
+
+function YAMLvars.prc.rhead(var, val)
+    YAMLvars.prc.setheader(val, 'r')
+end
+
+function YAMLvars.prc.lhead(var, val)
+    YAMLvars.prc.setheader(val, 'l')
+end
+
+function YAMLvars.prc.title(var, val)
+        token.set_macro('@title', val, 'global')
+end
+
+function YAMLvars.prc.author(var, val)
+        token.set_macro('@author', val, 'global')
+end
+
+function YAMLvars.prc.date(var, val)
+        token.set_macro('@date', val, 'global')
+end
+
+
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+
+
+local function prvcmd(cs, val) -- provide command via lua
+   if token.is_defined(cs) then
+        tex.print('\\PackageError{YAMLvars}{Variable '..cs..' already defined, could not declare}{}')
+    else
+        token.set_macro(cs, val, 'global')
+    end
+end
+
+local function deccmd(cs, def)
+    if def == nil then
+        prvcmd(cs, '\\PackageError{YAMLvars}{Variable "'..cs..'" was declared and used but, not set}{}')
+    else
+        prvcmd(cs, def)
+    end
+end
+
+local function getYAMLfile(y)
+    local f = io.open(y,"r")
+    if f ~= nil then
+        y = f:read('*a')
+        io.close(f)
+        return y
+    else
+        tex.print('\\PackageError{YAMLvars}{YAML file "'..y..'" not found}{}')
+        return 'YAMLvars: FileNotFound'
+    end
+end
+
+
+function YAMLvars.declareYAMLvarsStr(y)
+    local t = YAMLvars.yaml.parse(y)
+    for var, specs in pairs(t) do
+        YAMLvars.varspecs[var] = {xfm=YAMLvars.xfrmDefault,prc=YAMLvars.prcDefault,dft=YAMLvars.dftDefault}
+        if type(specs) == 'string' then
+            specs = {xfm={specs}}
+        end
+        if specs['xfm'] == nil then specs['xfm'] = {} end
+        for s, p in pairs(specs) do
+            if s == 'xfm' and type(p) ~= 'table' then p = {p} end
+            YAMLvars.varspecs[var][s] = p -- set property of var
+        end
+        if YAMLvars.varspecs[var]['prc'] == 'gdef' then
+            deccmd(var, YAMLvars.varspecs[var]['dft'])
+        elseif YAMLvars.varspecs[var]['prc'] == 'yvdef' then
+            deccmd('yv--'..var, YAMLvars.varspecs[var]['dft'])
+        end
+    end
+end
+
+function YAMLvars.declareYAMLvarsFile(y)
+    YAMLvars.declareYAMLvarsStr(getYAMLfile(y))
+end
+
+function YAMLvars.parseYAMLvarsStr(y)
+    YAMLvars.varsvals = YAMLvars.yaml.parse(y)
+    for var, val in pairs(YAMLvars.varsvals) do
+        if YAMLvars.varspecs[var] == nil then
+            if YAMLvars.allowUndeclared then
+                if YAMLvars.prcDefault == 'yvdef' then
+                    YAMLvars.prc.yvdef(var, val)
+                else
+                    prvcmd(var, val)
+                end
+             else
+                tex.print('\\PackageError{YAMLvars}{Variable "'..var..'" set but not declared}{}')
+            end
+        else
+            --if string.sub(val, 1, 2) == '~' then -- todo
+            --    val = t[string.sub(val, 2, #val)]  -- use other val
+            --end
+            for _, func in pairs(YAMLvars.varspecs[var]['xfm']) do --apply cleaning functions
+                local f = YAMLvars.xfm[func]
+                if f == nil then
+                    tex.print('\\PackageWarning{YAMLvars}{xfm function "'..func..'" not defined, skipping}{}')
+                else
+                    val = f(var, val)
+                end
+            end
+            f = YAMLvars.prc[YAMLvars.varspecs[var]['prc']]
+            if f == nil then
+                tex.print('\\PackageError{YAMLvars}{prc function "'..YAMLvars.varspecs[var]['prc']..'" not defined}{}')
+            end
+            f(var, val) -- prc the value of the variable
+        end
+    end
+end
+
+function YAMLvars.parseYAMLvarsFile(y)
+    YAMLvars.parseYAMLvarsStr(getYAMLfile(y))
+end
+
+
+
+function YAMLvars.print_varspecs()
+    local pretty = require('pl.pretty')
+    texio.write_nl('VVVVVV Var specifications:')
+    texio.write_nl(pretty.write(YAMLvars.varspecs))
+end
+
+
+
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+-- https://tex.stackexchange.com/questions/38150/in-lualatex-how-do-i-pass-the-content-of-an-environment-to-lua-verbatim
+recordedbuf = ""
+function readbuf(buf)
+    i,j = string.find(buf, '\\end{%w+}')
+     if i==nil then -- if not ending an environment
+        recordedbuf = recordedbuf .. buf .. "\n"
+        return ""
+    else
+        return nil
+    end
+end
+
+function startrecording()
+    recordedbuf = ""
+    luatexbase.add_to_callback('process_input_buffer', readbuf, 'readbuf')
+end
+
+function stoprecording()
+    luatexbase.remove_from_callback('process_input_buffer', 'readbuf')
+    recordedbuf = recordedbuf:gsub("\\end{%w+}\n","")
+end
+
+
+
+function YAMLvars.doYAMLfiles(t)
+    if #t == 2 then
+        YAMLvars.declareYAMLvarsFile(t[1])
+        YAMLvars.parseYAMLvarsFile(t[2])
+    elseif #t == 1 then
+        YAMLvars.parseYAMLvarsFile(t[1])
+    else
+        tex.print('\\PackageWarning{YAMLvars}{No .yaml files found in CLI args"}{}')
+    end
+end
+
+function YAMLvars.getYAMLcli()
+    local t = {}
+    if arg then
+      for i,v in pairs(arg) do
+          if v:find('.*%.yaml$') then
+              t[#t+1] = v
+          end
+      end
+    end
+    help_wrt(t)
+    return t
+ end
+
+
+return YAMLvars
+
+
+
+  --clean = clean or true
+    --if clean then -- clean first part of yaml string
+    --    y = clean_tex_spaces(y)
+    --end
+--local function clean_tex_spaces(s)
+--    help_wrt(s)
+--    if s:sub(1,2) == '%s' then
+--        s, _ = s:gsub('%s+','',1)
+--    end
+--    s, _ = s:gsub('\\par ','\n\n')
+--    return s
+--end
\ No newline at end of file


Property changes on: trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.sty	2021-09-24 21:44:17 UTC (rev 60602)
@@ -0,0 +1,59 @@
+% Kale Ewasiuk (kalekje at gmail.com)
+% 2021-09-24
+%
+% Copyright (C) 2021 Kale Ewasiuk
+%
+% 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.
+
+
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{YAMLvars}[2021]
+
+\RequirePackage{luacode}
+
+\luadirect{YAMLvars = require('YAMLvars')}
+
+\DeclareOption{useyv}{
+        \gdef\yv#1{\csname yv--#1\endcsname}
+        \luadirect{YAMLvars.prcDefault = 'yvdef'}
+}
+\DeclareOption{parseCLI}{
+      \AtBeginDocument{\luadirect{YAMLvars.doYAMLfiles(YAMLvars.getYAMLcli())}}
+}
+\DeclareOption{allowundeclared}{
+        \luadirect{YAMLvars.allowUndeclared = true}
+}
+
+\DeclareOption*{\PackageWarning{YAMLvars}{Unknown option: '\CurrentOption'}{}}
+\ProcessOptions\relax
+
+
+\newcommand{\declareYAMLvarsFile}[1]{\luadirect{YAMLvars.declareYAMLvarsFile(\luastring{#1})}}
+\newcommand{\parseYAMLvarsFile}[1]{\luadirect{YAMLvars.parseYAMLvarsFile(\luastring{#1})}}
+
+
+\newenvironment{declareYAMLvars}{\luadirect{startrecording()}}{\luadirect{stoprecording()}
+\luadirect{YAMLvars.declareYAMLvarsStr(recordedbuf)}
+}
+
+\newenvironment{parseYAMLvars}{\luadirect{startrecording()}}{\luadirect{stoprecording()}
+\luadirect{YAMLvars.parseYAMLvarsStr(recordedbuf)}
+}
\ No newline at end of file


Property changes on: trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/lualatex/yamlvars/tinyyaml.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/yamlvars/tinyyaml.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/yamlvars/tinyyaml.lua	2021-09-24 21:44:17 UTC (rev 60602)
@@ -0,0 +1,816 @@
+-------------------------------------------------------------------------------
+-- tinyyaml - YAML subset parser
+-------------------------------------------------------------------------------
+
+local table = table
+local string = string
+local schar = string.char
+local ssub, gsub = string.sub, string.gsub
+local sfind, smatch = string.find, string.match
+local tinsert, tconcat, tremove = table.insert, table.concat, table.remove
+local setmetatable = setmetatable
+local pairs = pairs
+local type = type
+local tonumber = tonumber
+local math = math
+local getmetatable = getmetatable
+local error = error
+
+local UNESCAPES = {
+  ['0'] = "\x00", z = "\x00", N    = "\x85",
+  a = "\x07",     b = "\x08", t    = "\x09",
+  n = "\x0a",     v = "\x0b", f    = "\x0c",
+  r = "\x0d",     e = "\x1b", ['\\'] = '\\',
+};
+
+-------------------------------------------------------------------------------
+-- utils
+local function select(list, pred)
+  local selected = {}
+  for i = 0, #list do
+    local v = list[i]
+    if v and pred(v, i) then
+      tinsert(selected, v)
+    end
+  end
+  return selected
+end
+
+local function startswith(haystack, needle)
+  return ssub(haystack, 1, #needle) == needle
+end
+
+local function ltrim(str)
+  return smatch(str, "^%s*(.-)$")
+end
+
+local function rtrim(str)
+  return smatch(str, "^(.-)%s*$")
+end
+
+local function trim(str)
+  return smatch(str, "^%s*(.-)%s*$")
+end
+
+-------------------------------------------------------------------------------
+-- Implementation.
+--
+local class = {__meta={}}
+function class.__meta.__call(cls, ...)
+  local self = setmetatable({}, cls)
+  if cls.__init then
+    cls.__init(self, ...)
+  end
+  return self
+end
+
+function class.def(base, typ, cls)
+  base = base or class
+  local mt = {__metatable=base, __index=base}
+  for k, v in pairs(base.__meta) do mt[k] = v end
+  cls = setmetatable(cls or {}, mt)
+  cls.__index = cls
+  cls.__metatable = cls
+  cls.__type = typ
+  cls.__meta = mt
+  return cls
+end
+
+
+local types = {
+  null = class:def('null'),
+  map = class:def('map'),
+  omap = class:def('omap'),
+  pairs = class:def('pairs'),
+  set = class:def('set'),
+  seq = class:def('seq'),
+  timestamp = class:def('timestamp'),
+}
+
+local Null = types.null
+function Null.__tostring() return 'yaml.null' end
+function Null.isnull(v)
+  if v == nil then return true end
+  if type(v) == 'table' and getmetatable(v) == Null then return true end
+  return false
+end
+local null = Null()
+
+function types.timestamp:__init(y, m, d, h, i, s, f, z)
+  self.year = tonumber(y)
+  self.month = tonumber(m)
+  self.day = tonumber(d)
+  self.hour = tonumber(h or 0)
+  self.minute = tonumber(i or 0)
+  self.second = tonumber(s or 0)
+  if type(f) == 'string' and sfind(f, '^%d+$') then
+    self.fraction = tonumber(f) * math.pow(10, 3 - #f)
+  elseif f then
+    self.fraction = f
+  else
+    self.fraction = 0
+  end
+  self.timezone = z
+end
+
+function types.timestamp:__tostring()
+  return string.format(
+    '%04d-%02d-%02dT%02d:%02d:%02d.%03d%s',
+    self.year, self.month, self.day,
+    self.hour, self.minute, self.second, self.fraction,
+    self:gettz())
+end
+
+function types.timestamp:gettz()
+  if not self.timezone then
+    return ''
+  end
+  if self.timezone == 0 then
+    return 'Z'
+  end
+  local sign = self.timezone > 0
+  local z = sign and self.timezone or -self.timezone
+  local zh = math.floor(z)
+  local zi = (z - zh) * 60
+  return string.format(
+    '%s%02d:%02d', sign and '+' or '-', zh, zi)
+end
+
+
+local function countindent(line)
+  local _, j = sfind(line, '^%s+')
+  if not j then
+    return 0, line
+  end
+  return j, ssub(line, j+1)
+end
+
+local function parsestring(line, stopper)
+  stopper = stopper or ''
+  local q = ssub(line, 1, 1)
+  if q == ' ' or q == '\t' then
+    return parsestring(ssub(line, 2))
+  end
+  if q == "'" then
+    local i = sfind(line, "'", 2, true)
+    if not i then
+      return nil, line
+    end
+    return ssub(line, 2, i-1), ssub(line, i+1)
+  end
+  if q == '"' then
+    local i, buf = 2, ''
+    while i < #line do
+      local c = ssub(line, i, i)
+      if c == '\\' then
+        local n = ssub(line, i+1, i+1)
+        if UNESCAPES[n] ~= nil then
+          buf = buf..UNESCAPES[n]
+        elseif n == 'x' then
+          local h = ssub(i+2,i+3)
+          if sfind(h, '^[0-9a-fA-F]$') then
+            buf = buf..schar(tonumber(h, 16))
+            i = i + 2
+          else
+            buf = buf..'x'
+          end
+        else
+          buf = buf..n
+        end
+        i = i + 1
+      elseif c == q then
+        break
+      else
+        buf = buf..c
+      end
+      i = i + 1
+    end
+    return buf, ssub(line, i+1)
+  end
+  if q == '{' or q == '[' then  -- flow style
+    return nil, line
+  end
+  if q == '|' or q == '>' then  -- block
+    return nil, line
+  end
+  if q == '-' or q == ':' then
+    if ssub(line, 2, 2) == ' ' or ssub(line, 2, 2) == '\n' or #line == 1 then
+      return nil, line
+    end
+  end
+  local buf = ''
+  while #line > 0 do
+    local c = ssub(line, 1, 1)
+    if sfind(stopper, c, 1, true) then
+      break
+    elseif c == ':' and (ssub(line, 2, 2) == ' ' or ssub(line, 2, 2) == '\n' or #line == 1) then
+      break
+    elseif c == '#' and (ssub(buf, #buf, #buf) == ' ') then
+      break
+    else
+      buf = buf..c
+    end
+    line = ssub(line, 2)
+  end
+  return rtrim(buf), line
+end
+
+local function isemptyline(line)
+  return line == '' or sfind(line, '^%s*$') or sfind(line, '^%s*#')
+end
+
+local function equalsline(line, needle)
+  return startswith(line, needle) and isemptyline(ssub(line, #needle+1))
+end
+
+local function compactifyemptylines(lines)
+  -- Appends empty lines as "\n" to the end of the nearest preceding non-empty line
+  local compactified = {}
+  local lastline = {}
+  for i = 1, #lines do
+    local line = lines[i]
+    if isemptyline(line) then
+      if #compactified > 0 and i < #lines then
+        tinsert(lastline, "\n")
+      end
+    else
+      if #lastline > 0 then
+        tinsert(compactified, tconcat(lastline, ""))
+      end
+      lastline = {line}
+    end
+  end
+  if #lastline > 0 then
+    tinsert(compactified, tconcat(lastline, ""))
+  end
+  return compactified
+end
+
+local function checkdupekey(map, key)
+  if map[key] ~= nil then
+    -- print("found a duplicate key '"..key.."' in line: "..line)
+    local suffix = 1
+    while map[key..'_'..suffix] do
+      suffix = suffix + 1
+    end
+    key = key ..'_'..suffix
+  end
+  return key
+end
+
+local function parseflowstyle(line, lines)
+  local stack = {}
+  while true do
+    if #line == 0 then
+      if #lines == 0 then
+        break
+      else
+        line = tremove(lines, 1)
+      end
+    end
+    local c = ssub(line, 1, 1)
+    if c == '#' then
+      line = ''
+    elseif c == ' ' or c == '\t' or c == '\r' or c == '\n' then
+      line = ssub(line, 2)
+    elseif c == '{' or c == '[' then
+      tinsert(stack, {v={},t=c})
+      line = ssub(line, 2)
+    elseif c == ':' then
+      local s = tremove(stack)
+      tinsert(stack, {v=s.v, t=':'})
+      line = ssub(line, 2)
+    elseif c == ',' then
+      local value = tremove(stack)
+      if value.t == ':' or value.t == '{' or value.t == '[' then error() end
+      if stack[#stack].t == ':' then
+        -- map
+        local key = tremove(stack)
+        key.v = checkdupekey(stack[#stack].v, key.v)
+        stack[#stack].v[key.v] = value.v
+      elseif stack[#stack].t == '{' then
+        -- set
+        stack[#stack].v[value.v] = true
+      elseif stack[#stack].t == '[' then
+        -- seq
+        tinsert(stack[#stack].v, value.v)
+      end
+      line = ssub(line, 2)
+    elseif c == '}' then
+      if stack[#stack].t == '{' then
+        if #stack == 1 then break end
+        stack[#stack].t = '}'
+        line = ssub(line, 2)
+      else
+        line = ','..line
+      end
+    elseif c == ']' then
+      if stack[#stack].t == '[' then
+        if #stack == 1 then break end
+        stack[#stack].t = ']'
+        line = ssub(line, 2)
+      else
+        line = ','..line
+      end
+    else
+      local s, rest = parsestring(line, ',{}[]')
+      if not s then
+        error('invalid flowstyle line: '..line)
+      end
+      tinsert(stack, {v=s, t='s'})
+      line = rest
+    end
+  end
+  return stack[1].v, line
+end
+
+local function parseblockstylestring(line, lines, indent)
+  if #lines == 0 then
+    error("failed to find multi-line scalar content")
+  end
+  local s = {}
+  local firstindent = -1
+  local endline = -1
+  for i = 1, #lines do
+    local ln = lines[i]
+    local idt = countindent(ln)
+    if idt <= indent then
+      break
+    end
+    if ln == '' then
+      tinsert(s, '')
+    else
+      if firstindent == -1 then
+        firstindent = idt
+      elseif idt < firstindent then
+        break
+      end
+      tinsert(s, ssub(ln, firstindent + 1))
+    end
+    endline = i
+  end
+
+  local striptrailing = true
+  local sep = '\n'
+  local newlineatend = true
+  if line == '|' then
+    striptrailing = true
+    sep = '\n'
+    newlineatend = true
+  elseif line == '|+' then
+    striptrailing = false
+    sep = '\n'
+    newlineatend = true
+  elseif line == '|-' then
+    striptrailing = true
+    sep = '\n'
+    newlineatend = false
+  elseif line == '>' then
+    striptrailing = true
+    sep = ' '
+    newlineatend = true
+  elseif line == '>+' then
+    striptrailing = false
+    sep = ' '
+    newlineatend = true
+  elseif line == '>-' then
+    striptrailing = true
+    sep = ' '
+    newlineatend = false
+  else
+    error('invalid blockstyle string:'..line)
+  end
+  local _, eonl = s[#s]:gsub('\n', '\n')
+  s[#s] = rtrim(s[#s])
+  if striptrailing then
+    eonl = 0
+  end
+  if newlineatend then
+    eonl = eonl + 1
+  end
+  for i = endline, 1, -1 do
+    tremove(lines, i)
+  end
+  return tconcat(s, sep)..string.rep('\n', eonl)
+end
+
+local function parsetimestamp(line)
+  local _, p1, y, m, d = sfind(line, '^(%d%d%d%d)%-(%d%d)%-(%d%d)')
+  if not p1 then
+    return nil, line
+  end
+  if p1 == #line then
+    return types.timestamp(y, m, d), ''
+  end
+  local _, p2, h, i, s = sfind(line, '^[Tt ](%d+):(%d+):(%d+)', p1+1)
+  if not p2 then
+    return types.timestamp(y, m, d), ssub(line, p1+1)
+  end
+  if p2 == #line then
+    return types.timestamp(y, m, d, h, i, s), ''
+  end
+  local _, p3, f = sfind(line, '^%.(%d+)', p2+1)
+  if not p3 then
+    p3 = p2
+    f = 0
+  end
+  local zc = ssub(line, p3+1, p3+1)
+  local _, p4, zs, z = sfind(line, '^ ?([%+%-])(%d+)', p3+1)
+  if p4 then
+    z = tonumber(z)
+    local _, p5, zi = sfind(line, '^:(%d+)', p4+1)
+    if p5 then
+      z = z + tonumber(zi) / 60
+    end
+    z = zs == '-' and -tonumber(z) or tonumber(z)
+  elseif zc == 'Z' then
+    p4 = p3 + 1
+    z = 0
+  else
+    p4 = p3
+    z = false
+  end
+  return types.timestamp(y, m, d, h, i, s, f, z), ssub(line, p4+1)
+end
+
+local function parsescalar(line, lines, indent)
+  line = trim(line)
+  line = gsub(line, '^%s*#.*$', '')  -- comment only -> ''
+  line = gsub(line, '^%s*', '')  -- trim head spaces
+
+  if line == '' or line == '~' then
+    return null
+  end
+
+  local ts, _ = parsetimestamp(line)
+  if ts then
+    return ts
+  end
+
+  local s, _ = parsestring(line)
+  -- startswith quote ... string
+  -- not startswith quote ... maybe string
+  if s and (startswith(line, '"') or startswith(line, "'")) then
+    return s
+  end
+
+  if startswith('!', line) then  -- unexpected tagchar
+    error('unsupported line: '..line)
+  end
+
+  if equalsline(line, '{}') then
+    return {}
+  end
+  if equalsline(line, '[]') then
+    return {}
+  end
+
+  if startswith(line, '{') or startswith(line, '[') then
+    return parseflowstyle(line, lines)
+  end
+
+  if startswith(line, '|') or startswith(line, '>') then
+    return parseblockstylestring(line, lines, indent)
+  end
+
+  -- Regular unquoted string
+  line = gsub(line, '%s*#.*$', '')  -- trim tail comment
+  local v = line
+  if v == 'null' or v == 'Null' or v == 'NULL'then
+    return null
+  elseif v == 'true' or v == 'True' or v == 'TRUE' then
+    return true
+  elseif v == 'false' or v == 'False' or v == 'FALSE' then
+    return false
+  elseif v == '.inf' or v == '.Inf' or v == '.INF' then
+    return math.huge
+  elseif v == '+.inf' or v == '+.Inf' or v == '+.INF' then
+    return math.huge
+  elseif v == '-.inf' or v == '-.Inf' or v == '-.INF' then
+    return -math.huge
+  elseif v == '.nan' or v == '.NaN' or v == '.NAN' then
+    return 0 / 0
+  elseif sfind(v, '^[%+%-]?[0-9]+$') or sfind(v, '^[%+%-]?[0-9]+%.$')then
+    return tonumber(v)  -- : int
+  elseif sfind(v, '^[%+%-]?[0-9]+%.[0-9]+$') then
+    return tonumber(v)
+  end
+  return s or v
+end
+
+local parsemap;  -- : func
+
+local function parseseq(line, lines, indent)
+  local seq = setmetatable({}, types.seq)
+  if line ~= '' then
+    error()
+  end
+  while #lines > 0 do
+    -- Check for a new document
+    line = lines[1]
+    if startswith(line, '---') then
+      while #lines > 0 and not startswith(lines, '---') do
+        tremove(lines, 1)
+      end
+      return seq
+    end
+
+    -- Check the indent level
+    local level = countindent(line)
+    if level < indent then
+      return seq
+    elseif level > indent then
+      error("found bad indenting in line: ".. line)
+    end
+
+    local i, j = sfind(line, '%-%s+')
+    if not i then
+      i, j = sfind(line, '%-$')
+      if not i then
+        return seq
+      end
+    end
+    local rest = ssub(line, j+1)
+
+    if sfind(rest, '^[^\'\"%s]*:%s*$') or sfind(rest, '^[^\'\"%s]*:%s+.') then
+      -- Inline nested hash
+      -- There are two patterns need to match as inline nested hash
+      --   first one should have no other characters except whitespace after `:`
+      --   and the second one should have characters besides whitespace after `:`
+      --
+      --  value:
+      --    - foo:
+      --        bar: 1
+      --
+      -- and
+      --
+      --  value:
+      --    - foo: bar
+      --
+      -- And there is one pattern should not be matched, where there is no space after `:`
+      --   in below, `foo:bar` should be parsed into a single string
+      --
+      -- value:
+      --   - foo:bar
+      local indent2 = j
+      lines[1] = string.rep(' ', indent2)..rest
+      tinsert(seq, parsemap('', lines, indent2))
+    elseif sfind(rest, '^%-%s+') then
+      -- Inline nested seq
+      local indent2 = j
+      lines[1] = string.rep(' ', indent2)..rest
+      tinsert(seq, parseseq('', lines, indent2))
+    elseif isemptyline(rest) then
+      tremove(lines, 1)
+      if #lines == 0 then
+        tinsert(seq, null)
+        return seq
+      end
+      if sfind(lines[1], '^%s*%-') then
+        local nextline = lines[1]
+        local indent2 = countindent(nextline)
+        if indent2 == indent then
+          -- Null seqay entry
+          tinsert(seq, null)
+        else
+          tinsert(seq, parseseq('', lines, indent2))
+        end
+      else
+        -- - # comment
+        --   key: value
+        local nextline = lines[1]
+        local indent2 = countindent(nextline)
+        tinsert(seq, parsemap('', lines, indent2))
+      end
+    elseif rest then
+      -- Array entry with a value
+      tremove(lines, 1)
+      tinsert(seq, parsescalar(rest, lines))
+    end
+  end
+  return seq
+end
+
+local function parseset(line, lines, indent)
+  if not isemptyline(line) then
+    error('not seq line: '..line)
+  end
+  local set = setmetatable({}, types.set)
+  while #lines > 0 do
+    -- Check for a new document
+    line = lines[1]
+    if startswith(line, '---') then
+      while #lines > 0 and not startswith(lines, '---') do
+        tremove(lines, 1)
+      end
+      return set
+    end
+
+    -- Check the indent level
+    local level = countindent(line)
+    if level < indent then
+      return set
+    elseif level > indent then
+      error("found bad indenting in line: ".. line)
+    end
+
+    local i, j = sfind(line, '%?%s+')
+    if not i then
+      i, j = sfind(line, '%?$')
+      if not i then
+        return set
+      end
+    end
+    local rest = ssub(line, j+1)
+
+    if sfind(rest, '^[^\'\"%s]*:') then
+      -- Inline nested hash
+      local indent2 = j
+      lines[1] = string.rep(' ', indent2)..rest
+      set[parsemap('', lines, indent2)] = true
+    elseif sfind(rest, '^%s+$') then
+      tremove(lines, 1)
+      if #lines == 0 then
+        tinsert(set, null)
+        return set
+      end
+      if sfind(lines[1], '^%s*%?') then
+        local indent2 = countindent(lines[1])
+        if indent2 == indent then
+          -- Null array entry
+          set[null] = true
+        else
+          set[parseseq('', lines, indent2)] = true
+        end
+      end
+
+    elseif rest then
+      tremove(lines, 1)
+      set[parsescalar(rest, lines)] = true
+    else
+      error("failed to classify line: "..line)
+    end
+  end
+  return set
+end
+
+function parsemap(line, lines, indent)
+  if not isemptyline(line) then
+    error('not map line: '..line)
+  end
+  local map = setmetatable({}, types.map)
+  while #lines > 0 do
+    -- Check for a new document
+    line = lines[1]
+    if startswith(line, '---') then
+      while #lines > 0 and not startswith(lines, '---') do
+        tremove(lines, 1)
+      end
+      return map
+    end
+
+    -- Check the indent level
+    local level, _ = countindent(line)
+    if level < indent then
+      return map
+    elseif level > indent then
+      error("found bad indenting in line: ".. line)
+    end
+
+    -- Find the key
+    local key
+    local s, rest = parsestring(line)
+
+    -- Quoted keys
+    if s and startswith(rest, ':') then
+      local sc = parsescalar(s, {}, 0)
+      if sc and type(sc) ~= 'string' then
+        key = sc
+      else
+        key = s
+      end
+      line = ssub(rest, 2)
+    else
+      error("failed to classify line: "..line)
+    end
+
+    key = checkdupekey(map, key)
+    line = ltrim(line)
+
+    if ssub(line, 1, 1) == '!' then
+      -- ignore type
+      local rh = ltrim(ssub(line, 3))
+      local typename = smatch(rh, '^!?[^%s]+')
+      line = ltrim(ssub(rh, #typename+1))
+    end
+
+    if not isemptyline(line) then
+      tremove(lines, 1)
+      line = ltrim(line)
+      map[key] = parsescalar(line, lines, indent)
+    else
+      -- An indent
+      tremove(lines, 1)
+      if #lines == 0 then
+        map[key] = null
+        return map;
+      end
+      if sfind(lines[1], '^%s*%-') then
+        local indent2 = countindent(lines[1])
+        map[key] = parseseq('', lines, indent2)
+      elseif sfind(lines[1], '^%s*%?') then
+        local indent2 = countindent(lines[1])
+        map[key] = parseset('', lines, indent2)
+      else
+        local indent2 = countindent(lines[1])
+        if indent >= indent2 then
+          -- Null hash entry
+          map[key] = null
+        else
+          map[key] = parsemap('', lines, indent2)
+        end
+      end
+    end
+  end
+  return map
+end
+
+
+-- : (list<str>)->dict
+local function parsedocuments(lines)
+  lines = compactifyemptylines(lines)
+
+  if sfind(lines[1], '^%%YAML') then tremove(lines, 1) end
+
+  local root = {}
+  local in_document = false
+  while #lines > 0 do
+    local line = lines[1]
+    -- Do we have a document header?
+    local docright;
+    if sfind(line, '^%-%-%-') then
+      -- Handle scalar documents
+      docright = ssub(line, 4)
+      tremove(lines, 1)
+      in_document = true
+    end
+    if docright then
+      if (not sfind(docright, '^%s+$') and
+          not sfind(docright, '^%s+#')) then
+        tinsert(root, parsescalar(docright, lines))
+      end
+    elseif #lines == 0 or startswith(line, '---') then
+      -- A naked document
+      tinsert(root, null)
+      while #lines > 0 and not sfind(lines[1], '---') do
+        tremove(lines, 1)
+      end
+      in_document = false
+    -- XXX The final '-+$' is to look for -- which ends up being an
+    -- error later.
+    elseif not in_document and #root > 0 then
+      -- only the first document can be explicit
+      error('parse error: '..line)
+    elseif sfind(line, '^%s*%-') then
+      -- An array at the root
+      tinsert(root, parseseq('', lines, 0))
+    elseif sfind(line, '^%s*[^%s]') then
+      -- A hash at the root
+      local level = countindent(line)
+      tinsert(root, parsemap('', lines, level))
+    else
+      -- Shouldn't get here.  @lines have whitespace-only lines
+      -- stripped, and previous match is a line with any
+      -- non-whitespace.  So this clause should only be reachable via
+      -- a perlbug where \s is not symmetric with \S
+
+      -- uncoverable statement
+      error('parse error: '..line)
+    end
+  end
+  if #root > 1 and Null.isnull(root[1]) then
+    tremove(root, 1)
+    return root
+  end
+  return root
+end
+
+--- Parse yaml string into table.
+local function parse(source)
+  local lines = {}
+  for line in string.gmatch(source .. '\n', '(.-)\r?\n') do
+    tinsert(lines, line)
+  end
+
+  local docs = parsedocuments(lines)
+  if #docs == 1 then
+    return docs[1]
+  end
+
+  return docs
+end
+
+return {
+  version = 0.1,
+  parse = parse,
+}


Property changes on: trunk/Master/texmf-dist/tex/lualatex/yamlvars/tinyyaml.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/tlpkg/bin/tlpkg-ctan-check
===================================================================
--- trunk/Master/tlpkg/bin/tlpkg-ctan-check	2021-09-24 21:42:36 UTC (rev 60601)
+++ trunk/Master/tlpkg/bin/tlpkg-ctan-check	2021-09-24 21:44:17 UTC (rev 60602)
@@ -859,7 +859,7 @@
     xoptarg xpatch xpeek xpiano xpicture xpinyin xprintlen xpunctuate
     xq xsavebox xsim xskak xstring xtab xtuthesis xunicode xurl
     xwatermark xyling xymtex xypic xypic-tut-pt xytree
-  yafoot yagusylo yaletter yannisgr yathesis yax yazd-thesis
+  yafoot yagusylo yaletter yamlvars yannisgr yathesis yax yazd-thesis
     ycbook ydoc yfonts yfonts-t1 yhmath
     yinit-otf york-thesis youngtab yplan yquant ytableau
   zapfchan zapfding zbmath-review-template zebra-goodies zed-csp

Modified: trunk/Master/tlpkg/libexec/ctan2tds
===================================================================
--- trunk/Master/tlpkg/libexec/ctan2tds	2021-09-24 21:42:36 UTC (rev 60601)
+++ trunk/Master/tlpkg/libexec/ctan2tds	2021-09-24 21:44:17 UTC (rev 60602)
@@ -2345,6 +2345,7 @@
  'xindex',      '(unicode|xindex)-.*\.lua$',
  'xlop',        'xlop\.(tex|sty)',      # no manual.sty
  'xstring',     'xstring\.tex|' . $standardtex,
+ 'yamlvars',	'\.lua|' . $standardtex,
  'yathesis',    '\.(sty|cls)$',         # no .cfg
  'yax',         '(t-)?yax\.(sty|tex)|' . $standardtex,
  'yquant',	'\.sty|yquant-.*[^c]\.tex', # not yquant-doc.{tex,pdf}

Modified: trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
===================================================================
--- trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc	2021-09-24 21:42:36 UTC (rev 60601)
+++ trunk/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc	2021-09-24 21:44:17 UTC (rev 60602)
@@ -71,3 +71,4 @@
 depend stricttex
 depend typewriter
 depend uninormalize
+depend yamlvars

Added: trunk/Master/tlpkg/tlpsrc/yamlvars.tlpsrc
===================================================================


More information about the tex-live-commits mailing list.