texlive[58815] Master/texmf-dist: expkv (10apr21)

commits+karl at tug.org commits+karl at tug.org
Sat Apr 10 23:10:11 CEST 2021


Revision: 58815
          http://tug.org/svn/texlive?view=revision&revision=58815
Author:   karl
Date:     2021-04-10 23:10:10 +0200 (Sat, 10 Apr 2021)
Log Message:
-----------
expkv (10apr21)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/expkv/README.md
    trunk/Master/texmf-dist/doc/latex/expkv/expkv.pdf
    trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx
    trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex
    trunk/Master/texmf-dist/tex/latex/expkv/expkv.sty

Modified: trunk/Master/texmf-dist/doc/latex/expkv/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/expkv/README.md	2021-04-10 21:09:57 UTC (rev 58814)
+++ trunk/Master/texmf-dist/doc/latex/expkv/README.md	2021-04-10 21:10:10 UTC (rev 58815)
@@ -1,7 +1,7 @@
 -------------------------------------------------------------------------------
 # expkv -- an expandable key=val implementation
 
-Version 2020-12-28 v1.6
+Version 2021-04-09 v1.7
 
 Released under the LaTeX Project Public License v1.3c or later
 See http://www.latex-project.org/lppl.txt
@@ -10,7 +10,7 @@
 
 -------------------------------------------------------------------------------
 
-Copyright (C) 2020 Jonathan P. Spratte
+Copyright (C) 2020-2021 Jonathan P. Spratte
 
 This  work may be  distributed and/or  modified under  the conditions  of the
 LaTeX Project Public License (LPPL),  either version 1.3c  of this license or

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

Modified: trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx	2021-04-10 21:09:57 UTC (rev 58814)
+++ trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx	2021-04-10 21:10:10 UTC (rev 58815)
@@ -1,6 +1,6 @@
 % \iffalse meta-comment
 %
-% File: expkv.dtx Copyright (C) 2020 Jonathan P. Spratte
+% File: expkv.dtx Copyright (C) 2020-2021 Jonathan P. Spratte
 %
 % This work  may be  distributed and/or  modified under  the conditions  of the
 % LaTeX Project Public License (LPPL),  either version 1.3c  of this license or
@@ -27,7 +27,7 @@
 See http://www.latex-project.org/lppl.txt
 --------------------------------------------------------------
 
-Copyright (C) 2020 Jonathan P. Spratte
+Copyright (C) 2020-2021 Jonathan P. Spratte
 
 This  work may be  distributed and/or  modified under  the conditions  of the
 LaTeX Project Public License (LPPL),  either version 1.3c  of this license or
@@ -84,10 +84,18 @@
     ,basicstyle=\fontfamily{jkp}\itshape
     ,morekeywords=^^A
       {^^A
-        \ekvdef,\ekvdefNoVal,\ekvlet,\ekvletNoVal,\ekvletkv,\ekvletkvNoVal,^^A
-        \ekvset,\ekvparse,\ekvVersion,\ekvDate,\ekvifdefined,^^A
-        \ekvifdefinedNoVal,\ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak,^^A
-        \ekvsneak,\ekvsneakPre,\ekvchangeset
+        \ekvdef,\ekvdefNoVal,^^A
+        \ekvlet,\ekvletNoVal,\ekvletkv,\ekvletkvNoVal,^^A
+        \ekvdefunknown,\ekvdefunknownNoVal,^^A
+        \ekvredirectunknown,\ekvredirectunknownNoVal,^^A
+        \ekvset,\ekvsetSneaked,^^A
+        \ekvsetdef,\ekvsetSneakeddef,\ekvsetdefSneaked,^^A
+        \ekvparse,^^A
+        \ekvVersion,\ekvDate,^^A
+        \ekvifdefined,\ekvifdefinedNoVal,\ekvifdefinedset,^^A
+        \ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak,^^A
+        \ekvsneak,\ekvsneakPre,^^A
+        \ekvchangeset
       }
     ,morecomment=[l]\%
     ,commentstyle=\color[gray]{0.4}
@@ -103,6 +111,7 @@
 \definecolor{expkvred}{HTML}{9F393D}
 \colorlet{expkvgrey}{black!75}
 \makeatletter
+\newcommand*\example{\par\smallskip\noindent\textit{Example:} \ignorespaces}
 \newcommand*\expFormat
   {^^A
     {^^A
@@ -164,6 +173,31 @@
 \newcommand*\expkvd{\expkvpkg{def}}
 \newcommand*\expkvc{\expkvpkg{cs}}
 \newcommand*\expkvo{\expkvpkg{opt}}
+\ExplSyntaxOn
+\newcommand*\pkglogo[1]
+  {
+    \texorpdfstring
+      {
+        \mbox
+          {
+            \BeginAccSupp{ActualText=#1}
+            \rmfamily
+            \str_case:nn {#1}
+              {
+                {yax}
+                  {
+                    Y\kern-.1em
+                    \raise.15em\hbox{\scshape a}
+                    \kern-.1em \lower.15em\hbox{X}%
+                  }
+              }
+            \EndAccSupp{}
+          }
+      }
+      {#1}
+  }
+\newcommand*\yax{\pkglogo{yax}}
+\ExplSyntaxOff
 \newcommand\kv{\meta{key}=\meta{value}}
 \newcommand\key{\meta{key}}
 \newcommand\val{\meta{value}}
@@ -173,6 +207,7 @@
 \DoNotIndex{\endgroup,\endinput,\errmessage,\expandafter,\input,\let,\long}
 \DoNotIndex{\protected,\ProvidesFile,\ProvidesPackage,\relax,\space}
 \DoNotIndex{\@,\unexpanded,\string,\expanded,\detokenize,\meaning,\lastnamedcs}
+\DoNotIndex{\romannumeral,\z@}
 \DoNotIndex{\ifcsname}
 \DoNotIndex{\ifx}
 \DoNotIndex{\else}
@@ -257,6 +292,9 @@
 % Note that while the package names are stylised with a vertical rule, their
 % names are all lower case with a hyphen (\emph{e.g.}, \file{expkv-def}).
 %
+% A list of concise comparisons to other \kv\ packages is contained in
+% \autoref{sec:cmp}.
+%
 % \subsection{Setting up Keys}\label{sec:define}
 % \expkv\ provides a rather simple approach to setting up keys, similar to
 % \pkg{keyval}. However there is an auxiliary package named \expkvd\ which
@@ -285,6 +323,10 @@
 %   Defines a \key\ taking a value in a \set\ to expand to \meta{code}. In
 %   \meta{code} you can use |#1| to refer to the given value.
 % \end{function}
+% \example Define |text| in |foo| to store the value inside |\foo at text|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \protected\long\ekvdef{foo}{text}{\def\foo at width{#1}}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvdefNoVal}
 %   \begin{syntax}
@@ -292,6 +334,10 @@
 %   \end{syntax}
 %   Defines a no value taking \key\ in a \set\ to expand to \meta{code}.
 % \end{function}
+% \example Define |bool| in |foo| to set |\iffoo at bool| to |true|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \protected\ekvdefNoVal{foo}{bool}{\foo at booltrue}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvlet}
 %   \begin{syntax}
@@ -300,6 +346,10 @@
 %   Let the value taking \key\ in \set\ to \meta{cs}, there are no checks on
 %   \meta{cs} enforced.
 % \end{function}
+% \example Let |cmd| in |foo| do the same as |\foo at cmd|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvlet{foo}{cmd}\foo at cmd
+% \end{lstlisting}
 %
 % \begin{function}{\ekvletNoVal}
 %   \begin{syntax}
@@ -308,6 +358,7 @@
 %   Let the no value taking \key\ in \set\ to \meta{cs}, it is not checked
 %   whether \meta{cs} exists or that it takes no parameter.
 % \end{function}
+% \example See above.
 %
 % \begin{function}{\ekvletkv}
 %   \begin{syntax}
@@ -317,6 +368,10 @@
 %   whether that second key exists (but take a look at
 %   \cs[no-index]{ekvifdefined}).
 % \end{function}
+% \example Let |B| in |bar| be an alias for |A| in |foo|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvletkv{bar}{B}{foo}{A}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvletkvNoVal}
 %   \begin{syntax}
@@ -326,6 +381,7 @@
 %   whether that second key exists (but take a look at
 %   \cs[no-index]{ekvifdefinedNoVal}).
 % \end{function}
+% \example See above.
 %
 % \begin{function}{\ekvdefunknown}
 %   \begin{syntax}
@@ -335,9 +391,19 @@
 %   this macro you can define \meta{code} that will be executed for a given
 %   \set\ when an unknown \key\ with a \val\ was encountered instead of throwing
 %   an error. You can refer to the given \val\ with |#1| and to the unknown
-%   \key's name with |#2| in \meta{code}.\footnotemark
+%   \key's name with |#2| in \meta{code}.\footnotemark{}
+%   |\ekvdefunknown| and |\ekvredirectunknown| are mutually exclusive,
+%   you can't use both.
 % \end{function}
 % \footnotetext{That order is correct, this way the code is faster.}
+% \example Also search |bar| for undefined keys of set |foo|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \long\ekvdefunknown{foo}{\ekvset{bar}{#2={#1}}}
+% \end{lstlisting}
+% This example differs from using |\ekvredirectunknown{foo}{bar}| (see below) in
+% that also the unknown-key handler of the |bar| set will be triggered, error
+% messages for undefined keys will look different, and this is slower than using
+% |\ekvredirectunknown|.
 %
 % \begin{function}{\ekvdefunknownNoVal}
 %   \begin{syntax}
@@ -347,8 +413,50 @@
 %   an error when encountering an unknown \key. With this you can instead let it
 %   execute \meta{code} if an unknown |NoVal| \key\ was encountered. You can
 %   refer to the given \key\ with |#1| in \meta{code}.
+%   |\ekvdefunknownNoVal| and |\ekvredirectunknownNoVal| are mutually exclusive,
+%   you can't use both.
 % \end{function}
+% \example Also search |bar| for undefined keys of set |foo|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvdefunknownNoVal{foo}{\ekvset{bar}{#1}}
+% \end{lstlisting}
 %
+% \begin{function}{\ekvredirectunknown}
+%   \begin{syntax}
+%     \cs{ekvredirectunknown}\marg{set}\marg{set-list}
+%   \end{syntax}
+%   This is a short cut to set up a special |\ekvdefunknown| for \set\ that will
+%   check each set in the comma separated \meta{set-list} for the unknown \key.
+%   You can't use prefixes (so no |\long| or |\protected|) with this macro, the
+%   resulting unknown-key handler will always be |\long|. The first set in the
+%   \meta{set-list} has highest priority. Once the \key\ is found the remaining
+%   sets are discarded, if the \key\ isn't found in any set an error will be
+%   thrown eventually. Note that the error messages are affected by the use of
+%   this macro, in particular, it isn't checked whether a |NoVal| key of the
+%   same name is defined in order to throw a |value forbidden| error.
+%   |\ekvdefunknown| and |\ekvredirectunknown| are mutually exclusive,
+%   you can't use both.
+% \end{function}
+% \example For every key not defined in the set |foo| also search the sets |bar|
+% and |baz|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvredirectunknown{foo}{bar, baz}
+% \end{lstlisting}
+%
+% \begin{function}{\ekvredirectunknownNoVal}
+%   \begin{syntax}
+%     \cs{ekvredirectunknownNoVal}\marg{set}\marg{set-list}
+%   \end{syntax}
+%   This behaves just like |\ekvredirectunknown| and does the same but for the
+%   |NoVal| keys. Again no prefixes are supported. Note that the error messages
+%   are affected by the use of this macro, in particular, it isn't checked
+%   whether a normal key of the same name is defined in order to throw a
+%   |value forbidden| error.
+%   |\ekvdefunknownNoVal| and |\ekvredirectunknownNoVal| are mutually exclusive,
+%   you can't use both.
+% \end{function}
+% \example See above.
+%
 % \subsection{Parsing Keys}
 %
 % \begin{function}{\ekvset}
@@ -367,6 +475,10 @@
 %   be split at the first one and the others are considered part of the value.
 %   |\ekvset| should be nestable.
 % \end{function}
+% \example Parse |key=arg, key| in the set |foo|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvset{foo}{key=arg, key}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvsetSneaked}
 %   \begin{syntax}
@@ -377,6 +489,10 @@
 %   \cs[no-index]{ekvsneak} has been called with \meta{sneak} as its argument as
 %   the first action.
 % \end{function}
+% \example Parse |key=arg, key| in the set |foo| with |\afterwards| sneaked out:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvsetSneaked{foo}{\afterwards}{key=arg, key}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvsetdef}
 %   \begin{syntax}
@@ -389,6 +505,12 @@
 %   \texttt
 %     {\string\long\string\def\meta{cs}\#1\{\string\ekvset\marg{set}\{\#1\}\}}
 % \end{function}
+% \example Define the macro |\foosetup| to parse keys in the set |foo| and use
+% it to parse |key=arg, key|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvsetdef\foosetup{foo}
+% \foosetup{key=arg, key}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvsetSneakeddef}
 %   \begin{syntax}
@@ -406,10 +528,17 @@
 %       \string\ekvsetSneaked\marg{set}\{\#1\}\{\#2\}\}^^A
 %     }
 % \end{function}
+% \example Define the macro |\foothings| to parse keys in the set |foo| and
+% accept a sneaked argument, then use it to parse |key=arg, key| and sneak
+% |\afterwards|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvsetSneakeddef\foothings{foo}
+% \foothings{\afterwards}{key=arg, key}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvsetdefSneaked}
 %   \begin{syntax}
-%     \cs{ekvsetSneakeddef}\meta{cs}\marg{set}\marg{sneaked}
+%     \cs{ekvsetdefSneaked}\meta{cs}\marg{set}\marg{sneaked}
 %   \end{syntax}
 %   And this one behaves like \cs[no-index]{ekvsetSneakeddef} but with a fixed
 %   \meta{sneaked} argument. So the resulting macro is faster than but else
@@ -420,6 +549,12 @@
 %       \string\ekvsetSneaked\marg{set}\marg{sneaked}\{\#1\}\}^^A
 %     }
 % \end{function}
+% \example Define the macro |\barthing| to parse keys in the set |bar| and
+% always execute |\afterwards| afterwards, then use it to parse |key=arg, key|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvsetdefSneaked\barthing{bar}{\afterwards}
+% \barthing{key=arg, key}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvparse}
 %   \begin{syntax}
@@ -449,8 +584,7 @@
 %     you can put an \cs[no-index]{expanded} around it and will still be faster
 %     since you need only a single \cs[no-index]{expandafter} this way.^^A
 %   }
-%
-%   As a small example:
+% \example
 % \begin{lstlisting}
 % \ekvparse{\handlekey{S}}{\handlekeyval{S}}{foo = bar, key, baz={zzz}}
 % \end{lstlisting}
@@ -472,15 +606,14 @@
 % \handle\kv{foo}{bar}\k{key}\kv{baz}{zzz}
 % \end{lstlisting}
 %
-% \subsection{Miscellaneous}
+% \subsection{Other Macros}
 %
-% \subsubsection{Other Macros}
-%
 % \expkv\ provides some other macros which might be of interest.
 %
 % \begin{function}{\ekvVersion,\ekvDate}
 %   These two macros store the version and date of the package.
 % \end{function}
+%
 % \begin{function}{\ekvifdefined,\ekvifdefinedNoVal}
 %   \begin{syntax}
 %     \cs{ekvifdefined}\marg{set}\marg{key}\marg{true}\marg{false}
@@ -490,6 +623,11 @@
 %   either a hash table entry doesn't exist for that key or its meaning is
 %   |\relax|.
 % \end{function}
+% \example Check whether the key |special| is already defined in set |foo|, if
+% it isn't input a file that contains more key definitions:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvifdefined{foo}{special}{}{\input{foo.morekeys.tex}}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvifdefinedset}
 %   \begin{syntax}
@@ -498,6 +636,12 @@
 %   This macro tests whether \set\ is defined (which it is if at least one key
 %   was defined for it). If it is \meta{true} will be run, else \meta{false}.
 % \end{function}
+% \example Check whether the set |VeRyUnLiKeLy| is already defined, if so throw
+% an error, else do nothing:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvifdefinedset{VeRyUnLiKeLy}
+%   {\errmessage{VeRyUnLiKeLy already defined}}{}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak}
 %   \begin{syntax}
@@ -510,6 +654,12 @@
 %   \meta{after} before anything that has been smuggled and |\ekvbreakPostSneak|
 %   will put \meta{after} after the stuff that has been sneaked out.
 % \end{function}
+% \example Define a key |abort| that will stop key parsing inside the set |foo|
+% and execute |\foo at aborted|, or if it got a value |\foo at aborted@with|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvdefNoVal{foo}{abort}{\ekvbreak{\foo at aborted}}
+% \ekvdef{foo}{abort}{\ekvbreak{\foo at aborted@with{#1}}}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvsneak,\ekvsneakPre}
 %   \begin{syntax}
@@ -523,6 +673,11 @@
 %   list everything that has been |\ekvsneak|ed will be left in the input
 %   stream. A small usage example is shown in \autoref{sec:sneakex}.
 % \end{function}
+% \example Define a key |secret| in the set |foo| that will sneak out a macro
+% |\foo at secretly@sneaked|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvdefNoVal{foo}{secret}{\ekvsneak{\foo at secretly@sneaked}}
+% \end{lstlisting}
 %
 % \begin{function}{\ekvchangeset}
 %   \begin{syntax}
@@ -530,17 +685,23 @@
 %   \end{syntax}
 %   Replaces the current set with \meta{new-set}, so for the rest of the current
 %   |\ekvset| call, that call behaves as if it was called with
-%   \texttt{\cs[no-index]{ekvset}\marg{new-set}}. Just like |\ekvsneak| this
-%   reads and reinserts the remainder of the current |\ekvset| macro to do its
-%   job. It is comparable to using \texttt{\meta{key}/.cd} in \pkg{pgfkeys}.
+%   \texttt{\cs[no-index]{ekvset}\marg{new-set}}. It is comparable to using
+%   \texttt{\meta{key}/.cd} in \pkg{pgfkeys}.
 % \end{function}
+% \example Define a key |cd| in set |foo| that will change to another set as
+% specified in the value, if the set is undefined it'll stop the parsing and
+% throw an error as defined in the macro |\foo at cd@error|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvdef{foo}{cd}
+%   {\ekvifdefinedset{#1}{\ekvchangeset{#1}}{\ekvbreak{\foo at cd@error}}}
+% \end{lstlisting}
 %
 % \bigskip
 %
 % \begin{function}{\ekv at name,\ekv at name@set,\ekv at name@key}
 %   \begin{syntax}
-%     \cs{ekv at name}\marg{set}\marg{key}\\
-%     \cs{ekv at name@set}\marg{set}\\
+%     \cs{ekv at name}\marg{set}\marg{key}
+%     \cs{ekv at name@set}\marg{set}
 %     \cs{ekv at name@key}\marg{key}
 %   \end{syntax}
 %   The names of the macros that correspond to a key in a set are build with
@@ -556,190 +717,11 @@
 %   macros outside of \expkv, but \emph{don't} change them! \expkv\ relies on
 %   their exact definitions internally.
 % \end{function}
-%
-% \subsubsection{Bugs}
-%
-% Just like \pkg{keyval}, \expkv\ is bug free. But if you find \pmso{bugs}hidden
-% features\footnote{Thanks, David!} you can tell me about them either via mail
-% (see the first page) or directly on GitHub if you have an account there:
-% \url{https://github.com/Skillmon/tex_expkv}
-%
-% \subsubsection{Comparisons}
-%
-% Comparisons of speed are done with a very simple test key and the help of the
-% \pkg{l3benchmark} package. The key and its usage should be equivalent to
-% \begin{lstlisting}
-% \protected\ekvdef{test}{height}{\def\myheight{#1}}
-% \ekvsetdef\expkvtest{test}
-% \expkvtest{ height = 6 }
+% \example Execute the callback of the |NoVal| key |key| in set |foo|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \csname\ekv at name{foo}{key}N\endcsname
 % \end{lstlisting}
-% and only the usage of the key, not its definition, is benchmarked. For the
-% impatient, the essence of these comparisons regarding speed and buggy
-% behaviour is contained in \autoref{tab:comp}.
 %
-% As far as I know \expkv\ is the only fully expandable \kv\ parser. I tried to
-% compare \expkv\ to every \kv\ package listed on
-% \href{https://ctan.org/topic/keyval}{CTAN}, however, one might notice that
-% some of those are missing from this list. That's because I didn't get the
-% others to work due to bugs, or because they just provide wrappers around other
-% packages in this list.
-%
-% In this subsubsection is no benchmark of |\ekvparse| and |\keyval_parse:NNn|
-% contained, as most other packages don't provide equivalent features to my
-% knowledge. |\ekvparse| is slightly faster than |\ekvset|, but keep in mind
-% that it does less. The same is true for |\keyval_parse:NNn| compared to
-% |\keys_set:nn| of \pkg{expl3} (where the difference is much bigger). Comparing
-% just the two, |\ekvparse| is a tad faster than |\keyval_parse:NNn| because of
-% the two tests (for empty key names and only a single equal sign) which are
-% omitted.
-%
-% \paragraph{\pkg{keyval}} is about \SIrange{30}{40}{\percent} faster and has a
-% comparable feature set just a slightly different way how it handles keys
-% without values.  That might be considered a drawback, as it limits the
-% versatility, but also as an advantage, as it might reduce doubled code. Keep
-% in mind that as soon as someone loads \pkg{xkeyval} the performance of
-% \pkg{keyval} gets replaced by \pkg{xkeyval}'s.
-%
-% Also \pkg{keyval} has a bug, which unfortunately can't really be resolved
-% without breaking backwards compatibility for \emph{many} documents, namely it
-% strips braces from the argument before stripping spaces if the argument isn't
-% surrounded by spaces, also it might strip more than one set of braces. Hence
-% all of the following are equivalent in their outcome, though the last two
-% lines should result in something different than the first two:
-% \begin{lstlisting}[belowskip=0pt]
-% \setkeys{foo}{bar=baz}
-% \setkeys{foo}{bar= {baz}}
-% \setkeys{foo}{bar={ baz}}  % should be ` baz'
-% \setkeys{foo}{bar={{baz}}} % should be `{baz}'
-% \end{lstlisting}
-%
-% \paragraph{\pkg{xkeyval}} is roughly twenty times slower, but it provides more
-% functionality, e.g., it has choice keys, boolean keys, and so on. It contains
-% the same bug as \pkg{keyval} as it has to be compatible with it by design (it
-% replaces \pkg{keyval}'s frontend), but also adds even more cases in which
-% braces are stripped that shouldn't be stripped, worsening the situation.
-%
-% \paragraph{\pkg{ltxkeys}} is no longer compatible with the \LaTeX\ kernel
-% starting with the release 2020-10-01. It is over 380 times slower -- which is
-% funny, because it aims to be ``[\ldots] faster [\ldots] than these earlier
-% packages [referring to \pkg{keyval} and \pkg{xkeyval}].'' It needs more time
-% to parse zero~keys than five of the packages in this comparison need to parse
-% 100~keys.  Since it aims to have a bigger feature set than \pkg{xkeyval}, it
-% most definitely also has a bigger feature set than \expkv.  Also, it can't
-% parse |\long| input, so as soon as your values contain a |\par|, it'll throw
-% errors.  Furthermore, \pkg{ltxkeys} doesn't strip outer braces at all by
-% design, which, imho, is a weird design choice. In addition \pkg{ltxkeys} loads
-% \pkg{catoptions} which is known to introduce bugs (e.g., see
-% \url{https://tex.stackexchange.com/questions/461783}). Because it is no longer
-% compatible with the kernel, I stop benchmarking it (so the numbers listed here
-% and in \autoref{tab:comp} regarding \pkg{ltxkeys} were last updated on
-% 2020-10-05).
-%
-% \paragraph{\pkg{l3keys}} is around four and a half times slower, but has an,
-% imho, great interface to define keys. It strips \emph{all} outer spaces, even
-% if somehow multiple spaces ended up on either end. It offers more features,
-% but is pretty much bound to \pkg{expl3} code. Whether that's a drawback is up
-% to you.
-%
-% \paragraph{\pkg{pgfkeys}} is around \num{2.7} times slower for one key if one
-% uses the |/|\meta{path}|/.cd| syntax and almost \SI{20}{\percent} slower if
-% one uses |\pgfqkeys|, but has an \emph{enormous} feature set.
-% To get the best performance |\pgfqkeys| was used in the benchmark. This
-% reduces the overhead for setting the base directory of the benchmark keys by
-% about \SI{43}{\ops} (so both $p_0$ and $T_0$ would be about \SI{43}{\ops}
-% bigger if |\pgfkeys{|\meta{path}|/.cd,|\meta{keys}|}| was used instead).
-% It has the same or a very similar bug \pkg{keyval} has. The brace bug (and
-% also the category fragility) can be fixed by \pkg{pgfkeyx}, but this package
-% was last updated in 2012 and it slows down |\pgfkeys| by factor~\num{8}. Also
-% \pkg{pgfkeyx} is no longer compatible with versions of \pkg{pgfkeys} newer
-% than 2020-05-25.
-%
-% \paragraph{\pkg{kvsetkeys} with \pkg{kvdefinekeys}} is about \num{4.4} times
-% slower, but it works even if commas and equals have category codes different
-% from 12 (just as some other packages in this list). Else the features of the
-% keys are equal to those of \pkg{keyval}, the parser has more features, though.
-%
-% \paragraph{\pkg{options}} is \num{1.7} times slower for only a single value.
-% It has a much bigger feature set. Unfortunately it also suffers from the
-% premature unbracing bug \pkg{keyval} has.
-%
-% \paragraph{\pkg{simplekv}} is hard to compare because I don't speak French (so
-% I don't understand the documentation). There was an update released on
-% 2020-04-27 which greatly improved the package's performance and adds
-% functionality so that it can be used more like most of the other \kv\
-% packages. It has problems with stripping braces and spaces in a hard to
-% predict manner just like \pkg{keyval}. Also, while it tries to be robust
-% against category code changes of commas and equal signs, the used mechanism
-% fails if the \kv\ list already got tokenised. Regarding unknown keys it got a
-% very interesting behaviour. It doesn't throw an error, but stores the \val\ in
-% a new entry accessible with \cs[no-index]{useKV}. Also if you omit \val\ it
-% stores |true| for that \key. For up to three keys, \expkv\ is a bit faster,
-% for more keys \pkg{simplekv} takes the lead.
-%
-% \paragraph{\pkg{yax}} is over twenty times slower. It has a pretty strange
-% syntax, imho, and again a direct equivalent is hard to define. It has the
-% premature unbracing bug, too. Also somehow loading \pkg{yax} broke
-% \pkg{options} for me. The tested definition was:
-% \begin{lstlisting}[belowskip=0pt]
-% \usepackage{yax}
-% \defactiveparameter yax {\storevalue\myheight yax:height } % key setup
-% \setparameterlist{yax}{ height = 6 }                       % benchmarked
-% \end{lstlisting}
-%
-% \begin{table}
-%   \def\fnsym{\textcolor{red!80!black}{*}}%
-%   \sisetup{round-precision=1, round-mode=places}%
-%   \begingroup
-%   \centering
-%   \newcommand*\yes{\textcolor{red!80!black}  {yes}}^^A
-%   \newcommand*\no {\textcolor{green!80!black}{no}}^^A
-%   \caption[Comparison of \kv\ packages]
-%     {^^A
-%       Comparison of \kv\ packages. The packages are ordered from
-%       fastest to slowest for one \kv\ pair. Benchmarking was done using
-%       \pkg{l3benchmark} and the scripts in the \file{Benchmarks} folder
-%       of the \href{https://github.com/Skillmon/tex_expkv}{git repository}.
-%       The columns $p_i$ are the polynomial coefficients of a linear fit to the
-%       run-time, $p_0$ can be interpreted as the overhead for initialisation
-%       and $p_1$ the cost per key. The $T_0$ column is the actual mean ops
-%       needed for an empty list argument, as the linear fit doesn't match that
-%       point well in general. The column ``BB'' lists whether the
-%       parsing is affected by some sort of brace bug, ``CF'' stands for
-%       category code fragile and lists whether the parsing breaks with active
-%       commas or equal signs.^^A
-%       \label{tab:comp}^^A
-%     }
-%   \begin{tabular}
-%       {>{\collectcell\pkg}l<{\endcollectcell}*3{S[table-format=4.1]}ccc}
-%     \toprule
-%     \rmfamily Package & {$p_1$} & {$p_0$} & {$T_0$}& BB & CF & Date \\
-%     \midrule
-%     keyval    &   13.742 &    1.486 &    7.254 & \yes & \yes & 2014-10-28 \\
-%     \expkv    &   19.701 &    2.169 &    6.592 & \no  & \no  & 2020-10-10 \\
-%     simplekv  &   18.334 &    6.971 &   17.710 & \yes & \yes & 2020-04-27 \\
-%     pgfkeys   &   24.274 &    1.725 &   10.650 & \yes & \yes & 2020-09-05 \\
-%     options   &   23.600 &   15.638 &   20.830 & \yes & \yes & 2015-03-01 \\
-%     kvsetkeys & {\fnsym} & {\fnsym} &   40.290 & \no  & \no  & 2019-12-15 \\
-%     l3keys    &   71.309 &   33.131 &   31.590 & \no  & \no  & 2020-09-24 \\
-%     xkeyval   &  253.563 &  202.246 &  168.300 & \yes & \yes & 2014-12-03 \\
-%     yax       &  421.853 &  157.003 &  114.700 & \yes & \yes & 2010-01-22 \\
-%     ltxkeys   & 3400.142 & 4737.958 & 5368.000 & \no  & \no  & 2012-11-17 \\
-%     \bottomrule
-%   \end{tabular}
-%   \par
-%   \endgroup
-%   \medskip
-%   \fnsym For \pkg{kvsetkeys} the linear model used for the other
-%   packages is a poor fit, \pkg{kvsetkeys} seems to have approximately
-%   quadratic run-time, the coefficients of the second degree polynomial fit are
-%   $p_2=\num{8.240}$, $p_1=\num{44.862}$, and $p_0=\num{60.793}$. Of course the
-%   other packages might not really have linear run-time, but at least from 1~to
-%   20~keys the fits don't seem too bad. If one extrapolates the fits for 100
-%   \kv\ pairs one finds that most of them match pretty well, the exception
-%   being \pkg{ltxkeys}, which behaves quadratic as well with
-%   $p_2=\num{23.500}$, $p_1=\num{2906.634}$, and $p_0=\num{6547.489}$.
-% \end{table}
-%
 % \subsection{Examples}
 %
 % \subsubsection{Standard Use-Case}
@@ -783,7 +765,7 @@
 %   \ourmacro{width=5pt}\par
 % \end{minipage}
 %
-% \paragraph{The same key using \protect\expkvd}
+% \paragraph{The same keys using \protect\expkvd}
 % Using \expkvd\ we can set up the equivalent key using a \kv\ interface, after
 % the following we could use |\ourmacro| in the same way as above. \expkvd\ will
 % allocate and initialise |\ourdim| and define the |width| key |\protected| for
@@ -799,6 +781,59 @@
 %   }
 % \end{lstlisting}
 %
+% \subsubsection{A Macro to Draw Rules}
+%
+% Another small example could be a \kv\ driven |\rule| alternative, because I
+% keep forgetting the correct order of its arguments. First we define the keys
+% (and initialize the used macros to store the keys):
+% \begin{lstlisting}
+% \makeatletter
+% \newcommand*\myrule at ht{1ex}
+% \newcommand*\myrule at wd{0.1em}
+% \newcommand*\myrule at raise{\z@}
+% \protected\ekvdef{myrule}{ht}{\def\myrule at ht{#1}}
+% \protected\ekvdef{myrule}{wd}{\def\myrule at wd{#1}}
+% \protected\ekvdef{myrule}{raise}{\def\myrule at raise{#1}}
+% \end{lstlisting}
+% Then we define a macro to change the defaults outside of |\myrule| and
+% |\myrule| itself:
+% \begin{lstlisting}
+% \ekvsetdef\myruleset{myrule}
+% \newcommand*\myrule[1][]{\begingroup\myruleset{#1}\myrule at out\endgroup}
+% \end{lstlisting}
+% And finally the output:
+% \begin{lstlisting}
+% \newcommand*\myrule at out{\rule[\myrule at raise]\myrule at wd\myrule at ht}
+% \makeatother
+% \end{lstlisting}
+% And we can use it:\\
+% \begin{minipage}{7cm}
+% \begin{lstlisting}
+% a\myrule\par
+% a\myrule[ht=2ex,raise=-.5ex]\par
+% \myruleset{wd=5pt}
+% a\myrule
+% \end{lstlisting}
+% \end{minipage}^^A
+% \begin{minipage}{\dimexpr\linewidth-7cm\relax}
+%   \makeatletter
+%   \newcommand*\myrule at ht{1ex}
+%   \newcommand*\myrule at wd{0.1em}
+%   \newcommand*\myrule at raise{\z@}
+%   \protected\ekvdef{myrule}{ht}{\def\myrule at ht{#1}}
+%   \protected\ekvdef{myrule}{wd}{\def\myrule at wd{#1}}
+%   \protected\ekvdef{myrule}{raise}{\def\myrule at raise{#1}}
+%   \ekvsetdef\myruleset{myrule}
+%   \newcommand*\myrule[1][]{\begingroup\myruleset{#1}\myrule at out\endgroup}
+%   \newcommand*\myrule at out{\rule[\myrule at raise]\myrule at wd\myrule at ht}
+%   \makeatother
+%   a\myrule\par
+%   a\myrule[ht=2ex,raise=-.5ex]\par
+%   \myruleset{wd=5pt}
+%   a\myrule
+% \end{minipage}
+% 
+%
 % \subsubsection{An Expandable \kv\ Macro Using \cs[no-index]{ekvsneak}}
 % \label{sec:sneakex}
 %
@@ -1004,6 +1039,23 @@
 %                            value forbidden (`<key>', set `<set>').
 % \end{lstlisting}
 %
+% If you're using an undefined key in a set for which |\ekvredirectunknown| was
+% used, and the key isn't found in any of the other sets as well, you'll get:
+% \begin{lstlisting}
+% ! Undefined control sequence.
+% <argument> \! expkv Error:
+%                            no key `<key>' in sets {<set1>}{<set2>}...
+% \end{lstlisting}
+%
+% If you're using an undefined |NoVal| key in a set for which
+% |\ekvredirectunknownNoVal| was used, and the key isn't found in any of the
+% other sets as well, you'll get:
+% \begin{lstlisting}
+% ! Undefined control sequence.
+% <argument> \! expkv Error:
+%                            no NoVal key `<key>' in sets {<set1>}...
+% \end{lstlisting}
+%
 % If you're using a set for which you never executed one of the defining macros
 % from \autoref{sec:define} you'll get a low level \TeX\ error, as that isn't
 % actively tested by the parser (and hence will lead to undefined behaviour and
@@ -1014,6 +1066,195 @@
 %                    \! expkv Error: Set `<set>' undefined.
 % \end{lstlisting}
 %
+% \subsection{Bugs}
+%
+% Just like \pkg{keyval}, \expkv\ is bug free. But if you find \pmso{bugs}hidden
+% features\footnote{Thanks, David!} you can tell me about them either via mail
+% (see the first page) or directly on GitHub if you have an account there:
+% \url{https://github.com/Skillmon/tex_expkv}
+%
+% \subsection{Comparisons}\label{sec:cmp}
+%
+% This subsection makes some basic comparison between \expkv\ and other \kv\
+% packages. The comparisons are really concise, regarding speed, feature range
+% (without listing the features of each package), and bugs and misfeatures.
+%
+% Comparisons of speed are done with a very simple test key and the help of the
+% \pkg{l3benchmark} package. The key and its usage should be equivalent to
+% \begin{lstlisting}
+% \protected\ekvdef{test}{height}{\def\myheight{#1}}
+% \ekvsetdef\expkvtest{test}
+% \expkvtest{ height = 6 }
+% \end{lstlisting}
+% and only the usage of the key, not its definition, is benchmarked. For the
+% impatient, the essence of these comparisons regarding speed and buggy
+% behaviour is contained in \autoref{tab:comp}.
+%
+% As far as I know \expkv\ is the only fully expandable \kv\ parser. I tried to
+% compare \expkv\ to every \kv\ package listed on
+% \href{https://ctan.org/topic/keyval}{CTAN}, however, one might notice that
+% some of those are missing from this list. That's because I didn't get the
+% others to work due to bugs, or because they just provide wrappers around other
+% packages in this list.
+%
+% In this subsection is no benchmark of |\ekvparse| and |\keyval_parse:NNn|
+% contained, as most other packages don't provide equivalent features to my
+% knowledge. |\ekvparse| is slightly faster than |\ekvset|, but keep in mind
+% that it does less. The same is true for |\keyval_parse:NNn| compared to
+% |\keys_set:nn| of \pkg{expl3} (where the difference is much bigger). Comparing
+% just the two, |\ekvparse| is a tad faster than |\keyval_parse:NNn| because of
+% the two tests (for empty key names and only a single equal sign) which are
+% omitted.
+%
+% \paragraph{\pkg{keyval}} is about \SIrange{30}{40}{\percent} faster and has a
+% comparable feature set (actually a bit smaller since \expkv\ supports
+% unknown-key handlers and redirection to other sets) just a slightly different
+% way how it handles keys without values.  That might be considered a drawback,
+% as it limits the versatility, but also as an advantage, as it might reduce
+% doubled code. Keep in mind that as soon as someone loads \pkg{xkeyval} the
+% performance of \pkg{keyval} gets replaced by \pkg{xkeyval}'s.
+%
+% Also \pkg{keyval} has a bug, which unfortunately can't really be resolved
+% without breaking backwards compatibility for \emph{many} documents, namely it
+% strips braces from the argument before stripping spaces if the argument isn't
+% surrounded by spaces, also it might strip more than one set of braces. Hence
+% all of the following are equivalent in their outcome, though the last two
+% lines should result in something different than the first two:
+% \begin{lstlisting}[belowskip=0pt]
+% \setkeys{foo}{bar=baz}
+% \setkeys{foo}{bar= {baz}}
+% \setkeys{foo}{bar={ baz}}  % should be ` baz'
+% \setkeys{foo}{bar={{baz}}} % should be `{baz}'
+% \end{lstlisting}
+%
+% \paragraph{\pkg{xkeyval}} is roughly twenty times slower, but it provides more
+% functionality, e.g., it has choice keys, boolean keys, and so on. It contains
+% the same bug as \pkg{keyval} as it has to be compatible with it by design (it
+% replaces \pkg{keyval}'s frontend), but also adds even more cases in which
+% braces are stripped that shouldn't be stripped, worsening the situation.
+%
+% \paragraph{\pkg{ltxkeys}} is no longer compatible with the \LaTeX\ kernel
+% starting with the release 2020-10-01. It is over 380 times slower -- which is
+% funny, because it aims to be ``[\ldots] faster [\ldots] than these earlier
+% packages [referring to \pkg{keyval} and \pkg{xkeyval}].'' It needs more time
+% to parse zero~keys than five of the packages in this comparison need to parse
+% 100~keys.  Since it aims to have a bigger feature set than \pkg{xkeyval}, it
+% most definitely also has a bigger feature set than \expkv.  Also, it can't
+% parse |\long| input, so as soon as your values contain a |\par|, it'll throw
+% errors.  Furthermore, \pkg{ltxkeys} doesn't strip outer braces at all by
+% design, which, imho, is a weird design choice. In addition \pkg{ltxkeys} loads
+% \pkg{catoptions} which is known to introduce bugs (e.g., see
+% \url{https://tex.stackexchange.com/questions/461783}). Because it is no longer
+% compatible with the kernel, I stop benchmarking it (so the numbers listed here
+% and in \autoref{tab:comp} regarding \pkg{ltxkeys} were last updated on
+% 2020-10-05).
+%
+% \paragraph{\pkg{l3keys}} is around four and a half times slower, but has an,
+% imho, great interface to define keys. It strips \emph{all} outer spaces, even
+% if somehow multiple spaces ended up on either end. It offers more features,
+% but is pretty much bound to \pkg{expl3} code. Whether that's a drawback is up
+% to you.
+%
+% \paragraph{\pkg{pgfkeys}} is around \num{2.7} times slower for one key if one
+% uses the |/|\meta{path}|/.cd| syntax and almost \SI{20}{\percent} slower if
+% one uses |\pgfqkeys|, but has an \emph{enormous} feature set.
+% To get the best performance |\pgfqkeys| was used in the benchmark. This
+% reduces the overhead for setting the base directory of the benchmark keys by
+% about \SI{43}{\ops} (so both $p_0$ and $T_0$ would be about \SI{43}{\ops}
+% bigger if |\pgfkeys{|\meta{path}|/.cd,|\meta{keys}|}| was used instead).
+% It has the same or a very similar bug \pkg{keyval} has. The brace bug (and
+% also the category fragility) can be fixed by \pkg{pgfkeyx}, but this package
+% was last updated in 2012 and it slows down |\pgfkeys| by factor~\num{8}. Also
+% \pkg{pgfkeyx} is no longer compatible with versions of \pkg{pgfkeys} newer
+% than 2020-05-25.
+%
+% \paragraph{\pkg{kvsetkeys} with \pkg{kvdefinekeys}} is about \num{4.4} times
+% slower, but it works even if commas and equals have category codes different
+% from 12 (just as some other packages in this list). Else the features of the
+% keys are equal to those of \pkg{keyval}, the parser has more features, though.
+%
+% \paragraph{\pkg{options}} is \num{1.7} times slower for only a single value.
+% It has a much bigger feature set. Unfortunately it also suffers from the
+% premature unbracing bug \pkg{keyval} has.
+%
+% \paragraph{\pkg{simplekv}} is hard to compare because I don't speak French (so
+% I don't understand the documentation). There was an update released on
+% 2020-04-27 which greatly improved the package's performance and adds
+% functionality so that it can be used more like most of the other \kv\
+% packages. It has problems with stripping braces and spaces in a hard to
+% predict manner just like \pkg{keyval}. Also, while it tries to be robust
+% against category code changes of commas and equal signs, the used mechanism
+% fails if the \kv\ list already got tokenised. Regarding unknown keys it got a
+% very interesting behaviour. It doesn't throw an error, but stores the \val\ in
+% a new entry accessible with \cs[no-index]{useKV}. Also if you omit \val\ it
+% stores |true| for that \key. For up to three keys, \expkv\ is a bit faster,
+% for more keys \pkg{simplekv} takes the lead.
+%
+% \paragraph{\protect\yax} is over twenty times slower. It has a pretty
+% strange syntax for the \TeX-world, imho, and again a direct equivalent is hard
+% to define (don't understand me wrong, I don't say I don't like the syntax,
+% it's just atypical). It has the premature unbracing bug, too. Also somehow
+% loading \yax\ broke \pkg{options} for me. The tested definition was:
+% \begin{lstlisting}[belowskip=0pt]
+% \usepackage{yax}
+% \defactiveparameter yax {\storevalue\myheight yax:height } % key setup
+% \setparameterlist{yax}{ height = 6 }                       % benchmarked
+% \end{lstlisting}
+%
+% \begin{table}
+%   \def\fnsym{\textcolor{red!80!black}{*}}%
+%   \sisetup{round-precision=1, round-mode=places}%
+%   \begingroup
+%   \centering
+%   \newcommand*\yes{\textcolor{red!80!black}  {yes}}^^A
+%   \newcommand*\no {\textcolor{green!80!black}{no}}^^A
+%   \caption[Comparison of \kv\ packages]
+%     {^^A
+%       Comparison of \kv\ packages. The packages are ordered from
+%       fastest to slowest for one \kv\ pair. Benchmarking was done using
+%       \pkg{l3benchmark} and the scripts in the \file{Benchmarks} folder
+%       of the \href{https://github.com/Skillmon/tex_expkv}{git repository}.
+%       The columns $p_i$ are the polynomial coefficients of a linear fit to the
+%       run-time, $p_0$ can be interpreted as the overhead for initialisation
+%       and $p_1$ the cost per key. The $T_0$ column is the actual mean ops
+%       needed for an empty list argument, as the linear fit doesn't match that
+%       point well in general. The column ``BB'' lists whether the
+%       parsing is affected by some sort of brace bug, ``CF'' stands for
+%       category code fragile and lists whether the parsing breaks with active
+%       commas or equal signs.^^A
+%       \label{tab:comp}^^A
+%     }
+%   \begin{tabular}
+%       {>{\collectcell\pkg}l<{\endcollectcell}*3{S[table-format=4.1]}ccc}
+%     \toprule
+%     \rmfamily Package & {$p_1$} & {$p_0$} & {$T_0$}& BB & CF & Date \\
+%     \midrule
+%     keyval    &   13.742 &    1.486 &    7.254 & \yes & \yes & 2014-10-28 \\
+%     \expkv    &   19.701 &    2.169 &    6.592 & \no  & \no  & 2020-10-10 \\
+%     simplekv  &   18.334 &    6.971 &   17.710 & \yes & \yes & 2020-04-27 \\
+%     pgfkeys   &   24.274 &    1.725 &   10.650 & \yes & \yes & 2020-09-05 \\
+%     options   &   23.600 &   15.638 &   20.830 & \yes & \yes & 2015-03-01 \\
+%     kvsetkeys & {\fnsym} & {\fnsym} &   40.290 & \no  & \no  & 2019-12-15 \\
+%     l3keys    &   71.309 &   33.131 &   31.590 & \no  & \no  & 2020-09-24 \\
+%     xkeyval   &  253.563 &  202.246 &  168.300 & \yes & \yes & 2014-12-03 \\
+%     \yax      &  421.853 &  157.003 &  114.700 & \yes & \yes & 2010-01-22 \\
+%     ltxkeys   & 3400.142 & 4737.958 & 5368.000 & \no  & \no  & 2012-11-17 \\
+%     \bottomrule
+%   \end{tabular}
+%   \par
+%   \endgroup
+%   \medskip
+%   \fnsym For \pkg{kvsetkeys} the linear model used for the other
+%   packages is a poor fit, \pkg{kvsetkeys} seems to have approximately
+%   quadratic run-time, the coefficients of the second degree polynomial fit are
+%   $p_2=\num{8.240}$, $p_1=\num{44.862}$, and $p_0=\num{60.793}$. Of course the
+%   other packages might not really have linear run-time, but at least from 1~to
+%   20~keys the fits don't seem too bad. If one extrapolates the fits for 100
+%   \kv\ pairs one finds that most of them match pretty well, the exception
+%   being \pkg{ltxkeys}, which behaves quadratic as well with
+%   $p_2=\num{23.500}$, $p_1=\num{2906.634}$, and $p_0=\num{6547.489}$.
+% \end{table}
+%
 % \subsection{License}
 %
 % Copyright \textcopyright\ 2020\unless\ifnum\year=2020--\the\year\fi\
@@ -1084,8 +1325,8 @@
 % \begin{macro}{\ekvVersion,\ekvDate}
 % We're on our first input, so lets store the version and date in a macro.
 %    \begin{macrocode}
-\def\ekvVersion{1.6}
-\def\ekvDate{2020-12-28}
+\def\ekvVersion{1.7}
+\def\ekvDate{2021-04-09}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -1291,6 +1532,38 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}[internal]{\ekv at csv@loop,\ekv at csv@loop at do,\ekv at csv@loop at end}
+%   This is just a very simple loop over a list of comma separated values,
+%   leaving each element as the argument to a specified function inside of
+%   |\unravel|. It should be used as
+%   \texttt
+%     {^^A
+%       \cs[no-index]{ekv at csv@loop}\hskip0pt^^A
+%       \marg{function}\hskip0pt^^A
+%       \cs[no-index]{ekv at mark}\hskip0pt^^A
+%       \meta{csv-list}\hskip0pt^^A
+%       ,\cs[no-index]{ekv at stop},^^A
+%     }.
+%   We use some |\expandafter| chain to preexpand |\ekv at strip| here.
+%    \begin{macrocode}
+\ekv at exparg{\long\def\ekv at csv@loop#1#2,}%
+  {%
+    \expandafter
+    \ekv at gobble@from at mark@to at stop
+      \expandafter#\expandafter2\expandafter\ekv at csv@loop at end\expandafter
+    \ekv at stop
+    \ekv at strip{#2}{\ekv at csv@loop at do{#1}}%
+    \ekv at csv@loop{#1}\ekv at mark
+  }
+\long\def\ekv at csv@loop at do#1#2{\unexpanded{#1{#2}}}
+\long\expandafter\def\expandafter\ekv at csv@loop at end
+    \expandafter\ekv at stop
+    \ekv at strip{#1}#2%
+    \ekv at csv@loop#3\ekv at mark
+  {}
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{\ekv at name,\ekv at name@set,\ekv at name@key}
 % The keys will all follow the same naming scheme, so we define it here.
 %    \begin{macrocode}
@@ -1429,11 +1702,148 @@
       \unexpanded\expandafter{\ekv at undefined@set{#1}\endcsname{#1}}%
     }%
     {\unexpanded\expandafter{\ekv at name{#3}{#4}}}%
-  }%
+  }
 \expandafter\ekvdef\ekvdefNoVal
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\ekvredirectunknown,\ekvredirectunknownNoVal}
+% \begin{macro}[internal]
+%   {
+%     \ekv at defredirectunknown,\ekv at redirectunknown@aux,
+%     \ekv at redirectunknownNoVal@aux
+%   }
+%   The redirection macros prepare the unknown function by looping over the
+%   provided list of sets and leaving a |\ekv at redirect@kv| or |\ekv at redirect@k|
+%   for each set. Only the first of these internals will receive the \key\ and
+%   \val\ as arguments.
+%    \begin{macrocode}
+\protected\def\ekvredirectunknown
+  {%
+    \ekv at defredirectunknown
+      \ekv at redirect@kv
+      \ekv at err@redirect at kv@notfound
+      {\long\ekvdefunknown}%
+      \ekv at redirectunknown@aux
+  }
+\protected\def\ekvredirectunknownNoVal
+  {%
+    \ekv at defredirectunknown
+      \ekv at redirect@k
+      \ekv at err@redirect at k@notfound
+      \ekvdefunknownNoVal
+      \ekv at redirectunknownNoVal@aux
+  }
+\protected\def\ekv at defredirectunknown#1#2#3#4#5#6%
+  {%
+    \begingroup
+    \edef\ekv at tmp
+      {%
+        \ekv at csv@loop#1\ekv at mark#6,\ekv at stop,%
+        \unexpanded{#2}%
+        {\ekv at csv@loop{}\ekv at mark#5,#6,\ekv at stop,}%
+      }%
+    \ekv at expargtwice
+      {\endgroup#3{#5}}%
+      {\expandafter#4\ekv at tmp\ekv at stop}%
+  }
+\def\ekv at redirectunknown@aux#1{#1{##1}{##2}}
+\def\ekv at redirectunknownNoVal@aux#1{#1{##1}}
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[internal]
+%   {
+%     \ekv at redirect@k,\ekv at redirect@k at a,\ekv at redirect@k at a@,
+%     \ekv at redirect@k at b,\ekv at redirect@k at c,\ekv at redirect@k at d,
+%     \ekv at redirect@kv,\ekv at redirect@kv at a,\ekv at redirect@kv at a@,
+%     \ekv at redirect@kv at b,\ekv at redirect@kv at c,\ekv at redirect@kv at d
+%   }
+%   The redirect code works by some simple loop over all the sets, which we
+%   already preprocessed in |\ekv at defredirectunknown|. For some optimisation we
+%   blow this up a bit code wise, essentially, all this does is |\ekvifdefined|
+%   or |\ekvifdefinedNoVal| in each set, if there is a match gobble the
+%   remainder of the specified sets and execute the key macro, else go on with
+%   the next set (to which the \key\ and \val\ are forwarded).
+%
+%   First we set up some code which is different depending on |\lastnamedcs|
+%   being available or not. All this is stored in a temporary macro to have
+%   pre-expanded |\ekv at name| constellations ready.
+%    \begin{macrocode}
+\def\ekv at redirect@k#1#2#3#4%
+  {%
+    \ekv at if@lastnamedcs
+      {%
+        \def\ekv at redirect@k##1##2##3%
+          {%
+            \ifcsname#1\endcsname\ekv at redirect@k at a\fi
+            ##3{##1}%
+          }%
+        \def\ekv at redirect@k at a\fi{\fi\expandafter\ekv at redirect@k at b\lastnamedcs}%
+        \long\def\ekv at redirect@kv##1##2##3##4%
+          {%
+            \ifcsname#2\endcsname\ekv at redirect@kv at a\fi\@gobble{##1}%
+            ##4{##1}{##2}%
+          }
+        \def\ekv at redirect@kv at a\fi\@gobble
+          {\fi\expandafter\ekv at redirect@kv at b\lastnamedcs}%
+      }
+      {%
+        \def\ekv at redirect@k##1##2##3%
+          {%
+            \ifcsname#1\endcsname\ekv at redirect@k at a\fi\ekv at redirect@k at a@
+              #1\endcsname
+            ##3{##1}%
+          }%
+        \def\ekv at redirect@k at a@#3\endcsname{}%
+        \def\ekv at redirect@k at a\fi\ekv at redirect@k at a@
+          {\fi\expandafter\ekv at redirect@k at b\csname}%
+        \long\def\ekv at redirect@kv##1##2##3##4%
+          {%
+            \ifcsname#2\endcsname\ekv at redirect@kv at a\fi\ekv at redirect@kv at a@
+              #2\endcsname{##1}%
+            ##4{##1}{##2}%
+          }
+        \long\def\ekv at redirect@kv at a@#4\endcsname##3{}%
+        \def\ekv at redirect@kv at a\fi\ekv at redirect@kv at a@
+          {\fi\expandafter\ekv at redirect@kv at b\csname}%
+      }
+  }
+%    \end{macrocode}
+%   The key name given to this loop will already be |\detokenize|d by |\ekvset|,
+%   so we can safely remove the |\detokenize| here for some performance gain.
+%    \begin{macrocode}
+\def\ekv at redirect@kv#1\detokenize#2#3\ekv at stop{\unexpanded{#1#2#3}}
+\edef\ekv at redirect@kv
+  {%
+    {\expandafter\ekv at redirect@kv\ekv at name{#2}{#1}N\ekv at stop}%
+    {\expandafter\ekv at redirect@kv\ekv at name{#3}{#2}\ekv at stop}%
+    {\expandafter\ekv at redirect@kv\ekv at name{#1}{#2}N\ekv at stop}%
+    {\expandafter\ekv at redirect@kv\ekv at name{#1}{#2}\ekv at stop}%
+  }
+%    \end{macrocode}
+%   Everything is ready to make the real definitions.
+%    \begin{macrocode}
+\expandafter\ekv at redirect@k\ekv at redirect@kv
+%    \end{macrocode}
+%   The remaining macros here are independent on |\lastnamedcs|, starting from
+%   the |@b| we know that there is a hash table entry, and get the macro as a
+%   parameter. We still have to test whether the macro is |\relax|, depending on
+%   the result of that test we have to either remove the remainder of the
+%   current test, or the remainder of the set list and invoke the macro.
+%    \begin{macrocode}
+\def\ekv at redirect@k at b#1%
+  {\ifx\relax#1\ekv at redirect@k at c\fi\ekv at redirect@k at d#1}
+\def\ekv at redirect@k at c\fi\ekv at redirect@k at d#1{\fi}
+\def\ekv at redirect@k at d#1#2\ekv at stop{#1}
+\def\ekv at redirect@kv at b#1%
+  {\ifx\relax#1\ekv at redirect@kv at c\fi\ekv at redirect@kv at d#1}
+\long\def\ekv at redirect@kv at c\fi\ekv at redirect@kv at d#1#2{\fi}
+\long\def\ekv at redirect@kv at d#1#2#3\ekv at stop{#1{#2}}
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}[internal]{\ekv at defsetmacro}
 %   In order to enhance the speed the set name given to |\ekvset| will be turned
 %   into a control sequence pretty early, so we have to define that control
@@ -1841,12 +2251,12 @@
 %
 % \begin{macro}{\ekvchangeset}
 % Provide a macro that is able to switch out the current \set\ in |\ekvset|.
-% This operation is slow (by comparison, it should be slightly faster than
-% |\ekvsneak|), but allows for something similar to \pkg{pgfkeys}'s
-% \texttt{\meta{key}/.cd} mechanism. However this operation is more expensive
-% than |/.cd| as we can't just redefine some token to reflect this, but have to
-% switch out the set expandably, so this works similar to the |\ekvsneak| macros
-% reading and reinserting the remainder of the \kv\ list.
+% This operation allows something similar to \pkg{pgfkeys}'s
+% \texttt{\meta{key}/.cd} mechanism. However this operation can be more
+% expensive than |/.cd| as we can't just redefine some token to reflect this,
+% but have to switch out the set expandably, so this works similar to the
+% |\ekvsneak| macros reading and reinserting things, but it only has to read and
+% reinsert the remainder of the current key's replacement code.
 %    \begin{macrocode}
 \ekv at exparg{\def\ekvchangeset#1}%
   {%
@@ -2034,18 +2444,12 @@
 % is unlikely) we want to somehow throw expandable errors, in our case via
 % undefined control sequences.
 %    \begin{macrocode}
-\begingroup
-\edef\ekv at err
+\def\ekv at err#1%
   {%
-    \endgroup
-    \unexpanded{\long\def\ekv at err}##1%
-      {%
-        \unexpanded{\expandafter\ekv at err@\@firstofone}%
-        {\unexpanded\expandafter{\csname ! expkv Error:\endcsname}##1.}%
-        \unexpanded{\ekv at stop}%
-      }%
+    \long\def\ekv at err##1{\expandafter\ekv at err@\@firstofone{#1##1.}\ekv at stop}%
   }
-\ekv at err
+\begingroup\expandafter\endgroup
+\expandafter\ekv at err\csname ! expkv Error:\endcsname
 \def\ekv at err@{\expandafter\ekv at gobbleto@stop}
 %    \end{macrocode}
 % \end{macro}
@@ -2059,10 +2463,15 @@
 % use those instead of the full strings.
 %    \begin{macrocode}
 \long\def\ekv at err@common #1#2{\expandafter\ekv at err@common@\string#2{#1}}
-\long\def\ekv at err@common@#1`#2' #3.#4#5{\ekv at err{#4 (`#5', set `#2')}}
+\ekv at exparg{\long\def\ekv at err@common@#1`#2' #3.#4#5}%
+  {\ekv at err{#4 (`#5', set `#2')}}
 \ekv at exparg{\long\def\ekv at err@unknown#1}{\ekv at err@common{unknown key}{#1}}
 \ekv at exparg{\long\def\ekv at err@noarg  #1}{\ekv at err@common{value forbidden}{#1}}
 \ekv at exparg{\long\def\ekv at err@reqval #1}{\ekv at err@common{value required}{#1}}
+\ekv at exparg{\long\def\ekv at err@redirect at kv@notfound#1#2#3\ekv at stop}%
+  {\ekv at err{no key `#2' in sets #3}}
+\ekv at exparg{\def\ekv at err@redirect at k@notfound#1#2\ekv at stop}%
+  {\ekv at err{no NoVal key `#1' in sets #2}}
 %    \end{macrocode}
 % \end{macro}
 %

Modified: trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex
===================================================================
--- trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex	2021-04-10 21:09:57 UTC (rev 58814)
+++ trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex	2021-04-10 21:10:10 UTC (rev 58815)
@@ -13,7 +13,7 @@
 %% See http://www.latex-project.org/lppl.txt
 %% --------------------------------------------------------------
 %% 
-%% Copyright (C) 2020 Jonathan P. Spratte
+%% Copyright (C) 2020-2021 Jonathan P. Spratte
 %% 
 %% This  work may be  distributed and/or  modified under  the conditions  of the
 %% LaTeX Project Public License (LPPL),  either version 1.3c  of this license or
@@ -39,8 +39,8 @@
   \errmessage{expkv requires e-TeX}
   \expandafter\endinput
 \fi
-\def\ekvVersion{1.6}
-\def\ekvDate{2020-12-28}
+\def\ekvVersion{1.7}
+\def\ekvDate{2021-04-09}
 \csname ekv at tmp\endcsname
 \expandafter\chardef\csname ekv at tmp\endcsname=\catcode`\@
 \catcode`\@=11
@@ -130,6 +130,21 @@
 \long\def\ekv at exparg@#1#2{#2{#1}}%
 \long\def\ekv at expargtwice#1#2{\expandafter\ekv at expargtwice@\expandafter{#2}{#1}}
 \def\ekv at expargtwice@{\expandafter\ekv at exparg@\expandafter}
+\ekv at exparg{\long\def\ekv at csv@loop#1#2,}%
+  {%
+    \expandafter
+    \ekv at gobble@from at mark@to at stop
+      \expandafter#\expandafter2\expandafter\ekv at csv@loop at end\expandafter
+    \ekv at stop
+    \ekv at strip{#2}{\ekv at csv@loop at do{#1}}%
+    \ekv at csv@loop{#1}\ekv at mark
+  }
+\long\def\ekv at csv@loop at do#1#2{\unexpanded{#1{#2}}}
+\long\expandafter\def\expandafter\ekv at csv@loop at end
+    \expandafter\ekv at stop
+    \ekv at strip{#1}#2%
+    \ekv at csv@loop#3\ekv at mark
+  {}
 \def\ekv at name@set#1{ekv#1(}
 \def\ekv at name@key#1{#1)}
 \edef\ekv at name
@@ -225,8 +240,95 @@
       \unexpanded\expandafter{\ekv at undefined@set{#1}\endcsname{#1}}%
     }%
     {\unexpanded\expandafter{\ekv at name{#3}{#4}}}%
-  }%
+  }
 \expandafter\ekvdef\ekvdefNoVal
+\protected\def\ekvredirectunknown
+  {%
+    \ekv at defredirectunknown
+      \ekv at redirect@kv
+      \ekv at err@redirect at kv@notfound
+      {\long\ekvdefunknown}%
+      \ekv at redirectunknown@aux
+  }
+\protected\def\ekvredirectunknownNoVal
+  {%
+    \ekv at defredirectunknown
+      \ekv at redirect@k
+      \ekv at err@redirect at k@notfound
+      \ekvdefunknownNoVal
+      \ekv at redirectunknownNoVal@aux
+  }
+\protected\def\ekv at defredirectunknown#1#2#3#4#5#6%
+  {%
+    \begingroup
+    \edef\ekv at tmp
+      {%
+        \ekv at csv@loop#1\ekv at mark#6,\ekv at stop,%
+        \unexpanded{#2}%
+        {\ekv at csv@loop{}\ekv at mark#5,#6,\ekv at stop,}%
+      }%
+    \ekv at expargtwice
+      {\endgroup#3{#5}}%
+      {\expandafter#4\ekv at tmp\ekv at stop}%
+  }
+\def\ekv at redirectunknown@aux#1{#1{##1}{##2}}
+\def\ekv at redirectunknownNoVal@aux#1{#1{##1}}
+\def\ekv at redirect@k#1#2#3#4%
+  {%
+    \ekv at if@lastnamedcs
+      {%
+        \def\ekv at redirect@k##1##2##3%
+          {%
+            \ifcsname#1\endcsname\ekv at redirect@k at a\fi
+            ##3{##1}%
+          }%
+        \def\ekv at redirect@k at a\fi{\fi\expandafter\ekv at redirect@k at b\lastnamedcs}%
+        \long\def\ekv at redirect@kv##1##2##3##4%
+          {%
+            \ifcsname#2\endcsname\ekv at redirect@kv at a\fi\@gobble{##1}%
+            ##4{##1}{##2}%
+          }
+        \def\ekv at redirect@kv at a\fi\@gobble
+          {\fi\expandafter\ekv at redirect@kv at b\lastnamedcs}%
+      }
+      {%
+        \def\ekv at redirect@k##1##2##3%
+          {%
+            \ifcsname#1\endcsname\ekv at redirect@k at a\fi\ekv at redirect@k at a@
+              #1\endcsname
+            ##3{##1}%
+          }%
+        \def\ekv at redirect@k at a@#3\endcsname{}%
+        \def\ekv at redirect@k at a\fi\ekv at redirect@k at a@
+          {\fi\expandafter\ekv at redirect@k at b\csname}%
+        \long\def\ekv at redirect@kv##1##2##3##4%
+          {%
+            \ifcsname#2\endcsname\ekv at redirect@kv at a\fi\ekv at redirect@kv at a@
+              #2\endcsname{##1}%
+            ##4{##1}{##2}%
+          }
+        \long\def\ekv at redirect@kv at a@#4\endcsname##3{}%
+        \def\ekv at redirect@kv at a\fi\ekv at redirect@kv at a@
+          {\fi\expandafter\ekv at redirect@kv at b\csname}%
+      }
+  }
+\def\ekv at redirect@kv#1\detokenize#2#3\ekv at stop{\unexpanded{#1#2#3}}
+\edef\ekv at redirect@kv
+  {%
+    {\expandafter\ekv at redirect@kv\ekv at name{#2}{#1}N\ekv at stop}%
+    {\expandafter\ekv at redirect@kv\ekv at name{#3}{#2}\ekv at stop}%
+    {\expandafter\ekv at redirect@kv\ekv at name{#1}{#2}N\ekv at stop}%
+    {\expandafter\ekv at redirect@kv\ekv at name{#1}{#2}\ekv at stop}%
+  }
+\expandafter\ekv at redirect@k\ekv at redirect@kv
+\def\ekv at redirect@k at b#1%
+  {\ifx\relax#1\ekv at redirect@k at c\fi\ekv at redirect@k at d#1}
+\def\ekv at redirect@k at c\fi\ekv at redirect@k at d#1{\fi}
+\def\ekv at redirect@k at d#1#2\ekv at stop{#1}
+\def\ekv at redirect@kv at b#1%
+  {\ifx\relax#1\ekv at redirect@kv at c\fi\ekv at redirect@kv at d#1}
+\long\def\ekv at redirect@kv at c\fi\ekv at redirect@kv at d#1#2{\fi}
+\long\def\ekv at redirect@kv at d#1#2#3\ekv at stop{#1{#2}}
 \edef\ekv at defsetmacro
   {%
     \unexpanded{\ifx#1\relax\edef#1##1}%
@@ -540,24 +642,23 @@
     \ekv at exparg{\ekv at zero\ekv at exparg{\long\def#1##1}}%
       {\ekvsetSneaked{#2}{#3}{##1}}%
   }
-\begingroup
-\edef\ekv at err
+\def\ekv at err#1%
   {%
-    \endgroup
-    \unexpanded{\long\def\ekv at err}##1%
-      {%
-        \unexpanded{\expandafter\ekv at err@\@firstofone}%
-        {\unexpanded\expandafter{\csname ! expkv Error:\endcsname}##1.}%
-        \unexpanded{\ekv at stop}%
-      }%
+    \long\def\ekv at err##1{\expandafter\ekv at err@\@firstofone{#1##1.}\ekv at stop}%
   }
-\ekv at err
+\begingroup\expandafter\endgroup
+\expandafter\ekv at err\csname ! expkv Error:\endcsname
 \def\ekv at err@{\expandafter\ekv at gobbleto@stop}
 \long\def\ekv at err@common #1#2{\expandafter\ekv at err@common@\string#2{#1}}
-\long\def\ekv at err@common@#1`#2' #3.#4#5{\ekv at err{#4 (`#5', set `#2')}}
+\ekv at exparg{\long\def\ekv at err@common@#1`#2' #3.#4#5}%
+  {\ekv at err{#4 (`#5', set `#2')}}
 \ekv at exparg{\long\def\ekv at err@unknown#1}{\ekv at err@common{unknown key}{#1}}
 \ekv at exparg{\long\def\ekv at err@noarg  #1}{\ekv at err@common{value forbidden}{#1}}
 \ekv at exparg{\long\def\ekv at err@reqval #1}{\ekv at err@common{value required}{#1}}
+\ekv at exparg{\long\def\ekv at err@redirect at kv@notfound#1#2#3\ekv at stop}%
+  {\ekv at err{no key `#2' in sets #3}}
+\ekv at exparg{\def\ekv at err@redirect at k@notfound#1#2\ekv at stop}%
+  {\ekv at err{no NoVal key `#1' in sets #2}}
 \catcode`\@=\ekv at tmp
 %% 
 %%

Modified: trunk/Master/texmf-dist/tex/latex/expkv/expkv.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/expkv/expkv.sty	2021-04-10 21:09:57 UTC (rev 58814)
+++ trunk/Master/texmf-dist/tex/latex/expkv/expkv.sty	2021-04-10 21:10:10 UTC (rev 58815)
@@ -13,7 +13,7 @@
 %% See http://www.latex-project.org/lppl.txt
 %% --------------------------------------------------------------
 %% 
-%% Copyright (C) 2020 Jonathan P. Spratte
+%% Copyright (C) 2020-2021 Jonathan P. Spratte
 %% 
 %% This  work may be  distributed and/or  modified under  the conditions  of the
 %% LaTeX Project Public License (LPPL),  either version 1.3c  of this license or



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