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.