texlive[63829] Master/texmf-dist: luakeys (7jul22)

commits+karl at tug.org commits+karl at tug.org
Thu Jul 7 21:54:23 CEST 2022


Revision: 63829
          http://tug.org/svn/texlive?view=revision&revision=63829
Author:   karl
Date:     2022-07-07 21:54:23 +0200 (Thu, 07 Jul 2022)
Log Message:
-----------
luakeys (7jul22)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/luatex/luakeys/README.md
    trunk/Master/texmf-dist/doc/luatex/luakeys/luakeys-doc.pdf
    trunk/Master/texmf-dist/doc/luatex/luakeys/luakeys-doc.tex
    trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.sty
    trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.tex
    trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys.lua
    trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys.sty

Modified: trunk/Master/texmf-dist/doc/luatex/luakeys/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/luakeys/README.md	2022-07-07 19:54:15 UTC (rev 63828)
+++ trunk/Master/texmf-dist/doc/luatex/luakeys/README.md	2022-07-07 19:54:23 UTC (rev 63829)
@@ -144,6 +144,18 @@
 local result = luakeys.parse('one,two,three', opts)
 ```
 
+## Development
+
+`luakeys` is developed using the
+[Lua](https://marketplace.visualstudio.com/items?itemName=sumneko.lua)
+extension in Visual Studio Code. This extension understands the [EmmyLua
+annotations](https://github.com/sumneko/lua-language-server/wiki/EmmyLua-Annotations).
+The Lua source code documentation is generated with
+[LDoc](https://stevedonovan.github.io/ldoc/manual/doc.md.html).
+
+The Lua code is automatically formatted with the help of the
+[LuaFormatter](https://github.com/Koihik/LuaFormatter).
+
 ## Tasks
 
 ### Installing
@@ -154,6 +166,9 @@
 
 ### Testing
 
+The framework [busted](https://github.com/Olivine-Labs/busted) is used
+for the tests.
+
 ```
 luarocks install busted
 busted --exclude-tags=skip test/lua/*.lua
@@ -167,11 +182,14 @@
 
 ### Release a new version
 
+This project uses [semantic versioning](https://semver.org).
+
 Update version in:
 
 * luakeys-doc.tex
 * luakeys-debug.sty
 * luakeys.sty
+* luakeys.lua
 
 Update copyright in:
 
@@ -185,4 +203,4 @@
 
 Summarize the changes in the luakeys-doc.tex as changes.
 
-Create a new git tag `git tag -sa v0.3`. Prefix the version with “v”.
+Create a new git tag `git tag -sa 0.7.0`.

Modified: trunk/Master/texmf-dist/doc/luatex/luakeys/luakeys-doc.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/luatex/luakeys/luakeys-doc.tex
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/luakeys/luakeys-doc.tex	2022-07-07 19:54:15 UTC (rev 63828)
+++ trunk/Master/texmf-dist/doc/luatex/luakeys/luakeys-doc.tex	2022-07-07 19:54:23 UTC (rev 63829)
@@ -1,6 +1,5 @@
 \documentclass{ltxdoc}
 
-\usepackage[hidelinks]{hyperref}
 \EnableCrossrefs
 \CodelineIndex
 \RecordChanges
@@ -7,6 +6,7 @@
 
 \usepackage{mdframed}
 \usepackage{minted}
+\usepackage{luakeys}
 \usepackage{luakeys-debug}
 \usepackage{multicol}
 \usepackage{luacode}
@@ -22,13 +22,13 @@
 \def\lua#1{\mintinline{lua}|#1|}
 \def\latex#1{\mintinline{latex}|#1|}
 
-\NewDocumentCommand { \InputLatexExample } { O{} m } {
+\NewDocumentCommand { \InputLatex } { O{} m } {
   \begin{mdframed}
   \inputminted[linenos=false,#1]{latex}{examples/#2}
   \end{mdframed}
 }
 
-\NewDocumentCommand { \InputLuaExample } { O{} m } {
+\NewDocumentCommand { \InputLua } { O{} m } {
   \begin{mdframed}
   \inputminted[linenos=false,#1]{lua}{examples/#2}
   \end{mdframed}
@@ -35,7 +35,18 @@
 }
 
 \catcode`_=12
+\def\DefaultOpt#1{%
+  \texttt{\directlua{
+    tex.print(luakeys.print_default('opts', '#1'))
+  }}%
+}
 
+\def\DefaultOptDescription#1{
+\noindent
+The default value of the option “\texttt{#1}” is:
+\DefaultOpt{#1}.
+}
+
 \begin{document}
 
 \providecommand*{\url}{\texttt}
@@ -44,15 +55,16 @@
 \author{%
   Josef Friedrich\\%
   \url{josef at friedrich.rocks}\\%
-  \href{https://github.com/Josef-Friedrich/luakeys}{github.com/Josef-Friedrich/luakeys}%
+  \href{https://github.com/Josef-Friedrich/luakeys}
+       {github.com/Josef-Friedrich/luakeys}%
 }
-\date{v0.6 from 2022/06/09}
+\date{0.7.0 from 2022/07/06}
 
 \maketitle
 
 \vfill
 
-\InputLuaExample[firstline=4,lastline=7]{first-page.lua}
+\InputLua[firstline=4,lastline=7]{first-page.lua}
 
 \noindent
 Result:
@@ -83,6 +95,7 @@
 
 \newpage
 
+% \section{Einführung}
 \section{Introduction}
 
 \noindent
@@ -112,20 +125,62 @@
 \href{http://www.inf.puc-rio.br/~roberto/lpeg/}{LPeg}, the parsing
 mechanism should be pretty robust.
 
+% Der Artikel TUGboat
+% \href{http://www.tug.org/tugboat/tb30-1/tb94wright-keyval.pdf}
+% {“Implementing key–value input: An introduction” (Volume 30 (2009), No.
+% 1)} von \emph{Joseph Wright} und \emph{Christian Feuersänger} gibt einen
+% einen guten Überblick über die verfügbaren Key-Value-Pakete.
 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 \emph{Joseph Wright} and \emph{Christian Feuersänger} gives a good
-overview of the available key-value packages.
+{“Implementing key–value input: An introduction” (Volume 30 (2009), No.
+1)} by \emph{Joseph Wright} and \emph{Christian Feuersänger} gives a
+good overview of the available key-value packages.
 
+% Dieses Paket wäre ohne den Artikel
+% \href{https://tug.org/TUGboat/tb40-2/tb125menke-lpeg.pdf}
+% {"Parsing complex data formats in LuaTEX with LPEG" (Volume 40 (2019),
+% No. 2)} nicht möglich gewesen.
 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)}.
+{“Parsing complex data formats in LuaTEX with LPEG” (Volume 40 (2019),
+No. 2)}.
 
+\subsection{Pros of \texttt{luakeys}}
+
+\begin{itemize}
+% \item Schlüssel-Wert-Paare können unabhängig vom Makro-Paket (latex or
+% context) analysiert werden.
+\item Key-value pairs can be parsed independently of the macro
+collection (\LaTeX{} or Con\TeX{}t).
+
+% \item Sogar in Plain LuaTex können Schlüssel analysiert werden
+\item Even in plain Lua\TeX{} keys can be parsed.
+
+% \item |luakeys| kann mit ineinander verschachtelten listen an
+% Schlüssel-Wert-Paaren umgehen, d. h. es kann mit einer rekursiven
+% Datenstruktur an Schlüssel umgehen.
+\item |luakeys| can handle nested lists of key-value pairs, i.e. it can
+handle a recursive data structure of keys.
+
+% \item Schlüssel müssen nicht, aber können definiert werden.
+\item Keys do not have to be defined, but can they can be defined.
+\end{itemize}
+
+\subsection{Cons of \texttt{luakeys}}
+
+\begin{itemize}
+% \item Das Packet funktioniert nur in der Verbindung mit Lua\TeX.
+\item The package works only in combination with Lua\TeX.
+
+% \item Du musst zwei Sprachen beherrschen: \TeX{} und Lua.
+\item You need to know two languages: \TeX{} and Lua.
+\end{itemize}
+
 %-----------------------------------------------------------------------
 %
 %-----------------------------------------------------------------------
 
+% \section{Wie das Paket geladen wird}
 \section{How the package is loaded}
 
 %%
@@ -142,7 +197,7 @@
 So you can use |luakeys| even without using the wrapper files
 |luakeys.sty| and |luakeys.tex|.
 
-\InputLatexExample{loading/lua.tex}
+\InputLatex{loading/lua.tex}
 
 %%
 %
@@ -155,8 +210,8 @@
 For example, the MiK\TeX{} package manager downloads packages only when
 needed.
 %
-% Es wurde berichtet, dass dieser automatische Download nur mit Hilfe der
-% Hüll-Dateien funktioniert.
+% Es wurde berichtet, dass dieser automatische Download nur mit Hilfe
+% der Hüll-Dateien funktioniert.
 It has been reported that this automatic download only works with this
 wrapper files.
 %
@@ -164,7 +219,7 @@
 % “\latex{\usepackage{luakeys}} ”Ausschau.
 Probably MiK\TeX{} is searching for an occurrence of the \LaTeX{} macro
 “\latex{\usepackage{luakeys}}”.
-
+%
 % Die mitgelieferte Lua\LaTeX{}-Datei ist recht klein:
 The supplied Lua\LaTeX{} file is quite small:
 
@@ -175,18 +230,24 @@
 \end{minted}
 
 \noindent
+% Es lädt das Lua-Modul in die globale Variable \texttt{luakeys}.
 It loads the Lua module into the global variable \texttt{luakeys}.
 
-\InputLatexExample{loading/latex.tex}
+\InputLatex{loading/tex-latex.tex}
 
 %%
 %
 %%
 
+% \subsection{Verwendung des Plain-Lua\TeX{}-Hüllpakets
+% \texttt{luakeys.tex}}
 \subsection{Using the plain Lua\TeX{} wrapper \texttt{luakeys.tex}}
 
-Even smaller is the file \texttt{luakeys.tex}. It consists of only one
-line:
+% Noch kleiner ist die Datei \texttt{luakeys.tex}.
+Even smaller is the file \texttt{luakeys.tex}.
+%
+% Sie besteht aus nur einer Zeile:
+It consists of only one line:
 
 \begin{minted}{latex}
 \directlua{luakeys = require('luakeys')}
@@ -193,21 +254,39 @@
 \end{minted}
 
 \noindent
+% Es macht dasselbe wie das Lua\LaTeX{}-Hüllpacket und lädt das
+% Lua-Modul \texttt{luakeys.lua} in die globale Variable
+% \texttt{luakeys}.
 It does the same as the Lua\LaTeX{} wrapper and loads the Lua module
 \texttt{luakeys.lua} into the global variable \texttt{luakeys}.
 
-\InputLatexExample{loading/tex.tex}
+\InputLatex{loading/tex-plain.tex}
 
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
 \section{Lua interface / API}
 
-To learn more about the individual functions (local functions), please
+% Das Lua-Modul exportiert diese Funktionen und Tabellen:
+The Lua module exports this functions and tables:
+
+\InputLua[firstline=3,lastline=13]{export.lua}
+
+% Diese Dokumentation stellt nur die öffentlichen Funktionen und
+% Tabellen vor.
+This documentation presents only the public functions and tables.
+%
+% Um mehr über die privaten, nicht exportierten Funktionen zu erfahren,
+% lesen Sie bitte die
+% \href{https://josef-friedrich.github.io/luakeys/}{Quellcode
+% Dokumentation}, die mit
+% \href{http://stevedonovan.github.io/ldoc/}{LDoc} erstellt wurde.
+To learn more about the private, not exported 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 and tables:
+\href{http://stevedonovan.github.io/ldoc/}{LDoc}.
 
-\InputLuaExample[firstline=3,lastline=12]{export.lua}
-
 \subsection{Lua indentifier names}
 
 % Das Projekt verwendet einige wenige Abkürzungen für Variablennamen,
@@ -219,7 +298,8 @@
 \begin{tabular}{lll}
 Abbreviation & spelled out & Example \\\hline
 \lua{kv_string} & Key-value string & \lua{'key=value'} \\
-\lua{opts} & Options (for the parse function) & \lua{ { no_error = false } } \\
+\lua{opts} & Options (for the parse function) &
+\lua{ { no_error = false } } \\
 \lua{defs} & Definitions \\
 \lua{def} & Definition \\
 \lua{attr} & Attributes (of a definition) \\
@@ -233,7 +313,8 @@
 \begin{tabular}{ll}
 
 \lua{result} &
-% Das Endergebnis aller einzelnen Übersetzungs- und Normalisierungsschritte
+% Das Endergebnis aller einzelnen Übersetzungs- und
+% Normalisierungsschritte
 The final result of all individual parsing and normalization steps. \\
 
 \lua{unknown} &
@@ -250,54 +331,41 @@
 %
 %%
 
-\subsection{Function \texttt{parse(kv_string, opts): result, unknown, raw}}
+\subsection{Function “\texttt{parse(kv_string, opts): result, unknown,
+raw}”}
 \label{parse}
 
 % Die Function parse ist die wichtigste Funktion des Pakets.
 The function \lua{parse(kv_string, opts)} is the most important function
 of the package.
+%
+%
 % Sie konvertiert eine Schlüssel-Wert-Zeichenkette in eine Lua Tabelle.
 It converts a key-value string into a Lua table.
 
-\begin{minted}{latex}
-\newcommand{\mykeyvalcmd}[2][]{
-  \directlua{
-    result = luakeys.parse('#1')
-    luakeys.debug(result)
-  }
-  #2
-}
-\mykeyvalcmd[one=1]{test}
-\end{minted}
+\InputLatex{functions/parse/tex-latex.tex}
 
 \noindent
 In plain \TeX:
 
-\begin{minted}{latex}
-\def\mykeyvalcommand#1{
-  \directlua{
-    result = luakeys.parse('#1')
-    luakeys.debug(result)
-  }
-}
-\mykeyvalcmd{one=1}
-\end{minted}
+\InputLatex{functions/parse/tex-plain.tex}
 
 \subsection{Options to configure the \texttt{parse} function}
 
 \noindent
 The \lua{parse} function can be called with an options table. This
-options are supported:
+options are supported: \catcode`_=12
+\directlua{luakeys.print_names('opts')}
 
-\InputLuaExample[firstline=5,lastline=69]{opts/all-opts.lua}
+\InputLua[firstline=5,lastline=69]{opts/all-opts.lua}
 
 \noindent
 The options can also be set globally using the exported table
 |opts|:
 
-\InputLuaExample[firstline=4,lastline=4]{opts/exported-default-opts.lua}
+\InputLua[firstline=4,lastline=4]{opts/exported-default-opts.lua}
 
-\InputLuaExample[firstline=10,lastline=11]{opts/exported-default-opts.lua}
+\InputLua[firstline=10,lastline=11]{opts/exported-default-opts.lua}
 
 %%
 %
@@ -305,20 +373,29 @@
 
 \subsubsection{Option “\texttt{convert_dimensions}”}
 
+% Wenn Sie die Option \lua{convert_dimensions} auf \lua{true} setzen,
+% erkennt |luakeys| die \TeX{}-Dimensionen und konvertiert sie mit Hilfe
+% der die Funktion \lua{tex.sp(dim)} in scaled points.
 If you set the option \lua{convert_dimensions} to \lua{true}, |luakeys|
 detects the \TeX{} dimensions and converts them into scaled points using
 the function \lua{tex.sp(dim)}.
 
-\InputLuaExample[firstline=4,lastline=7]{opts/convert-dimensions.lua}
+\InputLua[firstline=4,lastline=7]{opts/convert-dimensions/true.lua}
 
 \noindent
+% Standardmäßig werden die Dimensionen nicht in skalierte Punkte
+% umgewandelt.
 By default the dimensions are not converted into scaled points.
 
-\InputLuaExample[firstline=13,lastline=18]{opts/convert-dimensions.lua}
+\InputLua[firstline=4,lastline=9]{opts/convert-dimensions/false.lua}
 
 \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}.
+% Wenn Sie eine skalierte Punktzahl in einen Dimensionszeichenketten
+% umwandeln möchten, können Sie das Modul
+If you want to convert a scaled points number into a dimension string
+you can use the module
+\href{https://raw.githubusercontent.com/latex3/lualibs/master/lualibs-util-dim.lua}
+{lualibs-util-dim.lua}.
 
 \begin{minted}{lua}
 require('lualibs')
@@ -325,6 +402,8 @@
 tex.print(number.todimen(tex.sp('1cm'), 'cm', '%0.0F%s'))
 \end{minted}
 
+\DefaultOptDescription{convert_dimensions}
+
 %%
 %
 %%
@@ -331,19 +410,12 @@
 
 \subsubsection{Option “\texttt{debug}”}
 
-If the option “debug” is set to ture, the result table is printed to the
-console.
+% Wenn die Option \lua{debug} auf true gesetzt ist, wird die
+% Ergebnistabelle in der Konsole ausgegeben.
+If the option \lua{debug} is set to ture, the result table is printed to
+the console.
 
-\begin{minted}{latex}
-\documentclass{article}
-\usepackage{luakeys}
-\begin{document}
-\directlua{
-  luakeys.parse('one,two,three', { debug = true })
-}
-debug
-\end{document}
-\end{minted}
+\InputLatex{opts/debug-latex.tex}
 
 \begin{verbatim}
 This is LuaHBTeX, Version 1.15.0 (TeX Live 2022)
@@ -361,6 +433,8 @@
 Transcript written on debug.log.
 \end{verbatim}
 
+\DefaultOptDescription{debug}
+
 %%
 %
 %%
@@ -375,14 +449,16 @@
 (keys without a value) get. This option has no influence on keys with
 values.
 
-\InputLuaExample[firstline=4,lastline=5]{opts/default.lua}
+\InputLua[firstline=4,lastline=5]{opts/default.lua}
 
 \noindent
 % Standardmäßig erhalten nackte Schlüssel den Wert \lua{true}.
 By default, naked keys get the value \lua{true}.
 
-\InputLuaExample[firstline=11,lastline=12]{opts/default.lua}
+\InputLua[firstline=11,lastline=12]{opts/default.lua}
 
+\DefaultOptDescription{default}
+
 %%
 %
 %%
@@ -402,8 +478,10 @@
 Values in the defaults table are
 overwritten by values in the result table.
 
-\InputLuaExample[firstline=4,lastline=7]{opts/defaults.lua}
+\InputLua[firstline=4,lastline=7]{opts/defaults.lua}
 
+\DefaultOptDescription{defaults}
+
 %%
 %
 %%
@@ -421,14 +499,16 @@
 % Anstatt
 Instead of ...
 
-\InputLuaExample[firstline=4,lastline=5]{opts/defs.lua}
+\InputLua[firstline=4,lastline=5]{opts/defs.lua}
 
 \noindent
 % können wir schreiben ..
 we can write ...
 
-\InputLuaExample[firstline=11,lastline=13]{opts/defs.lua}
+\InputLua[firstline=11,lastline=13]{opts/defs.lua}
 
+\DefaultOptDescription{defs}
+
 %%
 %
 %%
@@ -438,17 +518,19 @@
 \begin{description}
 \item[lower] \strut
 
-\InputLuaExample[firstline=4,lastline=5]{opts/format-keys.lua}
+\InputLua[firstline=4,lastline=5]{opts/format-keys.lua}
 
 \item[snake] \strut
 
-\InputLuaExample[firstline=11,lastline=12]{opts/format-keys.lua}
+\InputLua[firstline=11,lastline=12]{opts/format-keys.lua}
 
 \item[upper] \strut
 
-\InputLuaExample[firstline=18,lastline=19]{opts/format-keys.lua}
+\InputLua[firstline=18,lastline=19]{opts/format-keys.lua}
 \end{description}
 
+\DefaultOptDescription{format_keys}
+
 %%
 %
 %%
@@ -463,8 +545,8 @@
 % Die Funktionen sind in der Verarbeitungsreihenfolge aufgelistet.
 The functions are listed in processing order.
 %
-% \lua{*_before_opts} bedeutet, dass die Hooks nach der LPeg Syntaxanalyse
-% und vor dem Anwenden der Optionen ausgeführt
+% \lua{*_before_opts} bedeutet, dass die Hooks nach der LPeg
+% Syntaxanalyse und vor dem Anwenden der Optionen ausgeführt
 \lua{*_before_opts} means that the hooks are executed after the LPeg
 syntax analysis and before the options are applied.
 %
@@ -504,7 +586,7 @@
 The \lua{kv_string} hook is called as the first of the hook functions
 before the LPeg syntax parser is executed.
 
-\InputLuaExample[firstline=4,lastline=11]{hooks/kv-string.lua}
+\InputLua[firstline=4,lastline=11]{hooks/kv-string.lua}
 
 \paragraph{\texttt{keys_*}}
 
@@ -515,12 +597,12 @@
 % Die Hook-Funktion muss zwei Werte zurückgeben: \lua{key, value}
 The hook function must return two values: \lua{key, value}.
 %
-% Das folgende Beispiel gibt \lua{key} und \lua{value} unverändert zurück,
-% sodass die Ergebnistabelle nicht verändert wird.
+% Das folgende Beispiel gibt \lua{key} und \lua{value} unverändert
+% zurück, sodass die Ergebnistabelle nicht verändert wird.
 The following example returns \lua{key} and \lua{value} unchanged, so
 the result table is not changed.
 
-\InputLuaExample[firstline=4,lastline=11]{hooks/keys-unchanged.lua}
+\InputLua[firstline=4,lastline=11]{hooks/keys-unchanged.lua}
 
 \noindent
 % Das nächste Beispiel demonstriert den dritten Parameter \lua{depth}
@@ -528,7 +610,7 @@
 The next example demonstrates the third parameter \lua{depth} of the
 hook function.
 
-\InputLuaExample[firstline=4,lastline=16]{hooks/keys-depth.lua}
+\InputLua[firstline=4,lastline=16]{hooks/keys-depth.lua}
 
 \paragraph{\texttt{result_*}}
 
@@ -549,13 +631,15 @@
 With the help of the option \lua{naked_as_value}, naked keys are not
 given a default value, but are stored as values in a Lua table.
 
-\InputLuaExample[firstline=4,lastline=5]{opts/naked-as-value.lua}
+\InputLua[firstline=4,lastline=5]{opts/naked-as-value.lua}
 
 \noindent
 If we set the option \lua{naked_as_value} to \lua{true}:
 
-\InputLuaExample[firstline=11,lastline=14]{opts/naked-as-value.lua}
+\InputLua[firstline=11,lastline=14]{opts/naked-as-value.lua}
 
+\DefaultOptDescription{naked_as_value}
+
 %%
 %
 %%
@@ -568,13 +652,15 @@
 % Mit Hilfe der Option \lua{no_error} kann dies unterbunden werden.
 This can be prevented with the help of the \lua{no_error} option.
 
-\InputLuaExample[firstline=5,lastline=6]{opts/no-error.lua}
+\InputLua[firstline=5,lastline=6]{opts/no-error.lua}
 
 \noindent
 If we set the option \lua{no_error} to \lua{true}:
 
-\InputLuaExample[firstline=9,lastline=10]{opts/no-error.lua}
+\InputLua[firstline=9,lastline=10]{opts/no-error.lua}
 
+\DefaultOptDescription{no_error}
+
 %%
 %
 %%
@@ -587,15 +673,17 @@
 With the help of the option \lua{unpack}, all tables that consist of
 only one a single naked key or a single standalone value are unpacked.
 
-\InputLuaExample[firstline=4,lastline=5]{opts/unpack.lua}
+\InputLua[firstline=4,lastline=5]{opts/unpack.lua}
 
-\InputLuaExample[firstline=11,lastline=12]{opts/unpack.lua}
+\InputLua[firstline=11,lastline=12]{opts/unpack.lua}
 
+\DefaultOptDescription{unpack}
+
 %%
 %
 %%
 
-\subsection{Function \texttt{define(defs, opts): parse}}
+\subsection{Function “\texttt{define(defs, opts): parse}”}
 \label{define}
 
 The \lua{define} function returns a \lua{parse} function (see
@@ -609,13 +697,13 @@
 \item by the “name” attribute.
 \end{enumerate}
 
-\InputLuaExample[firstline=4,lastline=16]{functions/define.lua}
+\InputLua[firstline=4,lastline=16]{functions/define.lua}
 
 \noindent
 For nested definitions, only the last two ways of specifying the key
 names can be used.
 
-\InputLuaExample[firstline=26,lastline=33]{functions/define.lua}
+\InputLua[firstline=26,lastline=33]{functions/define.lua}
 
 %-----------------------------------------------------------------------
 %
@@ -636,12 +724,16 @@
 list just a few naming possibilities) to define keys, was deliberately
 chosen to distinguish them from the options of the \lua{parse} function.
 %
+% Diese Attribute sind erlaubt.
+These attributes are allowed:
+\directlua{luakeys.print_names('attrs')}.
+%
 % Das folgende Codebeispiel listet alle Attribute auf, die verwendet
 % werden können, um Schlüssel-Wert-Paare zu definieren.
 The code example below lists all the attributes that can be used to
 define key-value pairs.
 
-\InputLuaExample[firstline=5,lastline=41]{defs/all-attrs.lua}
+\InputLua[firstline=5,lastline=46]{defs/all-attrs.lua}
 
 %%
 %
@@ -653,12 +745,12 @@
 The value is always stored under the original key name. A single alias
 name can be specified by a string ...
 
-\InputLuaExample[firstline=4,lastline=7]{defs/attrs/alias.lua}
+\InputLua[firstline=4,lastline=7]{defs/attrs/alias.lua}
 
 \noindent
 multiple aliases by a list of strings.
 
-\InputLuaExample[firstline=13,lastline=16]{defs/attrs/alias.lua}
+\InputLua[firstline=13,lastline=16]{defs/attrs/alias.lua}
 
 %%
 %
@@ -669,7 +761,7 @@
 % Die Option \lua{default} wird nur bei nackten Schlüsseln verwendet.
 The \lua{default} attribute is used only for naked keys.
 
-\InputLuaExample[firstline=4,lastline=5]{defs/attrs/always-present.lua}
+\InputLua[firstline=4,lastline=5]{defs/attrs/always-present.lua}
 
 \noindent
 % Wird die Option \lua{always_present} auf wahr gesetzt, wird der
@@ -678,7 +770,7 @@
 included in the result. If no default value is definied, true is taken
 as the value.
 
-\InputLuaExample[firstline=11,lastline=12]{defs/attrs/always-present.lua}
+\InputLua[firstline=11,lastline=12]{defs/attrs/always-present.lua}
 
 %%
 %
@@ -690,7 +782,7 @@
 Some key values should be selected from a restricted set of choices.
 These can be handled by passing an array table containing choices.
 
-\InputLuaExample[firstline=4,lastline=5]{defs/attrs/choices.lua}
+\InputLua[firstline=4,lastline=5]{defs/attrs/choices.lua}
 
 \noindent
 When the key-value pair is parsed, values will be checked, and an error
@@ -697,7 +789,7 @@
 message will be displayed if the value was not one of the acceptable
 choices:
 
-\InputLuaExample[firstline=13,lastline=15]{defs/attrs/choices.lua}
+\InputLua[firstline=13,lastline=15]{defs/attrs/choices.lua}
 
 %%
 %
@@ -727,8 +819,8 @@
 % Dann wird eine Fehlermeldung ausgegeben.
 Then an error message is displayed.
 
-\InputLuaExample[firstline=4,lastline=8]{defs/attrs/data-type.lua}
-\InputLuaExample[firstline=11,lastline=15]{defs/attrs/data-type.lua}
+\InputLua[firstline=4,lastline=8]{defs/attrs/data-type.lua}
+\InputLua[firstline=11,lastline=15]{defs/attrs/data-type.lua}
 
 %%
 %
@@ -746,7 +838,7 @@
 With the global \lua{default} attribute (\ref{option-default}) a default
 value can be specified for all naked keys.
 
-\InputLuaExample[firstline=4,lastline=9]{defs/attrs/default.lua}
+\InputLua[firstline=4,lastline=9]{defs/attrs/default.lua}
 
 %%
 %
@@ -766,14 +858,15 @@
 % Wert verwendet werden.
 Any value can be used as a name for this exclusive group.
 
-\InputLuaExample[firstline=4,lastline=9]{defs/attrs/exclusive-group.lua}
+\InputLua[firstline=4,lastline=9]{defs/attrs/exclusive-group.lua}
 
+\noindent
 % Werden mehrer Schlüssel der Gruppe angegeben, so wird eine
 % Fehlermeldung geworfen.
 If more than one key of the group is specified, an error message is
 thrown.
 
-\InputLuaExample[firstline=21,lastline=23]{defs/attrs/exclusive-group.lua}
+\InputLua[firstline=21,lastline=23]{defs/attrs/exclusive-group.lua}
 
 %%
 %
@@ -809,13 +902,13 @@
 If the key \lua{show} is parsed by the \lua{parse} function, then the
 target key \lua{visibility} receives the value \lua{true}.
 
-\InputLuaExample[firstline=4,lastline=7]{defs/attrs/opposite-keys.lua}
+\InputLua[firstline=4,lastline=7]{defs/attrs/opposite-keys.lua}
 
 % Wird der Schlüssel \lua{hide} gelesen, dann \lua{falsch}.
 \noindent
 If the key \lua{hide} is parsed, then \lua{false}.
 
-\InputLuaExample[firstline=13,lastline=13]{defs/attrs/opposite-keys.lua}
+\InputLua[firstline=13,lastline=13]{defs/attrs/opposite-keys.lua}
 
 %%
 %
@@ -844,14 +937,24 @@
 
 \subsubsection{Attribute “\texttt{match}”}
 
-% Der Wert des Schlüssel wird der Lua Funktion übergeben
-The value of the key is passed to the Lua function
-\lua{string.match(value, match)} ((\url{http://www.lua.org/manual/5.3/manual.html#pdf-string.match})).
+% Der Wert des Schlüssel wird zuerst der Lua Funktion
+% \lua{string.match(value, match)} übergeben, bevor er dem Schlüssel
+% zugewiesen wird.
+The value of the key is first passed to the Lua function
+\lua{string.match(value, match)}
+(\url{http://www.lua.org/manual/5.3/manual.html#pdf-string.match})
+before being assigned to the key.
+%
+% Du kannst das Attribut \lua{match} deshalb mit einer Pattern-Matching-
+% Zeichenkette konfigurieren, wie sie in Lua zu Einsatz kommt.
+You can therefore configure the \lua{match} attribute with a pattern
+matching string used in Lua.
+%
 % Werfe einen Blick in das Lua-Handbuch, wie man Patterns schreibt.
 Take a look at the Lua manual on how to write patterns
-(\url{http://www.lua.org/manual/5.3/manual.html#6.4.1})
+(\url{http://www.lua.org/manual/5.3/manual.html#6.4.1}).
 
-\InputLuaExample[firstline=4,lastline=6]{defs/attrs/match.lua}
+\InputLua[firstline=4,lastline=7]{defs/attrs/match/birthday.lua}
 
 \noindent
 % Kann das Pattern im Wert nicht gefunden werden, wird eine
@@ -858,7 +961,7 @@
 % Fehlermeldung ausgegeben.
 If the pattern cannot be found in the value, an error message is issued.
 
-\InputLuaExample[firstline=14,lastline=17]{defs/attrs/match.lua}
+\InputLua[firstline=15,lastline=18]{defs/attrs/match/birthday.lua}
 
 \noindent
 % Der Schlüssel erhält das Ergebnis der Funktion \lua{string.match(value,
@@ -867,8 +970,11 @@
 The key receives the result of the function \lua{string.match(value,
 match)}, which means that the original value may not be stored
 completely in the key.
+%
+% Im nächsten Beispiel wird der gesamte Eingabewert akzeptiert:
+In the next example, the entire input value is accepted:
 
-\InputLuaExample[firstline=22,lastline=23]{defs/attrs/match.lua}
+\InputLua[firstline=4,lastline=5]{defs/attrs/match/year.lua}
 
 \noindent
 % Das Präfix “waste ” und das Suffix “ rubbisch” der Zeichenketten wird
@@ -876,7 +982,7 @@
 The prefix “waste ” and the suffix “ rubbisch” of the string are
 discarded.
 
-\InputLuaExample[firstline=29,lastline=29]{defs/attrs/match.lua}
+\InputLua[firstline=11,lastline=11]{defs/attrs/match/year.lua}
 
 \noindent
 % Da Funktion \lua{string.match(value, match)} immer eine Zeichenkette
@@ -897,34 +1003,87 @@
 % Anstatt ...
 Instead of ...
 
-\InputLuaExample[firstline=4,lastline=5]{defs/attrs/name.lua}
+\InputLua[firstline=4,lastline=8]{defs/attrs/name/as-key.lua}
 
 \noindent
 % ... können wir schreiben:
 ... we can write:
 
-\InputLuaExample[firstline=11,lastline=15]{defs/attrs/name.lua}
+\InputLua[firstline=4,lastline=8]{defs/attrs/name/name-attr.lua}
 
 %%
 %
 %%
 
+\subsubsection{Attribute “\texttt{pick}”}
+
+% Das Attribut \lua{pick} sucht nach einem Wert, der keinem Schlüssel
+% zugeordnet ist.
+The attribute \lua{pick} searches for a value not assigned to a key.
+% Der zuerst gefundene Wert, d. h. der weiter links stehende Wert, wird
+% einem Schlüssel zugewiesen.
+The first value found, i.e. the one further to the left, is assigned to
+a key.
+
+\InputLua[firstline=4,lastline=6]{defs/attrs/pick/dimension.lua}
+
+\noindent
+% Es wird nur in der aktuellen Ergebnistabelle gesucht und nicht auf
+% anderen Ebenen in der rekursiven Datenstruktur.
+Only the current result table is searched, not other levels in the
+recursive data structure.
+
+\InputLua[firstline=4,lastline=11]{defs/attrs/pick/different-levels.lua}
+
+\noindent
+% Die Suche nach Werte wird aktiviert, wenn das Attribut \lua{pick} auf
+% \lua{true} gesetzt wird.
+The search for values is activated when the attribute \lua{pick} is set
+to \lua{true}.
+% Die Suche kann eingeschränkt werden, indem ein Datentype angegeben
+% wird.
+The search can be limited by specifying a data type.
+% Nach diesen Datentypen kann gesucht werden:
+These data types can be searched for:
+\directlua{
+  local types = {}
+  for t, fn in pairs(luakeys.is) do
+    table.insert(types, t)
+  end
+  tex.print(table.concat(types, ', '))
+}.
+
+% Wird einem Schlüssel bereits bei der Eingabe ein Wert zugewiesen, dann
+% wird nicht weiter nach Werten gesucht.
+If a value is already assigned to a key when it is entered, then no
+further search for values is performed.
+\InputLua[firstline=4,lastline=8]{defs/attrs/pick/value-set.lua}
+
+%%
+%
+%%
+
 \subsubsection{Attribute “\texttt{process}”}
 \label{attr-process}
 
-The \lua{process} attribute can be used to define a function whose return
-value is passed to the key. Four parameters are passed when the
-function is called:
+% Das Attribut \lua{process} kann dazu verwendet werden, um eine Funktion
+% zu definieren, deren Rückgabewert an den Schlüssel übergeben wird.
+The \lua{process} attribute can be used to define a function whose
+return value is passed to the key.
+%
+% Beim Aufruf der Funktion werden vier Parameter übergeben:
+Four parameters are passed when the function is called:
 
 \begin{enumerate}
 \item \lua{value}:
-% Der zum schlüssel gehörende aktuelle Wert.
+% Der zum Schlüssel gehörende aktuelle Wert.
 The current value asssociated with the key.
 
 \item \lua{input}:
 % Die Ergebnis-Tabelle, die vor dem Zeitpunkt geklont wurde, als mit dem
 % Anwenden der Definitionen begonnen wurde.
-The result table cloned before the time the definitions started to be applied.
+The result table cloned before the time the definitions started to be
+applied.
 
 \item \lua{result}: The table in which the final result will be saved.
 
@@ -932,26 +1091,29 @@
 are stored.
 \end{enumerate}
 
-% Das folgende Beispiel demonstriert den Parameter \lua{value}.
 \noindent
+% Das folgende Beispiel demonstriert den Parameter \lua{value}:
 The following example demonstrates the \lua{value} parameter:
-\InputLuaExample[firstline=4,lastline=14]{defs/attrs/process.lua}
+\InputLua[firstline=4,lastline=14]{defs/attrs/process/parameter-value.lua}
 
 \noindent
+% Das folgende Beispiel demonstriert den Parameter \lua{input}:
 The following example demonstrates the \lua{input} parameter:
 
-\InputLuaExample[firstline=22,lastline=34]{defs/attrs/process.lua}
+\InputLua[firstline=4,lastline=16]{defs/attrs/process/parameter-input.lua}
 
 \noindent
+% Das folgende Beispiel demonstriert den Parameter \lua{result}:
 The following example demonstrates the \lua{result} parameter:
 
-\InputLuaExample[firstline=42,lastline=50]{defs/attrs/process.lua}
+\InputLua[firstline=4,lastline=12]{defs/attrs/process/parameter-result.lua}
 
 \noindent
+% Das folgende Beispiel demonstriert den Parameter \lua{unknown}:
 The following example demonstrates the \lua{unknown} parameter:
 
-\InputLuaExample[firstline=58,lastline=65]{defs/attrs/process.lua}
-\InputLuaExample[firstline=69,lastline=69]{defs/attrs/process.lua}
+\InputLua[firstline=4,lastline=11]{defs/attrs/process/parameter-unknown.lua}
+\InputLua[firstline=15,lastline=15]{defs/attrs/process/parameter-unknown.lua}
 
 %%
 %
@@ -959,17 +1121,42 @@
 
 \subsubsection{Attribute “\texttt{required}”}
 
-\InputLuaExample[firstline=4,lastline=5]{defs/attrs/required.lua}
+% Durch das Attribut \lua{required} kann erzwungen werden, dass ein
+% bestimmter Schlüssel angegeben werden muss.
+The \lua{required} attribute can be used to enforce that a specific key
+must be specified.
+%
+% Im untenstehenden Beispiel wird der Schlüssel \lua{important} als
+% zwingend notwendig definiert.
+In the example below, the key \lua{important} is defined as mandatory.
 
-\InputLuaExample[firstline=13,lastline=14]{defs/attrs/required.lua}
+\InputLua[firstline=4,lastline=5]{defs/attrs/required.lua}
 
 \noindent
+% Fehlt der Schlüssel \lua{important} in der Eingabe, so tritt eine
+% Fehlermeldung auf.
+If the key \lua{important} is missing in the input, an error message
+occurs.
+
+\InputLua[firstline=13,lastline=14]{defs/attrs/required.lua}
+
+\noindent
 A recursive example:
 
-\InputLuaExample[firstline=18,lastline=23]{defs/attrs/required.lua}
-\InputLuaExample[firstline=29,lastline=30]{defs/attrs/required.lua}
-\InputLuaExample[firstline=38,lastline=39]{defs/attrs/required.lua}
+\InputLua[firstline=18,lastline=23]{defs/attrs/required.lua}
 
+\noindent
+% Der Schlüssel \lua{important2} auf der Ebene 2 fehlt.
+The \lua{important2} key on level 2 is missing.
+
+\InputLua[firstline=29,lastline=30]{defs/attrs/required.lua}
+
+\noindent
+% Der Schlüssel \lua{important1} auf der untersten Schlüssel-Ebene fehlt.
+The \lua{important1} key at the lowest key level is missing.
+
+\InputLua[firstline=38,lastline=39]{defs/attrs/required.lua}
+
 %%
 %
 %%
@@ -981,13 +1168,13 @@
 The \lua{sub_keys} attribute can be used to build nested key-value pair
 definitions.
 
-\InputLuaExample[firstline=4,lastline=16]{defs/attrs/sub-keys.lua}
+\InputLua[firstline=4,lastline=16]{defs/attrs/sub-keys.lua}
 
 %-----------------------------------------------------------------------
 %
 %-----------------------------------------------------------------------
 
-\subsection{Function \texttt{render(result): string}}
+\subsection{Function “\texttt{render(result): string}”}
 
 The function \lua{render(result)} reverses the function
 \lua{parse(kv_string)}. It takes a Lua table and converts this table
@@ -994,19 +1181,19 @@
 into a key-value string. The resulting string usually has a different
 order as the input table.
 
-\InputLuaExample[firstline=4,lastline=10]{functions/render.lua}
+\InputLua[firstline=4,lastline=10]{functions/render.lua}
 
 \noindent
 In Lua only tables with 1-based consecutive integer keys (a.k.a. array
 tables) can be parsed in order.
 
-\InputLuaExample[firstline=16,lastline=17]{functions/render.lua}
+\InputLua[firstline=16,lastline=17]{functions/render.lua}
 
 %%
 %
 %%
 
-\subsection{Function \texttt{debug(result): void}}
+\subsection{Function “\texttt{debug(result): void}”}
 
 The function \lua{debug(result)} pretty prints a Lua table to standard
 output (stdout). It is a utility function that can be used to debug and
@@ -1013,7 +1200,7 @@
 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.
 
-\InputLuaExample[firstline=4,lastline=5]{functions/debug.lua}
+\InputLua[firstline=4,lastline=5]{functions/debug.lua}
 
 \noindent
 The output should look like this:
@@ -1032,7 +1219,7 @@
 %
 %%
 
-\subsection{Function \texttt{save(identifier, result): void}}
+\subsection{Function “\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.
@@ -1043,7 +1230,7 @@
 %
 %%
 
-\subsection{Function \texttt{get(identifier): result}}
+\subsection{Function “\texttt{get(identifier): result}”}
 
 The function \lua{get(identifier)} retrieves a saved result from the
 result store.
@@ -1052,23 +1239,106 @@
 %
 %%
 
-\subsection{Table \texttt{is}}
+\subsection{Table “\texttt{is}”}
 
-\subsubsection{Function \texttt{is.boolean(value): boolean}}
-\InputLuaExample[firstline=7,lastline=23]{is-table.lua}
+% In der Tabelle \lua{is} werden einige Funktionen zusammengefasst, die
+% überprüft ob eine Eingabe einem bestimmen Datentyp entspricht.
+In the table \lua{is} some functions are summarized, which check whether
+an input corresponds to a certain data type.
+%
+% Alle Funktionen akzeptieren nicht nur die entsprechenden Lua-Datentypen,
+% sondern auch Eingaben als Zeichenketten.
+All functions accept not only the corresponding Lua data types, but also
+input as strings.
+%
+% Beispielsweise wird die Zeichenkette \lua{'true'} von der
+% \lua{is.boolean()}-Funktion als Wahrheitswert erkannt.
+For example, the string \lua{'true'} is recognized by the
+\lua{is.boolean()} function as a boolean value.
 
-\subsubsection{Function \texttt{is.dimension(value): boolean}}
-\InputLuaExample[firstline=27,lastline=37]{is-table.lua}
+\subsubsection{Function “\texttt{is.boolean(value): boolean}”}
+\InputLua[firstline=6,lastline=23]{functions/is/boolean.lua}
 
-\subsubsection{Function \texttt{is.integer(value): boolean}}
-\InputLuaExample[firstline=41,lastline=46]{is-table.lua}
+\subsubsection{Function “\texttt{is.dimension(value): boolean}”}
+\InputLua[firstline=6,lastline=16]{functions/is/dimension.lua}
 
-\subsubsection{Function \texttt{is.number(value): boolean}}
-\InputLuaExample[firstline=50,lastline=57]{is-table.lua}
+\subsubsection{Function “\texttt{is.integer(value): boolean}”}
+\InputLua[firstline=6,lastline=11]{functions/is/integer.lua}
 
-\subsubsection{Function \texttt{is.string(value): boolean}}
-\InputLuaExample[firstline=61,lastline=67]{is-table.lua}
+\subsubsection{Function “\texttt{is.number(value): boolean}”}
+\InputLua[firstline=6,lastline=13]{functions/is/number.lua}
 
+\subsubsection{Function “\texttt{is.string(value): boolean}”}
+\InputLua[firstline=6,lastline=12]{functions/is/string.lua}
+
+%%
+%
+%%
+
+\subsection{Table “\texttt{utils}”}
+
+\subsubsection{Function “\texttt{utils.scan_oarg(initial_delimiter?, end_delimiter?): string}”}
+
+% Plain \TeX{} kennt keine optionalen Argumente (oarg).
+Plain \TeX{} does not know optional arguments (oarg).
+%
+% Die Funktion ermöglicht es nicht nur in \LaTeX{}, sondern auch in
+% Plain \TeX{} nach optionalen Argumenten zu suchen.
+The function \\ \lua{utils.scan_oarg(initial_delimiter?,
+end_delimiter?): string} allows to search for optional arguments not only
+in \LaTeX{} but also in Plain \TeX.
+%
+% Die Funktion basiert auf der Token-Bibliothek.
+The function uses the token library built into Lua\TeX{}.
+%
+% Die beiden Parameter \lua{initial_delimiter} und \lua{end_delimiter}
+% können weggelassen werden.
+The two parameters \lua{initial_delimiter} and \lua{end_delimiter} can
+be omitted.
+%
+% Dann werden eckige Klammern als Begrenzungszeichen angenommen.
+Then square brackets are assumed to be delimiters.
+%
+% Dieser Lua-Code \lua{utils.scan_oarg('(', ')')} sucht beispielsweise
+% nach an einem optionalen Argument in runden Klammern.
+For example, this Lua code \lua{utils.scan_oarg('(', ')')} searches for
+an optional argument in round brackets
+%
+% Die Funktion gibt die Zeichenkette zwischen den Begrenzungszeichen
+% zurück, oder nil wenn Begrenzungszeichen gefunden werden konnten.
+The function returns the string between the delimiters or \lua{nil} if
+no delimiters could be found.
+%
+% Die Begrenzungszeichen sind im Ergebnis nicht enthalten.
+The delimiters themselves are not included in the result.
+%
+% Nach dem \latex{\directlua{}} darf das Makro, indem
+% \lua{utils.scan_oarg} eingesetzt wird, zu keinen Zeichen expandieren.
+After the \latex{\directlua{}}, the macro using \lua{utils.scan_oarg}
+must not expand to any characters.
+
+\InputLatex{utils/scan-oarg-plain.tex}
+
+%%
+%
+%%
+
+\subsection{Table “version”}
+
+% Das luakeys Projekt verwendet semantic versioning
+The luakeys project uses semantic versioning.
+% Die drei Versionenzahlen des Semantic Versioning Schemas werden in
+% einer Tabelle in der Reihenfolge MAJOR, MINOR, PATCH als Ganzzahlen
+% abgelegt.
+The three version numbers of the semantic versioning scheme are stored
+in a table as integers in the order MAJOR, MINOR, PATCH.
+% Mit Hilfe dieser Tabelle kann überprüft werden, ob die richtige
+% Version installiert ist.
+This table can be used to check whether the correct version is
+installed.
+
+\InputLua[firstline=4,lastline=10]{version.lua}
+
 %-----------------------------------------------------------------------
 %
 %-----------------------------------------------------------------------
@@ -1128,10 +1398,12 @@
   \alt `ex' | `EX'
   \alt `in' | `IN'
   \alt `mm' | `MM'
+  \alt `mu' | `MU'
   \alt `nc' | `NC'
   \alt `nd' | `ND'
   \alt `pc' | `PC'
   \alt `pt' | `PT'
+  \alt `px' | `PX'
   \alt `sp' | `SP'
 
 <boolean> ::= <boolean-true> | <boolean-false>
@@ -1156,16 +1428,10 @@
 \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}
+\InputLatex[firstline=5,lastline=12]{luakeysdebug/boolean-latex.tex}
+
+\columnbreak
+
 \begin{minted}{lua}
 {
   ['lower case true'] = true,
@@ -1185,18 +1451,19 @@
 \subsubsection{number}
 
 \begin{multicols}{2}
-\begin{minted}{latex}
-\luakeysdebug{
-  num1 = 4,
-  num2 = -4,
-  num3 = 0.4
-}
-\end{minted}
+\InputLatex[firstline=5,lastline=13]{luakeysdebug/number-latex.tex}
+
+\columnbreak
+
 \begin{minted}{lua}
 {
-  ['num1'] = 4,
-  ['num2'] = -4,
-  ['num3'] = 0.4
+  ['num0'] = 42,
+  ['num1'] = 42,
+  ['num2'] = -42,
+  ['num3'] = 4.2,
+  ['num4'] = 0.42,
+  ['num5'] = 0.42,
+  ['num6'] = '0 . 42', -- string
 }
 \end{minted}
 \end{multicols}
@@ -1207,6 +1474,22 @@
 
 \subsubsection{dimension}
 
+% Luakeys versucht alle Einheiten zu erkennen, die in der TeX-Welt
+% verwendet werden.
+|luakeys| tries to recognize all units used in the \TeX{} world.
+%
+% Nach dem Lua\TeX-Quellcode .
+According to the Lua\TeX{} source code
+(\href{https://github.com/TeX-Live/luatex/blob/51db1985f5500dafd2393aa2e403fefa57d3cb76/source/texk/web2c/luatexdir/lua/ltexlib.c#L434-L625}
+{source/texk/web2c/luatexdir/lua/ltexlib.c})
+%
+% und nach dem Dimensionen-Modul der lulibs-Bibliothek
+and the dimension module of the lualibs library
+(\href{https://raw.githubusercontent.com/latex3/lualibs/master/lualibs-util-dim.lua}
+{lualibs-util-dim.lua}),
+%
+% müssten alle Einheiten erkannt werden
+all units should be recognized.
 \begin{multicols}{3}
 \tiny
 \begin{center}
@@ -1221,10 +1504,12 @@
 ex & vertical measure of \emph{x} \\
 in & inch \\
 mm & milimeter \\
+mu & math unit \\
 nc & new cicero \\
 nd & new didot \\
 pc & pica \\
 pt & point \\
+px & x height current font \\
 sp & scaledpoint \\
 \end{tabular}
 \end{center}
@@ -1231,23 +1516,7 @@
 
 \columnbreak
 
-\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}
+\InputLatex[firstline=5,lastline=21]{luakeysdebug/dimension/all-latex.tex}
 
 \columnbreak
 
@@ -1261,15 +1530,39 @@
   ['ex'] = 282460,
   ['in'] = 4736286,
   ['mm'] = 186467,
+  ['mu'] = 65536,
   ['nc'] = 839105,
   ['nd'] = 69925,
   ['pc'] = 786432,
   ['pt'] = 65536,
+  ['px'] = 65781,
   ['sp'] = 1,
 }
 \end{minted}
 \end{multicols}
 
+\noindent
+% Im nächsten Beispiel werden die unterschiedlichen Notationsformen der
+% Dimensionen illustriert.
+The next example illustrates the different notations of the dimensions.
+
+\begin{multicols}{2}
+\InputLatex[firstline=5,lastline=12]{luakeysdebug/dimension/notations-latex.tex}
+
+\columnbreak
+
+\begin{minted}{lua}
+{
+  ['upper'] = 1864679,
+  ['lower'] = 1864679,
+  ['space'] = 1864679,
+  ['plus'] = 1864679,
+  ['minus'] = -1864679,
+  ['nodim'] = '1 c m', -- string
+}
+\end{minted}
+\end{multicols}
+
 %%
 %
 %%
@@ -1287,7 +1580,7 @@
 If the text have to contain commas, curly braces or equal signs, then
 double quotes must be used.
 
-\InputLuaExample[firstline=4,lastline=17]{data-types/string.lua}
+\InputLua[firstline=4,lastline=17]{data-types/string.lua}
 
 \subsubsection{Naked keys}
 
@@ -1303,7 +1596,7 @@
 % Index ist 1).
 In Lua an array is a table with numeric indexes (The first index is 1).
 
-\InputLatexExample[firstline=5,lastline=12]{luakeysdebug/naked-keys.tex}
+\InputLatex[firstline=5,lastline=12]{luakeysdebug/naked-keys-latex.tex}
 
 \noindent
 % VAlle erkannten Datentypen können als eigenständige Werte verwendet
@@ -1310,7 +1603,7 @@
 % werden.
 All recognized data types can be used as standalone values.
 
-\InputLatexExample[firstline=14,lastline=19]{luakeysdebug/naked-keys.tex}
+\InputLatex[firstline=14,lastline=19]{luakeysdebug/naked-keys-latex.tex}
 
 %-----------------------------------------------------------------------
 %
@@ -1320,6 +1613,10 @@
 
 \section{Examples}
 
+%%
+%
+%%
+
 \subsection{Extend and modify keys of existing macros}
 
 Extend the includegraphics macro with a new key named \latex{caption}
@@ -1327,17 +1624,34 @@
 between 0 and 1 is allowed and converted into
 \latex{width=0.5\linewidth}
 
-\InputLuaExample{extend-includegraphics/extend-keys.lua}
-\InputLatexExample{extend-includegraphics/extend-keys.tex}
+\InputLua{extend-includegraphics/extend-keys.lua}
+\InputLatex{extend-includegraphics/extend-keys-latex.tex}
 
+%%
+%
+%%
+
 \subsection{Process document class options}
 
-\begin{minted}{latex}
-\directlua{luakeys.parse('\@classoptionslist')}
-\end{minted}
+% Auf die Optionen einer \LaTeX{} Dokumentenklasse kann über das Macro
+% \latex{\@classoptionslist} zu gegriffen werden.
+The options of a \LaTeX{} document class can be accessed via the
+\latex{\@classoptionslist} macro.
+%
+% Die Zeichenkette des Makros \latex{\@classoptionslist} ist bereits
+% expandiert und normalisiert.
+The string of the macro \latex{\@classoptionslist} is already expanded
+and normalized.
+%
+% Neben Leerzeichen werden auch die geschweiften Klammern entfernt.
+In addition to spaces, the curly brackets are also removed.
+% Es ist deshalb nicht möglich, in einander geschachtelte hierarische
+% Schlüssel-Wert-Paare über die Option zu verarbeiten.
+It is therefore not possible to process nested hierarchical key-value
+pairs via the option.
 
-\InputLatexExample{class-options/test-class.cls}
-\InputLatexExample{class-options/use-test-class.tex}
+\InputLatex{class-options/test-class.cls}
+\InputLatex{class-options/use-test-class-latex.tex}
 
 \begin{minted}{lua}
 {
@@ -1458,30 +1772,30 @@
 
 \inputminted[linenos=true]{latex}{luakeys-debug.sty}
 
-\changes{v0.1}{2021/01/18}{Inital release}
-\changes{v0.2}{2021/09/19}{
+\changes{0.1.0}{2021/01/18}{Inital release}
+\changes{0.2.0}{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}{
+\changes{0.3.0}{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}{
+\changes{0.4.0}{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
 }
-\changes{v0.5}{2022/04/04}{
+\changes{0.5.0}{2022/04/04}{
 * Add possibility to change options globally
 * New option: standalone_as_true
 * Add a recursive converter callback / hook to process the parse tree
 * New option: case_insensitive_keys
 }
-\changes{v0.6}{2022/06/09}{
+\changes{0.6.0}{2022/06/09}{
 * New feature: keys now can be defined using the function
   “define(defs, opts)” or “define(kv_string, { defs = { key = { ... } } })”
 * Rename the global options table from “default_options” to “opts”
@@ -1494,6 +1808,14 @@
   produce the previous effect.
 * The function “print()” is now called “debug()”
 }
+\changes{0.7.0}{2022/07/06}{
+* The project now uses semantic versioning.
+* New definition attribute “pick” to pick standalone values and assign
+  them to a key
+* New function “utils.scan_oarg()” to search for an optional argument,
+  that means scan for tokens that are enclosed in square brackets
+* extend and improve documentation
+}
 \pagebreak
 \PrintChanges
 \pagebreak

Modified: trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.sty
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.sty	2022-07-07 19:54:15 UTC (rev 63828)
+++ trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.sty	2022-07-07 19:54:23 UTC (rev 63829)
@@ -17,6 +17,6 @@
 % luakeys-debug.sty and luakeys-debug.tex.
 
 \NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{luakeys-debug}[2022/06/09 v0.6 Debug package for luakeys.]
+\ProvidesPackage{luakeys-debug}[2022/07/06 0.7.0 Debug package for luakeys.]
 
 \input luakeys-debug.tex

Modified: trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.tex
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.tex	2022-07-07 19:54:15 UTC (rev 63828)
+++ trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys-debug.tex	2022-07-07 19:54:23 UTC (rev 63829)
@@ -16,50 +16,29 @@
 % This work consists of the files luakeys.lua, luakeys.sty, luakeys.tex
 % luakeys-debug.sty and luakeys-debug.tex.
 
-\directlua{
+\directlua
+{
   luakeys = require('luakeys')
 }
 
-% https://tex.stackexchange.com/a/418401/42311
-\catcode`\@=11
-\long\def\LuaKeysIfNextChar#1#2#3{%
-  \let\@tmpa=#1%
-  \def\@tmpb{#2}%
-  \def\@tmpc{#3}%
-  \futurelet\@future\LuaKeysIfNextChar at i%
-}%
-\def\LuaKeysIfNextChar at i{%
-  \ifx\@tmpa\@future%
-    \expandafter\@tmpb
-  \else
-    \expandafter\@tmpc
-  \fi
-}%
-\def\luakeysdebug at parse@options#1{
-  \directlua{
-    luakeys.save(
-      'debug_options',
-      luakeys.parse('#1', { format_keys = { 'snake', 'lower' } })
+\def\luakeysdebug%
+{%
+  \directlua%
+  {
+    local oarg = luakeys.utils.scan_oarg()
+    local marg = token.scan_argument(false)
+    local opts
+    if oarg then
+      opts = luakeys.parse(oarg, { format_keys = { 'snake', 'lower' } })
+    end
+    local result = luakeys.parse(marg, opts)
+    luakeys.debug(result)
+    tex.print(
+      '{' ..
+        '\string\\tt' ..
+        '\string\\parindent=0pt' ..
+        luakeys.stringify(result, true) ..
+      '}'
     )
-  }
-}%
-\def\luakeysdebug at output#1{
-  {
-    \tt
-    \parindent=0pt
-    \directlua{
-      local result = luakeys.parse('\luaescapestring{\unexpanded{#1}}', luakeys.get('debug_options'))
-      tex.print(luakeys.stringify(result, true))
-      luakeys.debug(result)
-    }
-  }
-}%
-\def\luakeysdebug at oarg[#1]#2{%
-  \luakeysdebug at parse@options{#1}%
-  \luakeysdebug at output{#2}%
-}%
-\def\luakeysdebug at marg#1{%
-  \luakeysdebug at output{#1}%
-}%
-\def\luakeysdebug{\LuaKeysIfNextChar[{\luakeysdebug at oarg}{\luakeysdebug at marg}}%
-\catcode`\@=12
+  }%
+}

Modified: trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys.lua	2022-07-07 19:54:15 UTC (rev 63828)
+++ trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys.lua	2022-07-07 19:54:23 UTC (rev 63829)
@@ -36,13 +36,59 @@
   }
 end
 
+--- Merge two tables in the first specified table.
+--- The `merge_tables` function copies all keys from the `source` table
+--- to a target table. It returns the modified target table.
+---
+--- at see https://stackoverflow.com/a/1283608/10193818
+---
+--- at param target table
+--- at param source table
+---
+--- at return table target The modified target table.
+local function merge_tables(target, source)
+  for key, value in pairs(source) do
+    if type(value) == 'table' then
+      if type(target[key] or false) == 'table' then
+        merge_tables(target[key] or {}, source[key] or {})
+      elseif target[key] == nil then
+        target[key] = value
+      end
+    elseif target[key] == nil then
+      target[key] = value
+    end
+  end
+  return target
+end
+
+---Clone a table.
+---
+--- at see http://lua-users.org/wiki/CopyTable
+---
+--- at param orig table
+---
+--- at return table
+local function clone_table(orig)
+  local orig_type = type(orig)
+  local copy
+  if orig_type == 'table' then
+    copy = {}
+    for orig_key, orig_value in next, orig, nil do
+      copy[clone_table(orig_key)] = clone_table(orig_value)
+    end
+    setmetatable(copy, clone_table(getmetatable(orig)))
+  else -- number, string, boolean, etc
+    copy = orig
+  end
+  return copy
+end
+
 local utils = {
   --- Get the size of an array like table `{ 'one', 'two', 'three' }` = 3.
-  --
-  -- @tparam table value A table or any input.
-  --
-  -- @treturn number The size of the array like table. 0 if the input is
-  -- no table or the table is empty.
+  ---
+  --- at param value table # A table or any input.
+  ---
+  --- at return number # The size of the array like table. 0 if the input is no table or the table is empty.
   get_array_size = function(value)
     local count = 0
     if type(value) == 'table' then
@@ -53,12 +99,11 @@
     return count
   end,
 
-  --- Get the size of a table `{ one = 'one', 'two', 'three' }` = 3.
-  --
-  -- @tparam table value A table or any input.
-  --
-  -- @treturn number The size of the array like table. 0 if the input is
-  -- no table or the table is empty.
+  ---Get the size of a table `{ one = 'one', 'two', 'three' }` = 3.
+  ---
+  --- at param value table|any # A table or any input.
+  ---
+  --- at return number # The size of the array like table. 0 if the input is no table or the table is empty.
   get_table_size = function(value)
     local count = 0
     if type(value) == 'table' then
@@ -69,6 +114,10 @@
     return count
   end,
 
+  merge_tables = merge_tables,
+
+  clone_table = clone_table,
+
   remove_from_array = function(array, element)
     for index, value in pairs(array) do
       if element == value then
@@ -77,58 +126,95 @@
       end
     end
   end,
-}
 
---- https://stackoverflow.com/a/1283608/10193818
-local function merge_tables(target, t2)
-  for k, v in pairs(t2) do
-    if type(v) == 'table' then
-      if type(target[k] or false) == 'table' then
-        merge_tables(target[k] or {}, t2[k] or {})
-      elseif target[k] == nil then
-        target[k] = v
+  ---Scan for an optional argument.
+  ---
+  --- at param initial_delimiter? string # The character that marks the beginning of an optional argument (by default `[`).
+  --- at param end_delimiter? string # The character that marks the end of an optional argument (by default `]`).
+  ---
+  --- at return string|nil # The string that was enclosed by the delimiters. The delimiters themselves are not returned.
+  scan_oarg = function(initial_delimiter, end_delimiter)
+    if initial_delimiter == nil then
+      initial_delimiter = '['
+    end
+
+    if end_delimiter == nil then
+      end_delimiter = ']'
+    end
+
+    local function convert_token(t)
+      if t.index ~= nil then
+        return utf8.char(t.index)
+      else
+        return '\\' .. t.csname
       end
-    elseif target[k] == nil then
-      target[k] = v
     end
-  end
-  return target
-end
 
---- http://lua-users.org/wiki/CopyTable
-local function clone_table(orig)
-  local orig_type = type(orig)
-  local copy
-  if orig_type == 'table' then
-    copy = {}
-    for orig_key, orig_value in next, orig, nil do
-      copy[clone_table(orig_key)] = clone_table(orig_value)
+    local function get_next_char()
+      local t = token.get_next()
+      return convert_token(t), t
     end
-    setmetatable(copy, clone_table(getmetatable(orig)))
-  else -- number, string, boolean, etc
-    copy = orig
-  end
-  return copy
-end
 
---- This table stores all allowed option keys.
-local all_options = {
-  convert_dimensions = false,
-  debug = false,
-  default = true,
-  defaults = false,
-  defs = false,
-  format_keys = false,
-  hooks = {},
-  naked_as_value = false,
-  no_error = false,
-  postprocess = false,
-  preprocess = false,
-  unpack = true,
+    local char, t = get_next_char()
+    if char == initial_delimiter then
+      local oarg = {}
+      char = get_next_char()
+      while char ~= end_delimiter do
+        table.insert(oarg, char)
+        char = get_next_char()
+      end
+      return table.concat(oarg, '')
+    else
+      token.put_next(t)
+    end
+  end,
 }
 
+local namespace = {
+  opts = {
+    convert_dimensions = false,
+    debug = false,
+    default = true,
+    defaults = false,
+    defs = false,
+    format_keys = false,
+    hooks = {},
+    naked_as_value = false,
+    no_error = false,
+    unpack = true,
+  },
+
+  hooks = {
+    kv_string = true,
+    keys_before_opts = true,
+    result_before_opts = true,
+    keys_before_def = true,
+    result_before_def = true,
+    keys = true,
+    result = true,
+  },
+
+  attrs = {
+    alias = true,
+    always_present = true,
+    choices = true,
+    data_type = true,
+    default = true,
+    exclusive_group = true,
+    l3_tl_set = true,
+    macro = true,
+    match = true,
+    name = true,
+    opposite_keys = true,
+    pick = true,
+    process = true,
+    required = true,
+    sub_keys = true,
+  },
+}
+
 --- The default options.
-local default_options = clone_table(all_options)
+local default_options = clone_table(namespace.opts)
 
 local function throw_error(message)
   if type(tex.error) == 'function' then
@@ -144,16 +230,15 @@
 -- @section
 
 --- The function `render(tbl)` reverses the function
---  `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. In Lua only tables with
---  1-based consecutive integer keys (a.k.a. array tables) can be
---  parsed in order.
---
--- @tparam table result A table to be converted into a key-value string.
---
--- @treturn string A key-value string that can be passed to a TeX
--- macro.
+--- `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. In Lua only tables with
+--- 1-based consecutive integer keys (a.k.a. array tables) can be
+--- parsed in order.
+---
+--- at param result table # A table to be converted into a key-value string.
+---
+--- at return string # A key-value string that can be passed to a TeX macro.
 local function render(result)
   local function render_inner(result)
     local output = {}
@@ -183,20 +268,19 @@
 end
 
 --- The function `stringify(tbl, for_tex)` converts a Lua table into a
---   printable string. Stringify a table means to convert the table into
---   a string. This function is used to realize the `debug` function.
---   `stringify(tbl, true)` (`for_tex = true`) generates a string which
---   can be embeded into TeX documents. The macro `\luakeysdebug{}` uses
---   this option. `stringify(tbl, false)` or `stringify(tbl)` generate a
---   string suitable for the terminal.
---
--- @tparam table result A table to stringify.
---
--- @tparam boolean for_tex Stringify the table into a text string that
---   can be embeded inside a TeX document via tex.print(). Curly braces
---   and whites spaces are escaped.
---
--- https://stackoverflow.com/a/54593224/10193818
+--- printable string. Stringify a table means to convert the table into
+--- a string. This function is used to realize the `debug` function.
+--- `stringify(tbl, true)` (`for_tex = true`) generates a string which
+--- can be embeded into TeX documents. The macro `\luakeysdebug{}` uses
+--- this option. `stringify(tbl, false)` or `stringify(tbl)` generate a
+--- string suitable for the terminal.
+---
+--- at see https://stackoverflow.com/a/54593224/10193818
+---
+--- at param result table # A table to stringify.
+--- at param for_tex? boolean # Stringify the table into a text string that can be embeded inside a TeX document via tex.print(). Curly braces and whites spaces are escaped.
+---
+--- at return string
 local function stringify(result, for_tex)
   local line_break, start_bracket, end_bracket, indent
 
@@ -242,7 +326,8 @@
             add(0, stringify_inner(value, depth + 1))
             add(depth, end_bracket .. ',');
           else
-            add(depth, key .. ' = ' .. start_bracket .. end_bracket .. ',')
+            add(depth,
+              key .. ' = ' .. start_bracket .. end_bracket .. ',')
           end
         else
           if (type(value) == 'string') then
@@ -259,19 +344,17 @@
     return table.concat(output, line_break)
   end
 
-  return
-    start_bracket .. line_break .. stringify_inner(result, 1) .. line_break ..
-      end_bracket
+  return start_bracket .. line_break .. stringify_inner(result, 1) ..
+           line_break .. end_bracket
 end
 
---- The function `debug(tbl)` pretty prints a Lua table to standard
+--- The function `debug(result)` 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
 --   `parse`. You have to compile your TeX document in a console to
 --   see the terminal output.
 --
--- @tparam table result A table to be printed to standard output for
--- debugging purposes.
+--- at param result table # A table to be printed to standard output for debugging purposes.
 local function debug(result)
   print('\n' .. stringify(result, false))
 end
@@ -280,19 +363,23 @@
 -- @section
 
 --- Generate the PEG parser using Lpeg.
---
--- Explanations of some LPeg notation forms:
---
--- * `patt ^ 0` = `expression *`
--- * `patt ^ 1` = `expression +`
--- * `patt ^ -1` = `expression ?`
--- * `patt1 * patt2` = `expression1 expression2`: Sequence
--- * `patt1 + patt2` = `expression1 / expression2`: Ordered choice
---
--- * [TUGboat article: Parsing complex data formats in LuaTEX with LPEG](https://tug.org/TUGboat/tb40-2/tb125menke-Patterndf)
---
--- @treturn userdata The parser.
-local function generate_parser(initial_rule, convert_dimensions)
+---
+--- Explanations of some LPeg notation forms:
+---
+--- * `patt ^ 0` = `expression *`
+--- * `patt ^ 1` = `expression +`
+--- * `patt ^ -1` = `expression ?`
+--- * `patt1 * patt2` = `expression1 expression2`: Sequence
+--- * `patt1 + patt2` = `expression1 / expression2`: Ordered choice
+---
+--- * [TUGboat article: Parsing complex data formats in LuaTEX with LPEG](https://tug.or-g/TUGboat/tb40-2/tb125menke-Patterndf)
+---
+--- at param initial_rule string # The name of the first rule of the grammar table passed to the `lpeg.P(attern)` function (e. g. `list`, `number`).
+--- at param convert_dimensions? boolean # Whether the dimensions should be converted to scaled points (by default `false`).
+---
+--- at return userdata # The parser.
+local function generate_parser(initial_rule,
+  convert_dimensions)
   if convert_dimensions == nil then
     convert_dimensions = false
   end
@@ -315,7 +402,17 @@
     return white_space ^ 0 * Pattern(match) * white_space ^ 0
   end
 
+  --- Convert a dimension to an normalized dimension string or an
+  --- integer in the scaled points format.
+  ---
+  --- at param input string
+  ---
+  --- at return integer|string # A dimension as an integer or a dimension string.
   local capture_dimension = function(input)
+    -- Remove all whitespaces
+    input = input:gsub('%s+', '')
+    -- Convert the unit string into lowercase.
+    input = input:lower()
     if convert_dimensions then
       return tex.sp(input)
     else
@@ -325,27 +422,27 @@
 
   --- Add values to a table in two modes:
   --
-  -- # Key value pair
+  -- Key-value pair:
   --
-  -- If arg1 and arg2 are not nil, then arg1 is the key and arg2 is the
+  -- If `arg1` and `arg2` are not nil, then `arg1` is the key and `arg2` is the
   -- value of a new table entry.
   --
-  -- # Index value
+  -- Indexed value:
   --
-  -- If arg2 is nil, then arg1 is the value and is added as an indexed
+  -- If `arg2` is nil, then `arg1` is the value and is added as an indexed
   -- (by an integer) value.
   --
-  -- @tparam table table
-  -- @tparam mixed arg1
-  -- @tparam mixed arg2
-  --
-  -- @treturn table
-  local add_to_table = function(table, arg1, arg2)
+  --- at param result table # The result table to which an additional key-value pair or value should to be added
+  --- at param arg1 any # The key or the value.
+  --- at param arg2? any # Always the value.
+  ---
+  --- at return table # The result table to which an additional key-value pair or value has been added.
+  local add_to_table = function(result, arg1, arg2)
     if arg2 == nil then
-      local index = #table + 1
-      return rawset(table, index, arg1)
+      local index = #result + 1
+      return rawset(result, index, arg1)
     else
-      return rawset(table, arg1, arg2)
+      return rawset(result, arg1, arg2)
     end
   end
 
@@ -438,6 +535,7 @@
 
     -- 'bp' / 'BP' / 'cc' / etc.
     -- https://raw.githubusercontent.com/latex3/lualibs/master/lualibs-util-dim.lua
+    -- https://github.com/TeX-Live/luatex/blob/51db1985f5500dafd2393aa2e403fefa57d3cb76/source/texk/web2c/luatexdir/lua/ltexlib.c#L434-L625
     unit =
       Pattern('bp') + Pattern('BP') +
       Pattern('cc') + Pattern('CC') +
@@ -447,10 +545,12 @@
       Pattern('ex') + Pattern('EX') +
       Pattern('in') + Pattern('IN') +
       Pattern('mm') + Pattern('MM') +
+      Pattern('mu') + Pattern('MU') +
       Pattern('nc') + Pattern('NC') +
       Pattern('nd') + Pattern('ND') +
       Pattern('pc') + Pattern('PC') +
       Pattern('pt') + Pattern('PT') +
+      Pattern('px') + Pattern('PX') +
       Pattern('sp') + Pattern('SP'),
 
     -- '"' ('\"' / !'"')* '"'
@@ -483,7 +583,8 @@
     callback_func)
     for key, value in pairs(current) do
       if type(value) == 'table' then
-        value = visit_tree_recursive(tree, value, {}, depth + 1, callback_func)
+        value = visit_tree_recursive(tree, value, {}, depth + 1,
+          callback_func)
       end
 
       key, value = callback_func(key, value, depth, current, tree)
@@ -514,7 +615,7 @@
       return true
     end
     local parser = generate_parser('boolean_only', false)
-    local result = parser:match(value)
+    local result = parser:match(tostring(value))
     return result ~= nil
   end,
 
@@ -523,7 +624,7 @@
       return false
     end
     local parser = generate_parser('dimension_only', false)
-    local result = parser:match(value)
+    local result = parser:match(tostring(value))
     return result ~= nil
   end,
 
@@ -543,7 +644,7 @@
       return true
     end
     local parser = generate_parser('number_only', false)
-    local result = parser:match(value)
+    local result = parser:match(tostring(value))
     return result ~= nil
   end,
 
@@ -555,14 +656,13 @@
 --- Apply the key-value-pair definitions (defs) on an input table in a
 --- recursive fashion.
 ---
---- at param defs table A table containing all definitions.
---- at param opts table The parse options table.
---- at param input table The current input table.
---- at param output table The current output table.
---- at param unknown table Always the root unknown table.
---- at param key_path table An array of key names leading to the current
---- at param input_root table The root input table
----  input and output table.
+--- at param defs table # A table containing all definitions.
+--- at param opts table # The parse options table.
+--- at param input table # The current input table.
+--- at param output table # The current output table.
+--- at param unknown table # Always the root unknown table.
+--- at param key_path table # An array of key names leading to the current
+--- at param input_root table # The root input table input and output table.
 local function apply_definitions(defs,
   opts,
   input,
@@ -583,7 +683,7 @@
     return new_key_path
   end
 
-  local function set_default_value(def)
+  local function get_default_value(def)
     if def.default ~= nil then
       return def.default
     elseif opts ~= nil and opts.default ~= nil then
@@ -599,7 +699,7 @@
       return value
       -- naked keys: values with integer keys
     elseif utils.remove_from_array(input, search_key) ~= nil then
-      return set_default_value(def)
+      return get_default_value(def)
     end
   end
 
@@ -634,7 +734,7 @@
 
     always_present = function(value, key, def)
       if value == nil and def.always_present then
-        return set_default_value(def)
+        return get_default_value(def)
       end
     end,
 
@@ -692,9 +792,10 @@
           throw_error('Unknown data type: ' .. def.data_type)
         end
         if converted == nil then
-          throw_error('The value “' .. value .. '” of the key “' .. key ..
-                        '” could not be converted into the data type “' ..
-                        def.data_type .. '”!')
+          throw_error(
+            'The value “' .. value .. '” of the key “' .. key ..
+              '” could not be converted into the data type “' ..
+              def.data_type .. '”!')
         else
           return converted
         end
@@ -723,7 +824,8 @@
         return
       end
       if def.l3_tl_set ~= nil then
-        tex.print(l3_code_cctab, '\\tl_set:Nn \\g_' .. def.l3_tl_set .. '_tl')
+        tex.print(l3_code_cctab,
+          '\\tl_set:Nn \\g_' .. def.l3_tl_set .. '_tl')
         tex.print('{' .. value .. '}')
       end
     end,
@@ -747,8 +849,9 @@
         end
         local match = string.match(value, def.match)
         if match == nil then
-          throw_error('The value “' .. value .. '” of the key “' .. key ..
-                        '” does not match “' .. def.match .. '”!')
+          throw_error(
+            'The value “' .. value .. '” of the key “' .. key ..
+              '” does not match “' .. def.match .. '”!')
         else
           return match
         end
@@ -780,6 +883,41 @@
       end
     end,
 
+    pick = function(value, key, def)
+      if def.pick then
+        if type(def.pick) == 'string' and is[def.pick] == nil then
+          throw_error(
+            'Wrong setting. Allowed settings for the attribute “def.pick” are: true, boolean, dimension, integer, number, string. Got “' ..
+              tostring(def.pick) .. '”.')
+        end
+
+        if value ~= nil then
+          return value
+        end
+        for i, v in pairs(input) do
+          -- We can not use ipairs here. `ipairs(t)` iterates up to the
+          -- first absent index. Values are deleted from the `input`
+          -- table.
+          if type(i) == 'number' then
+            local picked_value = nil
+            -- Pick a value by type:  boolean, dimension, integer, number, string
+            if is[def.pick] ~= nil then
+              if is[def.pick](v) then
+                picked_value = v
+              end
+              -- Pick every value
+            elseif v ~= nil then
+              picked_value = v
+            end
+            if picked_value ~= nil then
+              input[i] = nil
+              return picked_value
+            end
+          end
+        end
+      end
+    end,
+
     required = function(value, key, def)
       if def.required ~= nil and def.required and value == nil then
         throw_error(string.format('Missing required key “%s”!', key))
@@ -797,8 +935,8 @@
         elseif type(value) == 'table' then
           v = value
         end
-        v = apply_definitions(def.sub_keys, opts, v, output[key], unknown,
-          add_to_key_path(key_path, key), input_root)
+        v = apply_definitions(def.sub_keys, opts, v, output[key],
+          unknown, add_to_key_path(key_path, key), input_root)
         if utils.get_table_size(v) > 0 then
           return v
         end
@@ -821,23 +959,33 @@
   end
 
   for index, def in pairs(defs) do
-    --- Find key and def
+    -- Find key and def
     local key
-    if type(def) == 'table' and def.name == nil and type(index) == 'string' then
+    -- `{ key1 = { }, key2 = { } }`
+    if type(def) == 'table' and def.name == nil and type(index) ==
+      'string' then
       key = index
+      -- `{ { name = 'key1' }, { name = 'key2' } }`
     elseif type(def) == 'table' and def.name ~= nil then
       key = def.name
+      -- Definitions as strings in an array: `{ 'key1', 'key2' }`
     elseif type(index) == 'number' and type(def) == 'string' then
       key = def
-      def = { default = true }
+      def = { default = get_default_value({}) }
     end
 
     if type(def) ~= 'table' then
-      throw_error('Key definition must be a table')
+      throw_error('Key definition must be a table!')
     end
 
+    for attr, _ in pairs(def) do
+      if namespace.attrs[attr] == nil then
+        throw_error('Unknown definition attribute: ' .. tostring(attr))
+      end
+    end
+
     if key == nil then
-      throw_error('key name couldn’t be detected!')
+      throw_error('Key name couldn’t be detected!')
     end
 
     local value = find_value(key, def)
@@ -845,6 +993,7 @@
     for _, def_opt in ipairs({
       'alias',
       'opposite_keys',
+      'pick',
       'always_present',
       'required',
       'data_type',
@@ -888,14 +1037,14 @@
 
 --- Parse a LaTeX/TeX style key-value string into a Lua table.
 ---
---- at param kv_string string A string in the TeX/LaTeX style key-value format as described above.
---- at param opts table A table containing the settings:
+--- at param kv_string string # A string in the TeX/LaTeX style key-value format as described above.
+--- at param opts table # A table containing the settings:
 ---   `convert_dimensions`, `unpack`, `naked_as_value`, `converter`,
 ---   `debug`, `preprocess`, `postprocess`.
 --
---- at return table result The final result of all individual parsing and normalization steps.
---- at return table unknown A table with unknown, undefinied key-value pairs.
---- at return table raw The unprocessed, raw result of the LPeg parser.
+--- at return table result # The final result of all individual parsing and normalization steps.
+--- at return table unknown # A table with unknown, undefinied key-value pairs.
+--- at return table raw # The unprocessed, raw result of the LPeg parser.
 local function parse(kv_string, opts)
   if kv_string == nil then
     return {}
@@ -903,21 +1052,21 @@
 
   --- Normalize the parse options.
   ---
-  --- @param opts table Options in a raw format. The table may be empty or some keys are not set.
+  --- at param opts table # Options in a raw format. The table may be empty or some keys are not set.
   ---
-  --- @return table
+  --- at return table
   local function normalize_opts(opts)
     if type(opts) ~= 'table' then
       opts = {}
     end
     for key, _ in pairs(opts) do
-      if all_options[key] == nil then
+      if namespace.opts[key] == nil then
         throw_error('Unknown parse option: ' .. tostring(key) .. '!')
       end
     end
     local old_opts = opts
     opts = {}
-    for name, _ in pairs(all_options) do
+    for name, _ in pairs(namespace.opts) do
       if old_opts[name] ~= nil then
         opts[name] = old_opts[name]
       else
@@ -925,18 +1074,8 @@
       end
     end
 
-    local hooks = {
-      kv_string = true,
-      keys_before_opts = true,
-      result_before_opts = true,
-      keys_before_def = true,
-      result_before_def = true,
-      keys = true,
-      result = true,
-    }
-
     for hook in pairs(opts.hooks) do
-      if hooks[hook] == nil then
+      if namespace.hooks[hook] == nil then
         throw_error('Unknown hook: ' .. tostring(hook) .. '!')
       end
     end
@@ -983,8 +1122,8 @@
   --  tasks are performed on the raw input table coming directly from
   --  the PEG parser:
   --
-  --- @param result table The raw input table coming directly from the PEG parser
-  --- @param opts table Some options.
+  --- at param result table # The raw input table coming directly from the PEG parser
+  --- at param opts table # Some options.
   local function apply_opts(result, opts)
     local callbacks = {
       unpack = function(key, value)
@@ -1012,7 +1151,8 @@
             elseif style == 'upper' then
               key = key:upper()
             else
-              throw_error('Unknown style to format keys: ' .. tostring(style) ..
+              throw_error('Unknown style to format keys: ' ..
+                            tostring(style) ..
                             ' Allowed styles are: lower, snake, upper')
             end
           end
@@ -1031,8 +1171,9 @@
 
     if opts.format_keys then
       if type(opts.format_keys) ~= 'table' then
-        throw_error('The option “format_keys” has to be a table not ' ..
-                      type(opts.format_keys))
+        throw_error(
+          'The option “format_keys” has to be a table not ' ..
+            type(opts.format_keys))
       end
       result = visit_tree(result, callbacks.format_key)
     end
@@ -1045,8 +1186,8 @@
   local unknown = nil
   if type(opts.defs) == 'table' then
     apply_hooks('before_defs')
-    result, unknown = apply_definitions(opts.defs, opts, result, {}, {}, {},
-      clone_table(result))
+    result, unknown = apply_definitions(opts.defs, opts, result, {}, {},
+      {}, clone_table(result))
   end
 
   apply_hooks()
@@ -1077,6 +1218,26 @@
 -- @section
 
 local export = {
+  version = { 0, 7, 0 },
+
+  namespace = namespace,
+
+  ---This function is used in the documentation.
+  ---
+  --- at param from string # A key in the namespace table, either `opts`, `hook` or `attrs`.
+  print_names = function(from)
+    local names = {}
+    for name in pairs(namespace[from]) do
+      table.insert(names, name)
+    end
+    table.sort(names)
+    tex.print(table.concat(names, ', '))
+  end,
+
+  print_default = function(from, name)
+    tex.print(tostring(namespace[from][name]))
+  end,
+
   --- @see default_options
   opts = default_options,
 
@@ -1089,7 +1250,10 @@
   define = function(defs, opts)
     return function(kv_string, inner_opts)
       local options
-      if inner_opts ~= nil then
+
+      if inner_opts ~= nil and opts ~= nil then
+        options = merge_tables(opts, inner_opts)
+      elseif inner_opts ~= nil then
         options = inner_opts
       elseif opts ~= nil then
         options = opts
@@ -1116,11 +1280,9 @@
   --  Therefore, it is not necessary to pollute the global namespace to
   --  store results for the later usage.
   --
-  -- @tparam string identifier The identifier under which the result is
-  --   saved.
+  --- at param identifier string # The identifier under which the result is saved.
   --
-  -- @tparam table result A result to be stored and that was created by
-  --   the key-value parser.
+  --- at param result table # A result to be stored and that was created by the key-value parser.
   save = function(identifier, result)
     result_store[identifier] = result
   end,
@@ -1128,8 +1290,9 @@
   --- The function `get(identifier): table` retrieves a saved result
   --  from the result store.
   --
-  -- @tparam string identifier The identifier under which the result was
-  --   saved.
+  --- at param identifier string # The identifier under which the result was saved.
+  ---
+  --- at return table
   get = function(identifier)
     -- if result_store[identifier] == nil then
     --   throw_error('No stored result was found for the identifier \'' .. identifier .. '\'')
@@ -1138,6 +1301,8 @@
   end,
 
   is = is,
+
+  utils = utils,
 }
 
 return export

Modified: trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys.sty
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys.sty	2022-07-07 19:54:15 UTC (rev 63828)
+++ trunk/Master/texmf-dist/tex/luatex/luakeys/luakeys.sty	2022-07-07 19:54:23 UTC (rev 63829)
@@ -17,5 +17,5 @@
 % luakeys-debug.sty and luakeys-debug.tex.
 
 \NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{luakeys}[2022/06/09 v0.6 Parsing key-value options using Lua.]
+\ProvidesPackage{luakeys}[2022/07/06 0.7.0 Parsing key-value options using Lua.]
 \directlua{luakeys = require('luakeys')}



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