\documentclass{ltxdoc} \usepackage{hyperref} \EnableCrossrefs \CodelineIndex \RecordChanges \usepackage{mdframed} \usepackage{minted} \usepackage{luakeys-debug} \usepackage{multicol} \usepackage{luacode} \usepackage{syntax} \usemintedstyle{friendly} \BeforeBeginEnvironment{minted}{\begin{mdframed}} \AfterEndEnvironment{minted}{\end{mdframed}} \setminted{ breaklines=true, fontsize=\footnotesize, } \def\lua#1{\mintinline{lua}|#1|} \begin{document} \providecommand*{\url}{\texttt} \title{The \textsf{luakeys} package} \author{% Josef Friedrich\\% \url{josef@friedrich.rocks}\\% \href{https://github.com/Josef-Friedrich/luakeys}{github.com/Josef-Friedrich/luakeys}% } \date{v0.2 from 2021/09/19} \maketitle \vfill %\luakeysdebug{level1={level2={level3={dim=1cm,bool=true,num=-1e-03,str=lua}}}} \begin{minted}{lua} local luakeys = require('luakeys') local kv = luakeys.parse('level1={level2={level3={dim=1cm,bool=true,num=-1e-03,str=lua}}}') luakeys.print(kv) \end{minted} \noindent Result: \begin{center} \begin{minted}{lua} { ['level1'] = { ['level2'] = { ['level3'] = { ['dim'] = 1864679, ['bool'] = true, ['num'] = -0.001 ['str'] = 'lua', } } } } \end{minted} \end{center} \vfill \strut \newpage \tableofcontents \newpage \section{Introduction} \noindent |luakeys| is a Lua module that can parse key-value options like the \TeX{} packages \href{https://www.ctan.org/pkg/keyval}{keyval}, \href{https://www.ctan.org/pkg/kvsetkeys}{kvsetkeys}, \href{https://www.ctan.org/pkg/kvoptions}{kvoptions}, \href{https://www.ctan.org/pkg/xkeyval}{xkeyval}, \href{https://www.ctan.org/pkg/pgfkeys}{pgfkeys} etc. do. |luakeys|, however, accompilshes this task entirely, by using the Lua language and doesn’t rely on \TeX{}. Therefore this package can only be used with the \TeX{} engine Lua\TeX{}. Since |luakeys| uses \href{http://www.inf.puc-rio.br/~roberto/lpeg/}{LPeg}, the parsing mechanism should be pretty robust. The TUGboat article \href{http://www.tug.org/tugboat/tb30-1/tb94wright-keyval.pdf} {“Implementing key–value input: An introduction” (Volume 30 (2009), No. 1)} by Joseph Wright and Christian Feuersänger gives a good overview of the available key-value packages. This package would not be possible without the article \href{https://tug.org/TUGboat/tb40-2/tb125menke-lpeg.pdf} {Parsing complex data formats in LuaTEX with LPEG (Volume 40 (2019), No. 2)}. %----------------------------------------------------------------------- % %----------------------------------------------------------------------- \clearpage \section{Usage} \begin{minted}{latex} \documentclass{article} \directlua{ luakeys = require('luakeys') } \newcommand{\helloworld}[2][]{ \directlua{ local keys = luakeys.parse('\luaescapestring{\unexpanded{#1}}') luakeys.print(keys) local marg = '#2' tex.print(keys.greeting .. ', ' .. marg .. keys.punctuation) } } \begin{document} \helloworld[greeting=hello,punctuation=!]{world} \end{document} \end{minted} \section{Syntax of the recognized key-value format} %% % %% \subsection{A attempt to put the syntax into words} A key-value pair is definied by an equal sign (\texttt{key=value}). Several key-value pairs or values without keys are lined up with commas (\texttt{key=value,value}) and build a key-value list. Curly brackets can be used to create a recursive data structure of nested key-value lists (\texttt{level1=\{level2=\{key=value,value\}\}}). %% % %% \subsection{An (incomplete) attempt to put the syntax into the Extended Backus-Naur Form} \begin{grammar} ::= | ::= ( | ) [ `,' ] ::== `{' `}' ::= \alt \alt \alt \alt ::= `-' | `+' ::= `0' | `1' | `2' | `3' | `4' | `5' | `6' | `7' | `8' | `9' ::= `bp' | `BP' \alt `cc' | `CC' \alt `cm' | `CM' \alt `dd' | `DD' \alt `em' | `EM' \alt `ex' | `EX' \alt `in' | `IN' \alt `mm' | `MM' \alt `nc' | `NC' \alt `nd' | `ND' \alt `pc' | `PC' \alt `pt' | `PT' \alt `sp' | `SP' \end{grammar} ... to be continued %% % %% \subsection{Recognized data types} \subsubsection{boolean} The strings \texttt{true}, \texttt{TRUE} and \texttt{True} are converted into Lua’s boolean type \lua{true}, the strings \texttt{false}, \texttt{FALSE} and \texttt{False} into \lua{false}. \begin{multicols}{2} \begin{minted}{latex} \luakeysdebug{ lower case true = true, upper case true = TRUE, title case true = True lower case false = false, upper case false = FALSE, title case false = False, } \end{minted} \begin{minted}{lua} { ['lower case true'] = true, ['upper case true'] = true, ['title case true'] = true, ['lower case false'] = false, ['upper case false'] = false ['title case false'] = false, } \end{minted} \end{multicols} %% % %% \clearpage \subsubsection{number} \begin{multicols}{2} \begin{minted}{latex} \luakeysdebug{ num1 = 4, num2 = -4, num3 = 0.4, num4 = 4.57e-3, num5 = 0.3e12, num6 = 5e+20 } \end{minted} \begin{minted}{lua} { ['num1'] = 4, ['num2'] = -4, ['num3'] = 0.4, ['num4'] = 0.00457, ['num5'] = 300000000000.0, ['num6'] = 5e+20 } \end{minted} \end{multicols} %% % %% \clearpage \subsubsection{dimension} |luakeys| detects \TeX{} dimensions and automatically converts the dimensions into scaled points using the function \lua{tex.sp(dim)}. Use the option \lua{convert_dimensions} of the function \lua{parse(kv_string, options)} to disalbe the automatic conversion. \begin{minted}{lua} local result = parse('dim=1cm', { convert_dimensions = false, }) \end{minted} \noindent If you want to convert a scale point into a unit string you can use the module \href{https://raw.githubusercontent.com/latex3/lualibs/master/lualibs-util-dim.lua}{lualibs-util-dim.lua}. \begin{minted}{latex} \begin{luacode} require('lualibs') tex.print(number.todimen(tex.sp('1cm'), 'cm', '%0.0F%s')) \end{luacode} \end{minted} \begin{center} \begin{tabular}{rl} \textbf{Unit name} & \textbf{Description} \\\hline bp & big point \\ cc & cicero \\ cm & centimeter \\ dd & didot \\ em & horizontal measure of \emph{M} \\ ex & vertical measure of \emph{x} \\ in & inch \\ mm & milimeter \\ nc & new cicero \\ nd & new didot \\ pc & pica \\ pt & point \\ sp & scaledpoint \\ \end{tabular} \end{center} \begin{multicols}{2} \begin{minted}{latex} \luakeysdebug{ bp = 1bp, cc = 1cc, cm = 1cm, dd = 1dd, em = 1em, ex = 1ex, in = 1in, mm = 1mm, nc = 1nc, nd = 1nd, pc = 1pc, pt = 1pt, sp = 1sp, } \end{minted} \begin{minted}{lua} { ['bp'] = 65781, ['cc'] = 841489, ['cm'] = 1864679, ['dd'] = 70124, ['em'] = 655360, ['ex'] = 282460, ['in'] = 4736286, ['mm'] = 186467, ['nc'] = 839105, ['nd'] = 69925, ['pc'] = 786432, ['pt'] = 65536, ['sp'] = 1, } \end{minted} \end{multicols} %% % %% \subsubsection{string} There are two ways to specify strings: With or without quotes. If the text have to contain commas or equal signs, then double quotation marks must be used. \begin{multicols}{2} \begin{minted}{latex} \luakeysdebug{ without quotes = no commas and equal signs are allowed, with double quotes = ", and = are allowed", } \end{minted} \begin{minted}{lua} { ['without quotes'] = 'no commas and equal signs are allowed', ['with double quotes'] = ', and = are allowed', } \end{minted} \end{multicols} \subsubsection{Standalone values} Standalone values are values without a key. They are converted into an array. In Lua an array is a table with numeric indexes (The first index is 1). \begin{multicols}{2} \begin{minted}{latex} \luakeysdebug{one,two,three} \end{minted} \columnbreak \begin{minted}{lua} { 'one', 'two', 'three' } \end{minted} \noindent is equivalent to \begin{minted}{lua} { [1] = 'one', [2] = 'two', [3] = 'three', } \end{minted} \end{multicols} \noindent All recognized data types can be used as standalone values. \begin{multicols}{2} \begin{minted}{latex} \luakeysdebug{one,2,3cm} \end{minted} \columnbreak \begin{minted}{lua} { 'one', 2, 5594039 } \end{minted} \end{multicols} %----------------------------------------------------------------------- % %----------------------------------------------------------------------- \clearpage \section{Exported functions of the Lua module \texttt{luakeys.lua}} To learn more about the individual functions (local functions), please read the \href{https://josef-friedrich.github.io/luakeys/}{source code documentation}, which was created with \href{http://stevedonovan.github.io/ldoc/}{LDoc}. The Lua module exports this functions: \begin{minted}{lua} local luakeys = require('luakeys') local parse = luakeys.parse local render = luakeys.render --local print = luakeys.print -- That would overwrite the built-in Lua function local save = luakeys.save local get = luakeys.get \end{minted} %% % %% \subsection{\texttt{parse(kv\_string, options)}: table} The function \lua{parse(input_string, options)} is the main method of this module. It parses a key-value string into a Lua table. \begin{minted}{latex} \newcommand{\mykeyvalcmd}[1][]{ \directlua{ result = luakeys.parse('#1') luakeys.print(result) } #2 } \mykeyvalcmd[one=1]{test} \end{minted} \noindent In plain \TeX: \begin{minted}{latex} \def\mykeyvalcommand#1{ \directlua{ result = luakeys.parse('#1') luakeys.print(result) } } \mykeyvalcmd{one=1} \end{minted} \noindent The function can be called with a options table. This two options are supported. \begin{minted}{lua} local result = parse('one,two,three', { convert_dimensions = false, unpack_single_array_value = false }) \end{minted} %% % %% \subsection{\texttt{render(tbl)}: string} The function \lua{render(tbl)} reverses the function \lua{parse(kv_string)}. It takes a Lua table and converts this table into a key-value string. The resulting string usually has a different order as the input table. \begin{minted}{lua} result = luakeys.parse('one=1,two=2,tree=3,') print(luakeys.render(result)) --- one=1,two=2,tree=3, --- or: --- two=2,one=1,tree=3, --- or: --- ... \end{minted} \noindent In Lua only tables with 1-based consecutive integer keys (a.k.a. array tables) can be parsed in order. \begin{minted}{lua} result = luakeys.parse('one,two,three') print(luakeys.render(result)) --- one,two,three, (always) \end{minted} %% % %% \subsection{\texttt{print(tbl): void}} The function \lua{print(tbl)} pretty prints a Lua table to standard output (stdout). It is a utility function that can be used to debug and inspect the resulting Lua table of the function \lua{parse}. You have to compile your \TeX{} document in a console to see the terminal output. %\luakeysdebug{level1={level2={key=value}}} \begin{minted}{lua} result = luakeys.parse('level1={level2={key=value}}') luakeys.print(result) \end{minted} \noindent The output should look like this: \begin{minted}{md} { ['level1'] = { ['level2'] = { ['key'] = 'value', }, } \end{minted} %% % %% \subsection{\texttt{save(identifier, result): void}} The function \lua{save(identifier, result)} saves a result (a table from a previous run of \lua{parse}) under an identifier. Therefore, it is not necessary to pollute the global namespace to store results for the later usage. %% % %% \subsection{\texttt{get(identifier): table}} The function \lua{get(identifier)} retrieves a saved result from the result store. %----------------------------------------------------------------------- % %----------------------------------------------------------------------- \clearpage \section{Debug packages} Two small debug packages are included in |luakeys|. One debug package can be used in \LaTeX{} (luakeys-debug.sty) and one can be used in plain \TeX{} (luakeys-debug.tex). Both packages provide only one command: |\luakeysdebug{kv-string}| \begin{minted}{latex} \luakeysdebug{one,two,three} \end{minted} \noindent Then the following output should appear in the document: \bigskip \luakeysdebug{one,two,three} %% % %% \subsection{For plain \TeX: luakeys-debug.tex} An example of how to use the command in plain \TeX: \begin{minted}{latex} \input luakeys-debug.tex \luakeysdebug{one,two,three} \bye \end{minted} %% % %% \subsection{For \LaTeX: luakeys-debug.sty} An example of how to use the command in \LaTeX: \begin{minted}{latex} \documentclass{article} \usepackage{luakeys-debug} \begin{document} \luakeysdebug[ unpack single array values=false, convert dimensions=false ]{one,two,three} \end{document} \end{minted} %----------------------------------------------------------------------- % %----------------------------------------------------------------------- \clearpage \section{Implementation} %% % %% \subsection{luakeys.lua} \inputminted[linenos=true]{lua}{luakeys.lua} %% % %% \clearpage \subsection{luakeys-debug.tex} \inputminted[linenos=true]{latex}{luakeys-debug.tex} %% % %% \clearpage \subsection{luakeys-debug.sty} \inputminted[linenos=true]{latex}{luakeys-debug.sty} \changes{v0.1}{2021/01/18}{Inital release} \changes{v0.2}{2021/09/19}{ * Allow all recognized data types as keys * Allow TeX macros in the values * New public Lua functions: save(identifier, result), get(identifier) } \pagebreak \PrintChanges \pagebreak \PrintIndex \end{document}