\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.4 from 2021/12/31}

\maketitle

\vfill

%level1={level2={level3={dim=1cm,bool=true,num=-0.001,str=lua}}}

\begin{minted}{lua}
local luakeys = require('luakeys')
local kv = luakeys.parse('level1={level2={level3={dim=1cm,bool=true,num=-0.001,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}

%%
%
%%

\subsection{Using the Lua module \texttt{luakeys.lua}}

The core functionality of this package is realized in Lua. So you can
use \texttt{luakeys} without using the wrapper \TeX{} files
\texttt{luakeys.sty} and \texttt{luakeys.tex}.

\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}

%%
%
%%

\subsection{Using the Lua\LaTeX{} wrapper \texttt{luakeys.sty}}

The supplied Lua\LaTeX{} file is quite small:

\begin{minted}{latex}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{luakeys}
\directlua{luakeys = require('luakeys')}
\end{minted}

\noindent
It loads the Lua module into the global variable \texttt{luakeys}.

\begin{minted}{latex}
\documentclass{article}
\usepackage{luakeys}

\begin{document}
  \directlua{
    local keys = luakeys.parse('one,two,three')
    tex.print(keys[1])
    tex.print(keys[2])
    tex.print(keys[3])
  }
\end{document}
\end{minted}

%%
%
%%

\subsection{Using the plain Lua\TeX{} wrapper \texttt{luakeys.tex}}

Even smaller is the file \texttt{luakeys.tex}. It consists of only one
line:

\begin{minted}{latex}
\directlua{luakeys = require('luakeys')}
\end{minted}

\noindent
It does the same as the Lua\LaTeX{} wrapper and loads the Lua module
\texttt{luakeys.lua} into the global variable \texttt{luakeys}.

\begin{minted}{latex}
\input luakeys.tex

\directlua{
  local keys = luakeys.parse('one,two,three')
  tex.print(keys[1])
  tex.print(keys[2])
  tex.print(keys[3])
}
\bye
\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}
<list> ::= \{ <list-item> \}

<list-container> ::= `{' <list> `}'

<list-item> ::= ( <list-container> | <key-value-pair> | <value> ) [ `,' ]

<key-value-pair> ::= <value> `=' ( <list-container> | <value> )

<value> ::= <boolean>
  \alt <dimension>
  \alt <number>
  \alt <string-quoted>
  \alt <string-unquoted>

<sign> ::= `-' | `+'

<integer> ::= `0' | `1' | `2' | `3' | `4' | `5' | `6' | `7' | `8' | `9'

<unit> ::= `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'

<boolean> ::= <boolean-true> | <boolean-false>

<boolean-true> ::= `true' | `TRUE' | `True'

<boolean-false> ::= `false' | `FALSE' | `False'
\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
}
\end{minted}
\begin{minted}{lua}
{
  ['num1'] = 4,
  ['num2'] = -4,
  ['num3'] = 0.4
}
\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)
}
\changes{v0.3}{2021/11/05}{
* Add a LuaLaTeX wrapper “luakeys.sty”
* Add a plain LuaTeX wrapper “luakeys.tex”
* Rename the previous documentation file “luakeys.tex” to luakeys-doc.tex”
}
\changes{v0.4}{2021/12/31}{
* Parser: Add support for nested tables (for example {{'a', 'b'}})
* Parser: Allow only strings and numbers as keys
* Parser: Remove support from Lua numbers with exponents (for example '5e+20')
* Switch the Lua testing framework to busted
}
\pagebreak
\PrintChanges
\pagebreak
\PrintIndex
\end{document}
