% Kale Ewasiuk (kalekje@gmail.com) % 2021-12-15 % 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[11pt,parskip=half]{scrartcl} \setlength{\parindent}{0ex} \newcommand{\llcmd}[1]{\leavevmode\llap{\texttt{\detokenize{#1}\ }}} \newcommand{\cmd}[1]{\texttt{\detokenize{#1}}} \newcommand{\qcmd}[1]{``\cmd{#1}''} \usepackage{url} \usepackage{showexpl} \lstset{explpreset={justification=\raggedright,pos=r,wide=true}} \setlength\ResultBoxRule{0mm} \lstset{ language=[LaTeX]TeX, basicstyle=\ttfamily\small, commentstyle=\ttfamily\small\color{gray}, frame=none, numbers=left, numberstyle=\ttfamily\small\color{gray}, prebreak=\raisebox{0ex}[0ex][0ex]{\color{gray}\ensuremath{\hookleftarrow}}, extendedchars=true, breaklines=true, tabsize=4, } \addtokomafont{title}{\raggedright} \addtokomafont{author}{\raggedright} \addtokomafont{date}{\raggedright} \author{Kale Ewasiuk (\url{kalekje@gmail.com})} \usepackage[yyyymmdd]{datetime}\renewcommand{\dateseparator}{--} \date{\today} \usepackage{YAMLvars} \title{YAMLvars} \subtitle{a YAML variable parser for LuaLaTeX} \begin{document} \maketitle 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\{\}} command. This also allows numbers and symbols in the variable names. Note that internally, the variables are stored in the command sequence \texttt{yv--}. \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} To change how a variable is declared (initialize), you can modify or add functions in \texttt{YAMLvars.dec} table, where the index is the same as the \texttt{prc} name. This function accepts two variables, the var name, and the default value set by dft. For lengths and toggles (from etoolbox), these functions are used to initialize lengths with newlength and newtoggle. \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} If you want to run some Lua code and write in your YAML file (weird idea, but maybe useful for one-off functions), you can do so by specifying a transform function with an \texttt{=} in it to make a lambda function. For example, a \texttt{xfm} equal to ``\texttt{= '---'..x..'---'}'' would surround your YAML variable's value with em-dashes. You can access the variable name with this lambda function with \texttt{v}. If you want to just execute code (instead of settings \texttt{x = }, use \texttt{/}). \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}