summaryrefslogtreecommitdiff
path: root/obsolete/macros/generic/expkv/expkv.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'obsolete/macros/generic/expkv/expkv.dtx')
-rw-r--r--obsolete/macros/generic/expkv/expkv.dtx2980
1 files changed, 2980 insertions, 0 deletions
diff --git a/obsolete/macros/generic/expkv/expkv.dtx b/obsolete/macros/generic/expkv/expkv.dtx
new file mode 100644
index 0000000000..8651ad97a3
--- /dev/null
+++ b/obsolete/macros/generic/expkv/expkv.dtx
@@ -0,0 +1,2980 @@
+% \iffalse meta-comment
+%
+% 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
+% (at your option) any later version. The latest version of this license is in
+% the file:
+%
+% http://www.latex-project.org/lppl.txt
+%
+% ------------------------------------------------------------------------------
+%
+%<*driver>^^A>>=
+\def\nameofplainTeX{plain}
+\ifx\fmtname\nameofplainTeX\else
+ \expandafter\begingroup
+\fi
+\input l3docstrip.tex
+\askforoverwritefalse
+\preamble
+
+--------------------------------------------------------------
+expkv -- an expandable key=val implementation
+E-mail: jspratte@yahoo.de
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+--------------------------------------------------------------
+
+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
+(at your option) any later version. The latest version of this license is in
+the file:
+
+ http://www.latex-project.org/lppl.txt
+
+This work is "maintained" (as per LPPL maintenance status) by
+ Jonathan P. Spratte.
+
+This work consists of the file expkv.dtx
+and the derived files expkv.pdf
+ expkv.sty
+ expkv.tex
+ t-expkv.tex
+
+\endpreamble
+% stop docstrip adding \endinput
+\postamble
+\endpostamble
+\generate{\file{expkv.sty}{\from{expkv.dtx}{pkg}}}
+\generate{\file{expkv.tex}{\from{expkv.dtx}{tex}}}
+\generate{\file{t-expkv.tex}{\from{expkv.dtx}{ctx}}}
+\ifx\fmtname\nameofplainTeX
+ \expandafter\endbatchfile
+\else
+ \expandafter\endgroup
+\fi
+%
+\IfFileExists{expkv.tex}{\input{expkv.tex}}{}
+\ProvidesFile{expkv.dtx}
+ [\csname ekvDate\endcsname\ an expandable key=val implementation]
+\PassOptionsToPackage{full}{textcomp}
+\documentclass{l3doc}
+\RequirePackage[oldstylenums,nott]{kpfonts}
+\input{glyphtounicode}
+\pdfgentounicode=1
+\RequirePackage{xfp} ^^A required for an example
+\RequirePackage{listings}
+\RequirePackage{booktabs}
+\RequirePackage{array}
+\RequirePackage{collcell}
+\RequirePackage{siunitx}
+\DeclareSIUnit\ops{ops}
+\RequirePackage{xcolor}
+\RequirePackage{caption}
+\RequirePackage{microtype}
+\RequirePackage{accsupp}
+\RequirePackage{enumitem}
+\RequirePackage{tcolorbox}
+\newtcolorbox{exresult}[2][]
+ {colback=expkvgrey!10!white,colframe=expkvgrey,fontupper=\small,width={#2},#1}
+\newtcbox\exres[1][]
+ {
+ colback=expkvgrey!10!white
+ ,colframe=expkvgrey
+ ,size=small
+ ,nobeforeafter
+ ,tcbox raise base
+ ,fontupper=\small
+ ,#1
+ }
+\def\mylstwd{.55em}
+\lstdefinelanguage{expkv}[primitive]{TeX}
+ {
+ ,moretexcs=[4]^^A e-TeX
+ {
+ expanded,
+ numexpr,
+ protected,
+ }
+ ,moretexcs=[5]^^A plain/LaTeX/ConTeXt
+ {
+ approx,
+ begin,
+ empty,
+ item,
+ LaTeX,
+ makeatletter,makeatother,
+ newcommand,newdimen,
+ RequirePackage,
+ rule,
+ TeX,
+ textit,texttt,
+ usepackage,
+ usemodule,
+ }
+ ,moretexcs=[6]^^A used packages
+ {
+ ^^A xfp
+ fpeval,
+ ^^A keyval
+ setkeys,
+ ^^A yax
+ defactiveparameter,storevalue,setparameterlist
+ }
+ ,moretexcs=[2]^^A expkv macros
+ {
+ 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,^^A
+ ekvoptarg,ekvoptargTF,^^A
+ ekverr
+ }
+ ,moretexcs=[3]^^A expkv-pkg macros
+ {
+ ^^A expkv-cs
+ ekvcSecondaryKeys,ekvcSplitAndForward,
+ ^^A expkv-def
+ ekvdefinekeys
+ }
+ }
+\colorlet{codeparam}{cyan!65!black}
+\lstset
+ {
+ ,language=expkv
+ ,flexiblecolumns=false
+ ,basewidth=\mylstwd
+ ,gobble=2
+ ,basicstyle=\fontfamily{jkp}\itshape
+ ,texcsstyle=*[1]{\bfseries\color{expkvgrey}} ^^A primitives
+ ,texcsstyle=*[2]{\bfseries\color{expkvred}} ^^A expkv
+ ,texcsstyle=*[3]{\color{expkvred}} ^^A expkv-pkg
+ ,texcsstyle=*[4]{\bfseries\color{expkvgrey}} ^^A e-TeX
+ ,texcsstyle=*[5]{\bfseries\color{expkvgrey}} ^^A plain/LaTeX macros
+ ,texcsstyle=*[6]{} ^^A macros of other packages
+ ,commentstyle=\color[gray]{0.4}
+ ,literate=
+ {\{} {{\CodeUpBf\{}}{1}
+ {\}} {{\CodeUpBf\}}}{1}
+ {$} {{\CodeUpBf\$}}{1}
+ {[} {{\CodeUp[}}{1}
+ {]} {{\CodeUp]}}{1}
+ {(} {{\CodeUp(}}{1}
+ {)} {{\CodeUp)}}{1}
+ {*} {{$*$}}{1}
+ {1} {{\CodeUp{1}}}{1}
+ {2} {{\CodeUp{2}}}{1}
+ {3} {{\CodeUp{3}}}{1}
+ {4} {{\CodeUp{4}}}{1}
+ {5} {{\CodeUp{5}}}{1}
+ {6} {{\CodeUp{6}}}{1}
+ {7} {{\CodeUp{7}}}{1}
+ {8} {{\CodeUp{8}}}{1}
+ {9} {{\CodeUp{9}}}{1}
+ {0} {{\CodeUp{0}}}{1}
+ {##} {{\CodeColored{codeparam}{1}{\#}}}{1}
+ {##1}{{\CodeColored{codeparam}{2}{\#1}}}{2}
+ {##2}{{\CodeColored{codeparam}{2}{\#2}}}{2}
+ {##3}{{\CodeColored{codeparam}{2}{\#3}}}{2}
+ {##4}{{\CodeColored{codeparam}{2}{\#4}}}{2}
+ {##5}{{\CodeColored{codeparam}{2}{\#5}}}{2}
+ {##6}{{\CodeColored{codeparam}{2}{\#6}}}{2}
+ {##7}{{\CodeColored{codeparam}{2}{\#7}}}{2}
+ {##8}{{\CodeColored{codeparam}{2}{\#8}}}{2}
+ {##9}{{\CodeColored{codeparam}{2}{\#9}}}{2}
+ {<key>}{{$\langle$}key{$\rangle$}}{5}
+ {<set>}{{$\langle$}set{$\rangle$}}{5}
+ {<set1>}{{$\langle$}set1{$\rangle$}}{6}
+ {<set2>}{{$\langle$}set2{$\rangle$}}{6}
+ ^^A,literate=*{<key>}{\key}{4}{<set>}{\set}{4}
+ }
+\newcommand*\CodeColored[3]{\textcolor{#1}{\makebox[\dimexpr\mylstwd*#2]{#3}}}
+\newcommand*\CodeUpBf[1]{\makebox[\mylstwd]{\textup{\textbf{#1}}}}
+\newcommand*\CodeUp[1]{\makebox[\mylstwd]{\textup{#1}}}
+\RequirePackage{randtext}
+\let\metaORIG\meta
+\protected\def\meta #1{\texttt{\metaORIG{#1}}}
+\renewcommand*\thefootnote{\fnsymbol{footnote}}
+\definecolor{expkvred}{HTML}{9F393D}
+\colorlet{expkvgrey}{black!75}
+\makeatletter
+\newcommand*\example{\par\smallskip\noindent\textit{Example:} \ignorespaces}
+\newcommand*\expFormat
+ {^^A
+ {^^A
+ \rmfamily
+ \bfseries
+ {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A
+ \lower.493ex\hbox{{\color{expkvgrey}P}}^^A
+ }^^A
+ }
+\newcommand*\kvstyle
+ {^^A
+ \normalfont\rmfamily\bfseries\color{expkvred}^^A
+ }
+\newcommand*\expkv
+ {^^A
+ \texorpdfstring
+ {^^A
+ \mbox
+ {^^A
+ \BeginAccSupp{ActualText=expkv}^^A
+ \href{https://github.com/Skillmon/tex_expkv}
+ {^^A
+ \rmfamily
+ \bfseries
+ {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A
+ \lower.493ex
+ \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A
+ \kern-.18em{\color{expkvred}v}^^A
+ }^^A
+ \EndAccSupp{}^^A
+ }^^A
+ }
+ {expkv}^^A
+ }
+\newcommand*\expkvpkg[1]
+ {^^A
+ \texorpdfstring
+ {^^A
+ \mbox
+ {^^A
+ \BeginAccSupp{ActualText=expkv-#1}^^A
+ \href{https://github.com/Skillmon/tex_expkv-#1}
+ {^^A
+ \rmfamily
+ \bfseries
+ {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A
+ \lower.493ex
+ \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A
+ \kern-.18em{\color{expkvred}v}^^A
+ {\color{expkvgrey}^^A
+ \kern.05em\rule[-.1ex]{.08em}{1.2ex}\kern.05em\textsc{#1}^^A
+ }^^A
+ }^^A
+ \EndAccSupp{}^^A
+ }^^A
+ }
+ {expkv-#1}^^A
+ }
+\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}}
+\newcommand\set{\meta{set}}
+\hypersetup{linkcolor=red!80!black,urlcolor=purple!80!black}
+\DoNotIndex{\directlua}
+\DoNotIndex{\def,\edef,\,,\=,\begingroup,\catcode,\chardef,\csname,\endcsname}
+\DoNotIndex{\endgroup,\endinput,\errmessage,\expandafter,\input,\let,\long}
+\DoNotIndex{\protected,\ProvidesFile,\ProvidesPackage,\relax,\space}
+\DoNotIndex{\^,\@,\unexpanded,\string,\expanded,\detokenize,\meaning}
+\DoNotIndex{\lastnamedcs,\romannumeral,\z@}
+\DoNotIndex{\noexpand,\normalexpanded,\normalunexpanded}
+\DoNotIndex{\par,\protect}
+\DoNotIndex{\unprotect}
+\DoNotIndex{\writestatus}
+\DoNotIndex{\ifcsname}
+\DoNotIndex{\ifx}
+\DoNotIndex{\iffalse}
+\DoNotIndex{\ifnum}
+\DoNotIndex{\else}
+\DoNotIndex{\fi}
+\@gobble\fi ^^A ignoring \ifx etc., but only one \fi
+\@gobble\fi ^^A ignoring \ifx etc., but only one \fi
+\@gobble\fi ^^A ignoring \ifx etc., but only one \fi
+\@ifdefinable\gobbledocstriptag{\def\gobbledocstriptag#1>{}}
+\newcommand*\pmso[1] ^^A poor man's strike out
+ {^^A
+ \leavevmode
+ \begingroup
+ \sbox0{#1}^^A
+ \rlap{\vrule height .6ex depth -.5ex width \wd0\relax}^^A
+ \usebox0\relax
+ \endgroup
+ }
+\makeatother
+\begin{document}
+ \title
+ {^^A
+ \texorpdfstring
+ {^^A
+ \huge\expkv
+ \\[\medskipamount]
+ \Large an {\expFormat}andable
+ \meta{{\kvstyle k}\kern-.05em ey}=^^A
+ \meta{{\kvstyle v}\kern-.05em alue}
+ implementation^^A
+ }{expkv - an expandable <key>=<value> implementation}^^A
+ }
+ \date{\ekvDate\space v\ekvVersion}
+ \author{Jonathan P. Spratte\thanks{\protect\randomize{jspratte@yahoo.de}}}
+ \DocInput{expkv.dtx}
+\end{document}
+%</driver>^^A=<<
+% \fi
+%
+% \maketitle
+% \renewcommand*\thefootnote{\arabic{footnote}}
+%
+% \begin{abstract}
+% \noindent\parfillskip=0pt
+% \expkv\ provides a small interface for \kv\ parsing. The parsing macro is
+% \emph{fully expandable}, the \meta{code} of your keys might be not. \expkv\ is
+% \emph{swift}, close to the fastest \kv\ implementation. However it is the
+% fastest which copes with active commas and equal signs and doesn't strip
+% braces accidentally.
+% \end{abstract}
+%
+% \tableofcontents
+%
+% \begin{documentation}^^A>>=
+%
+% \section{Documentation}
+%
+% \expkv\ provides an expandable \kv\ parser. The \kv\ pairs should be given as
+% a comma separated list and the separator between a \key\ and the associated
+% \val\ should be an equal sign. Both, the commas and the equal signs, might be
+% of category 12 (other) or 13 (active). To support this is necessary as for
+% example \pkg{babel} turns characters active for some languages, for instance
+% the equal sign is turned active for Turkish.
+%
+% \expkv\ is usable as generic code, as a \LaTeX\ package or as a \ConTeXt\
+% module. To use it, just use one of:
+% \begin{lstlisting}
+% \input expkv % plainTeX
+% \usepackage{expkv} % LaTeX
+% \usemodule[expkv] % ConTeXt
+% \end{lstlisting}
+% Both the \LaTeX\ package and the \ConTeXt\ module don't do more than
+% \file{expkv.tex}, except calling |\ProvidesPackage| and setting things up such
+% that \file{expkv.tex} will use |\ProvidesFile|, or printing some status
+% information. The \ConTeXt\ support is not thoroughly tested, though (since I
+% don't use \ConTeXt\ myself I don't know if there are additional pitfalls I
+% wasn't aware of).
+%
+% In the \expkv\ family are other packages contained which provide additional
+% functionality. Those packages currently are:
+% \begin{description}[format=\expkvpkg]
+% \item[def]
+% a key-defining frontend for \expkv\ using a \kv\ syntax
+% \item[cs]
+% define expandable \kv\ macros using \expkv
+% \item[opt]
+% parse package and class options with \expkv
+% \end{description}
+% 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
+% provides a more sophisticated interface, similar to well established packages
+% like \pkg{pgfkeys} or \pkg{l3keys}.
+%
+% Keys in \expkv\ (as in almost all other \kv\ implementations) belong to a
+% \emph{set} such that different sets can contain keys of the same name. Unlike
+% many other implementations \expkv\ doesn't provide means to set a default
+% value, instead we have keys that take values and keys that don't (the latter
+% are called |NoVal| keys by \expkv), but both can have the same name (on the
+% user level).
+%
+% The following macros are available to define new keys. Those macros containing
+% ``|def|'' in their name can be prefixed by anything allowed to prefix |\def|
+% (but \emph{don't} use |\outer|, keys defined with it won't ever be usable),
+% prefixes allowed for |\let| can prefix those with ``|let|'' in their name,
+% accordingly. Neither \set\ nor \key\ are allowed to be empty for new keys.
+% \set\ will be used as is inside of |\csname ...\endcsname| and \key\ will get
+% |\detokenize|d. Also \set\ should not contain an explicit |\par| token.
+%
+% \begin{function}{\ekvdef}
+% \begin{syntax}
+% \cs{ekvdef}\marg{set}\marg{key}\marg{code}
+% \end{syntax}
+% 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@text|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \protected\long\ekvdef{foo}{text}{\def\foo@text{#1}}
+% \end{lstlisting}
+%
+% \begin{function}{\ekvdefNoVal}
+% \begin{syntax}
+% \cs{ekvdefNoVal}\marg{set}\marg{key}\marg{code}
+% \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@bool| to |true|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \protected\ekvdefNoVal{foo}{bool}{\foo@booltrue}
+% \end{lstlisting}
+%
+% \begin{function}{\ekvlet}
+% \begin{syntax}
+% \cs{ekvlet}\marg{set}\marg{key}\meta{cs}
+% \end{syntax}
+% Let the value taking \key\ in \set\ to \meta{cs}, there are no checks on
+% \meta{cs} enforced, but the code should expect the value as a single braced
+% argument directly following it.
+% \end{function}
+% \example Let |cmd| in |foo| do the same as |\foo@cmd|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvlet{foo}{cmd}\foo@cmd
+% \end{lstlisting}
+%
+% \begin{function}{\ekvletNoVal}
+% \begin{syntax}
+% \cs{ekvletNoVal}\marg{set}\marg{key}\meta{cs}
+% \end{syntax}
+% 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}
+% \cs{ekvletkv}\marg{set}\marg{key}\marg{set2}\marg{key2}
+% \end{syntax}
+% Let the \key\ in \set\ to \meta{key2} in \meta{set2}, it is not checked
+% 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}
+% \cs{ekvletkvNoVal}\marg{set}\marg{key}\marg{set2}\marg{key2}
+% \end{syntax}
+% Let the \key\ in \set\ to \meta{key2} in \meta{set2}, it is not checked
+% whether that second key exists (but take a look at
+% \cs[no-index]{ekvifdefinedNoVal}).
+% \end{function}
+% \example See above.
+%
+% \begin{function}{\ekvdefunknown}
+% \begin{syntax}
+% \cs{ekvdefunknown}\marg{set}\marg{code}
+% \end{syntax}
+% By default an error will be thrown if an unknown \key\ is encountered. With
+% 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{}
+% |\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}
+% \cs{ekvdefunknownNoVal}\marg{set}\marg{code}
+% \end{syntax}
+% As already explained for \cs[no-index]{ekvdefunknown}, \expkv\ would throw
+% 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 an |unwanted value| 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
+% |missing value| error.
+% |\ekvdefunknownNoVal| and |\ekvredirectunknownNoVal| are mutually exclusive,
+% you can't use both.
+% \end{function}
+% \example See above.
+%
+% \subsection{Parsing Keys}
+%
+% \begin{function}{\ekvset}
+% \begin{syntax}
+% \cs{ekvset}\marg{set}\{\kv,\ldots\}
+% \end{syntax}
+% Splits \kv\ pairs on commas. From both \key\ and \val\ up to one space is
+% stripped from both ends, if then only a braced group remains the braces are
+% stripped as well. So |\ekvset{foo}{bar=baz}| and
+% |\ekvset{foo}{ {bar}= {baz} }| will both do
+% \texttt{\cs[no-index]{\meta{foobarcode}}\{baz\}}, so you can hide commas,
+% equal signs and spaces at the ends of either \key\ or \val\ by putting
+% braces around them. If you omit the equal sign the code of the key created
+% with the |NoVal| variants described in \autoref{sec:define} will be
+% executed. If \kv\ contains more than a single unhidden equal sign, it will
+% be split at the first one and the others are considered part of the value.
+% |\ekvset| should be nestable.
+%
+% |\ekvset| is currently \emph{not} alignment safe.\footnotemark\ As a result,
+% key names and values that contain an |&| must be wrapped in braces when
+% |\ekvset| is used inside an alignment (like \LaTeXe's |tabular| environment)
+% or you have to create a wrapper that ensures an alignment safe context.
+% \end{function}
+% \footnotetext{This might change in the future, I've not decided yet.}
+% \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}
+% \cs{ekvsetSneaked}\marg{set}\marg{sneak}\{\kv,\ldots\}
+% \end{syntax}
+% Just like \cs[no-index]{ekvset}, this macro parses the \kv\ pairs within the
+% given \set. But \cs[no-index]{ekvsetSneaked} will behave as if
+% \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}
+% \cs{ekvsetdef}\meta{cs}\marg{set}
+% \end{syntax}
+% With this function you can define a shorthand macro \meta{cs} to parse keys
+% of a specified \set. It is always defined |\long|, but if you need to you
+% can also prefix it with |\global|. The resulting macro is faster than but
+% else equivalent to the idiomatic definition:\par
+% \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}
+% \cs{ekvsetSneakeddef}\meta{cs}\marg{set}
+% \end{syntax}
+% Just like \cs[no-index]{ekvsetdef} this defines a shorthand macro \meta{cs},
+% but this macro will make it a shorthand for \cs[no-index]{ekvsetSneaked},
+% meaning that \meta{cs} will take two arguments, the first being stuff that
+% should be given to \cs[no-index]{ekvsneak} and the second the \kv\ list. The
+% resulting macro is faster than but else equivalent to the idiomatic
+% definition:\par
+% \texttt
+% {%
+% \string\long\string\def\meta{cs}\#1\#2\{^^A
+% \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{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
+% equivalent to the idiomatic definition:\par
+% \texttt
+% {%
+% \string\long\string\def\meta{cs}\#1\{^^A
+% \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}
+% \cs{ekvparse}\marg{code1}\marg{code2}\{\kv,\ldots\}
+% \end{syntax}
+% This macro parses the \kv\ pairs and provides those list elements which are
+% only keys as an argument to \meta{code1}, and those which are a \kv\ pair
+% to \meta{code2} as two arguments. It is fully expandable as well and returns
+% each element of the parsed list in |\unexpanded|, which has no effect
+% outside of an |\expanded| or |\edef| context.
+% Also |\ekvparse| expands in exactly two steps of expansion.
+% You can use multiple tokens in \meta{code1} and \meta{code2} or just a
+% single control sequence name. In both cases the found \key\ and \val\ are
+% provided as a brace group following them.
+%
+% |\ekvparse| is alignment safe, meaning that you don't have to take any
+% precautions if it is used inside an alignment context (like \LaTeXe's
+% |tabular| environment) and any key or value can contain an |&|.
+%
+% |\ekvbreak|, |\ekvsneak|, and |\ekvchangeset| and their relatives don't work
+% in |\ekvparse|. It is analogue to \pkg{expl3}'s |\keyval_parse:NNn|, but
+% not with the same parsing rules -- |\keyval_parse:NNn| throws an error on
+% multiple equal signs per \kv\ pair and on empty \key\ names in a \kv\ pair,
+% both of which |\ekvparse| doesn't deal with.
+% \end{function}
+% \example
+% \begin{lstlisting}
+% \ekvparse{\handlekey{S}}{\handlekeyval{S}}{foo = bar, key, baz={zzz}}
+% \end{lstlisting}
+% would be equivalent to
+% \begin{lstlisting}
+% \handlekeyval{S}{foo}{bar}\handlekey{S}{key}\handlekeyval{S}{baz}{zzz}
+% \end{lstlisting}
+% and afterwards |\handlekey| and |\handlekeyval| would have to further handle
+% the \key. There are no macros like these two contained in \expkv, you have
+% to set them up yourself if you want to use |\ekvparse| (of course the names
+% might differ). If you need the results of |\ekvparse| as the argument for
+% another macro, you should use |\expanded|, or expand |\ekvparse| twice, as
+% only then the input stream will contain the output above:
+% \begin{lstlisting}
+% \expandafter\parse\expanded{\ekvparse\k\kv{foo = bar, key, baz={zzz}}}
+% \end{lstlisting}
+% or
+% \begin{lstlisting}
+% \expandafter\expandafter\expandafter
+% \parse\ekvparse\k\kv{foo = bar, key, baz={zzz}}
+% \end{lstlisting}
+% would both expand to
+% \begin{lstlisting}
+% \parse\kv{foo}{bar}\k{key}\kv{baz}{zzz}
+% \end{lstlisting}
+%
+% \subsection{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}
+% \cs{ekvifdefinedNoVal}\marg{set}\marg{key}\marg{true}\marg{false}
+% \end{syntax}
+% These two macros test whether there is a \key\ in \set. It is false if
+% 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}
+% \cs{ekvifdefinedset}\marg{set}\marg{true}\marg{false}
+% \end{syntax}
+% 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}
+% \cs{ekvbreak}\marg{after}
+% \end{syntax}
+% Gobbles the remainder of the current |\ekvset| macro and its argument list
+% and reinserts \meta{after}. So this can be used to break out of |\ekvset|.
+% The first variant will also gobble anything that has been sneaked out using
+% |\ekvsneak| or |\ekvsneakPre|, while |\ekvbreakPreSneak| will put
+% \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@aborted|, or if it got a value |\foo@aborted@with|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvdefNoVal{foo}{abort}{\ekvbreak{\foo@aborted}}
+% \ekvdef{foo}{abort}{\ekvbreak{\foo@aborted@with{#1}}}
+% \end{lstlisting}
+%
+% \begin{function}{\ekvsneak,\ekvsneakPre}
+% \begin{syntax}
+% \cs{ekvsneak}\marg{after}
+% \end{syntax}
+% Puts \meta{after} after the effects of |\ekvset|. The first variant will put
+% \meta{after} after any other tokens which might have been sneaked before,
+% while |\ekvsneakPre| will put \meta{after} before other smuggled stuff.
+% This reads and reinserts the remainder of the current |\ekvset| macro and
+% its argument list to do its job. After |\ekvset| has parsed the entire \kv\
+% 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
+% |\foo@secretly@sneaked|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvdefNoVal{foo}{secret}{\ekvsneak{\foo@secretly@sneaked}}
+% \end{lstlisting}
+%
+% \begin{function}{\ekvchangeset}
+% \begin{syntax}
+% \cs{ekvchangeset}\marg{new-set}
+% \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}}. 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@cd@error|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \ekvdef{foo}{cd}
+% {\ekvifdefinedset{#1}{\ekvchangeset{#1}}{\ekvbreak{\foo@cd@error}}}
+% \end{lstlisting}
+%
+% \begin{function}{\ekvoptarg}
+% \begin{syntax}
+% \cs{ekvoptarg}\marg{next}\marg{default}
+% \end{syntax}
+% This macro will check for a following optional argument in brackets (|[]|)
+% expandably. After the optional argument there has to be a mandatory one. The
+% code in \meta{next} should expect two arguments (the processed optional
+% argument and the mandatory one). If there was an optional argument the
+% result will be \meta{next}\marg{optional}\meta{mandatory} (so the optional
+% argument will be wrapped in braces, the mandatory argument will be
+% untouched). If there was no optional argument the result will be
+% \meta{next}\marg{default}\marg{mandatory} (so the default will be used and
+% the mandatory argument will be wrapped in braces after being read once -- if
+% it was already wrapped it is effectively unchanged).
+% \end{function}
+% |\ekvoptarg| expands in exactly two steps, grabs all the arguments only at the
+% second expansion step, and is alignment safe. It has its limitations however.
+% It can't tell the difference between |[| and |{[}|, so it doesn't work if the
+% mandatory argument is a single bracket. Also if the optional argument should
+% contain a nested closing bracket, the optional argument has to use nested
+% braces like so: |[{arg]ument}]|.
+% \example Say we have a macro that should take an optional argument defaulting
+% to 1:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \newcommand\foo{\ekvoptarg\@foo{1}}
+% \newcommand\@foo[2]{Mandatory: #2\par Optional: #1}
+% \end{lstlisting}
+%
+% \begin{function}{\ekvoptargTF}
+% \begin{syntax}
+% \cs{ekvoptargTF}\marg{true}\marg{false}
+% \end{syntax}
+% This macro is similar to |\ekvoptarg|, but will result in
+% \meta{true}\marg{optional}\meta{mandatory} or \meta{false}\marg{mandatory}
+% instead of placing a default value.
+% \end{function}
+% |\ekvoptargTF| expands in exactly two steps, grabs all the arguments only at
+% the second expansion step, and is alignment safe. It has the same limitations
+% as |\ekvoptarg|.
+% \example Say we have a macro that should behave differently depending on
+% whether there was an optional argument or not. This could be done with:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \newcommand\foo{\ekvoptargTF\foo@a\foo@b}
+% \newcommand\foo@a[2]{Mandatory: #2\par Optional: #1}
+% \newcommand\foo@b[1]{Mandatory: #1\par No optional.}
+% \end{lstlisting}
+%
+% \begin{function}{\ekvcsvloop}
+% \begin{syntax}
+% \cs{ekvcsvloop}\marg{code}\marg{csv-list}
+% \end{syntax}
+% This loops over the comma separated items in \meta{csv-list} and, after
+% stripping spaces from either end of \meta{item} and removing at most one set
+% of outer braces, leaves
+% \texttt
+% {^^A
+% \cs[no-index]{unexpanded}\hskip0pt
+% \{\hskip0pt
+% \meta{code}\hskip0pt
+% \marg{item}\hskip0pt
+% \}^^A
+% }
+% for each list item in the input stream. Blank elements are ignored (if you
+% need a blank element it should be given as |{}|). It supports both active
+% commas and commas of category other. You could consider it as a watered down
+% version of |\ekvparse|. However it is not alignment safe, which you could
+% achieve by nesting it in |\expanded| (since the braces around the argument
+% of |\expanded| will hide |&|s from \TeX's alignment parsing).
+% \end{function}
+% \example The following splits a comma separated list and prints it in a
+% typewriter font with parentheses around each element.\\
+% \begin{minipage}{.75\linewidth}
+% \begin{lstlisting}
+% \newcommand*\myprocessor[1]{\texttt{(#1)}}
+% \ekvcsvloop\myprocessor{abc,def,ghi}\par
+% \ekvcsvloop\myprocessor{1,,2,,3,,4}\par
+% \end{lstlisting}
+% \end{minipage}^^A
+% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth}
+% \newcommand*\myprocessor[1]{\texttt{(#1)}}
+% \ekvcsvloop\myprocessor{abc,def,ghi}\par
+% \ekvcsvloop\myprocessor{1,,2,,3,,4}\par
+% \end{exresult}
+%
+% \begin{function}{\ekverr}
+% \begin{syntax}
+% \cs{ekverr}\marg{package}\marg{message}
+% \end{syntax}
+% This macro will throw an error fully expandably.\footnotemark\ The error
+% length is limited to a total length of 69~characters, and since ten
+% characters will be added for the formatting (\verb*|! | and
+% \verb*| Error: |) that leaves us with a total length for \meta{package} plus
+% \meta{message} of 59~characters. If the message gets longer \TeX\ will only
+% display the first 69~characters and append |\ETC.| to the end.
+%
+% Neither \meta{package} nor \meta{message} expand any further. Also
+% \meta{package} must not contain an explicit |\par| token or the token
+% |\thanks@jfbu|. No such restriction applies to \meta{message}.
+%
+% If |^^J| is set up as the |\newlinechar| (which is the case in \LaTeXe\ but
+% not in plain \TeX\ by default) you can use that to introduce line breaks in
+% your error message. However that doesn't change the message length limit.
+% \end{function}
+% \footnotetext{The used mechanism was to the best of my knowledge first
+% implemented by Jean-François Burnol.}
+% After your own error message some further text will be placed. The
+% formatting of that text will look good if |^^J| is the |\newlinechar|, else
+% not so much. That text will read:
+% \begin{verbatim}
+% ! Paragraph ended before \<an-expandable-macro>
+% completed due to above exception. If the error
+% summary is not comprehensible see the package
+% documentation.
+% I will try to recover now. If you're in inter-
+% active mode hit <return> at the ? prompt and I
+% continue hoping recovery was complete.
+% \end{verbatim}
+% Any clean up has to be done by you, |\ekverr| will expand to nothing after
+% throwing the error message.
+%
+% In \ConTeXt\ this macro works differently. While still being fully expandable,
+% it doesn't have the character count limitation and doesn't impose restrictions
+% on \meta{package}. It will not display the additional text and adding line
+% breaks is not possible.
+% \example Say we set up a small calculation which works with user input. In our
+% calculation we need a division, so have to watch out for division by zero. If
+% we detect such a case we throw an error and do the recovery by using the
+% biggest integer allowed in \TeX\ as the result.
+% \begin{lstlisting}
+% \newcommand*\mydivision[2]
+% {%
+% \the\numexpr
+% \ifnum\numexpr#2=0 % space here on purpose
+% \ekverr{my}{division by 0. Setting result to 2147483647.}%
+% 2147483647%
+% \else
+% (#1)/(#2)%
+% \fi
+% \relax
+% }
+% $(10+5)/(3-3)\approx\mydivision{10+5}{3-3}$
+% \end{lstlisting}
+% If that code gets executed the following will be the terminal output
+% \begin{verbatim}
+% Runaway argument?
+% ! my Error: division by 0. Setting result to 2147483647.
+% ! Paragraph ended before \<an-expandable-macro>
+% completed due to above exception. If the error
+% summary is not comprehensible see the package
+% documentation.
+% I will try to recover now. If you're in inter-
+% active mode hit <return> at the ? prompt and I
+% continue hoping recovery was complete.
+% <to be read again>
+% \par
+% l.15 $(10+5)/(3-3)\approx\mydivision{10+5}{3-3}
+% $
+% ?
+% \end{verbatim}
+% and the output would contain
+% \exres{$(10+5)/(3-3)\approx2147483647$}
+% if we continued the \TeX\ run at the prompt.
+%
+% \bigskip
+%
+% \begin{function}{\ekv@name,\ekv@name@set,\ekv@name@key}
+% \begin{syntax}
+% \cs{ekv@name}\marg{set}\marg{key}
+% \cs{ekv@name@set}\marg{set}
+% \cs{ekv@name@key}\marg{key}
+% \end{syntax}
+% The names of the macros that correspond to a key in a set are build with
+% these macros. The name is built from two blocks, one that is formatting the
+% \set\ name (|\ekv@name@set|) and one for formatting the \key\ name
+% (|\ekv@name@key|). To get the actual name the argument to |\ekv@name@key|
+% must be |\detokenize|d. Both blocks are put together (with the necessary
+% |\detokenize|) by |\ekv@name|.
+% For |NoVal| keys an additional |N| gets appended irrespective of these
+% macros' definition, so their name is
+% \texttt{\cs[no-index]{ekv@name}\marg{set}\marg{key}N}.\par
+% You can use these macros to implement additional functionality or access key
+% macros outside of \expkv, but \emph{don't} change them! \expkv\ relies on
+% their exact definitions internally.
+% \end{function}
+% \example Execute the callback of the |NoVal| key |key| in set |foo|:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \csname\ekv@name{foo}{key}N\endcsname
+% \end{lstlisting}
+%
+% \subsection{Examples}
+%
+% \subsubsection{Standard Use-Case}
+%
+% Say we have a macro for which we want to create a \kv\ interface. The macro
+% has a parameter, which is stored in the dimension |\ourdim| having a default
+% value from its initialisation. Now we want to be able to change that dimension
+% with the |width| key to some specified value. For that we'd do
+% \begin{lstlisting}
+% \newdimen\ourdim
+% \ourdim=150pt
+% \protected\ekvdef{our}{width}{\ourdim=#1\relax}
+% \end{lstlisting}
+% as you can see, we use the set |our| here. We want the key to behave different
+% if no value is specified. In that case the key should not use its initial
+% value, but be smart and determine the available space from
+% \cs[no-index]{hsize}, so we also define
+% \begin{lstlisting}
+% \protected\ekvdefNoVal{our}{width}{\ourdim=.9\hsize}
+% \end{lstlisting}
+% Now we set up our macro to use this \kv\ interface
+% \begin{lstlisting}
+% \protected\def\ourmacro#1%
+% {\begingroup\ekvset{our}{#1}\the\ourdim\endgroup}
+% \end{lstlisting}
+% Finally we can use our macro like in the following\\
+% \begin{minipage}{.5\linewidth}
+% \begin{lstlisting}
+% \ourmacro{}\par
+% \ourmacro{width}\par
+% \ourmacro{width=5pt}\par
+% \end{lstlisting}
+% \end{minipage}^^A
+% \begin{exresult}[nobeforeafter,box align=center]{.5\linewidth}
+% \newdimen\ourdim\ourdim=150pt
+% \protected\ekvdef{our}{width}{\ourdim=#1\relax}^^A
+% \protected\ekvdefNoVal{our}{width}{\ourdim=.9\hsize}^^A
+% \protected\def\ourmacro#1^^A
+% {\begingroup\ekvset{our}{#1}\the\ourdim\endgroup}^^A
+% \ourmacro{}\par
+% \ourmacro{width}\par
+% \ourmacro{width=5pt}\par
+% \end{exresult}
+%
+% \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
+% us, so the result will be exactly the same -- with the exception that the
+% default will use |\ourdim=.9\hsize\relax| instead.
+% \begin{lstlisting}
+% \input expkv-def % or \usepackage{expkv-def}
+% \ekvdefinekeys{our}
+% {
+% dimen width = \ourdim,
+% qdefault width = .9\hsize,
+% initial width = 150pt
+% }
+% \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 macros used to store the keys):
+% \begin{lstlisting}
+% \makeatletter
+% \newcommand*\myrule@ht{1ex}
+% \newcommand*\myrule@wd{0.1em}
+% \newcommand*\myrule@raise{\z@}
+% \protected\ekvdef{myrule}{ht}{\def\myrule@ht{#1}}
+% \protected\ekvdef{myrule}{wd}{\def\myrule@wd{#1}}
+% \protected\ekvdef{myrule}{raise}{\def\myrule@raise{#1}}
+% \protected\ekvdef{myrule}{lower}{\def\myrule@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@out\endgroup}
+% \end{lstlisting}
+% And finally the output:
+% \begin{lstlisting}
+% \newcommand*\myrule@out{\rule[\myrule@raise]\myrule@wd\myrule@ht}
+% \makeatother
+% \end{lstlisting}
+% And we can use it:\\
+% \begin{minipage}{.5\linewidth}
+% \begin{lstlisting}
+% a\myrule\par
+% a\myrule[ht=2ex,lower=.5ex]\par
+% \myruleset{wd=5pt}
+% a\myrule
+% \end{lstlisting}
+% \end{minipage}^^A
+% \begin{exresult}[nobeforeafter,box align=center]{.5\linewidth}
+% \makeatletter
+% \newcommand*\myrule@ht{1ex}
+% \newcommand*\myrule@wd{0.1em}
+% \newcommand*\myrule@raise{\z@}
+% \protected\ekvdef{myrule}{ht}{\def\myrule@ht{#1}}
+% \protected\ekvdef{myrule}{wd}{\def\myrule@wd{#1}}
+% \protected\ekvdef{myrule}{raise}{\def\myrule@raise{#1}}
+% \protected\ekvdef{myrule}{lower}{\def\myrule@raise{-#1}}
+% \ekvsetdef\myruleset{myrule}
+% \newcommand*\myrule[1][]{\begingroup\myruleset{#1}\myrule@out\endgroup}
+% \newcommand*\myrule@out{\rule[\myrule@raise]\myrule@wd\myrule@ht}
+% \makeatother
+% a\myrule\par
+% a\myrule[ht=2ex,lower=.5ex]\par
+% \myruleset{wd=5pt}
+% a\myrule
+% \end{exresult}
+%
+%
+% \subsubsection{An Expandable \kv\ Macro Using \cs[no-index]{ekvsneak}}
+% \label{sec:sneakex}
+%
+% Let's set up an expandable macro, that uses a \kv\ interface. The problems
+% we'll face for this are:
+% \begin{enumerate}
+% \item ignoring duplicate keys
+% \item default values for keys which weren't used
+% \item providing the values as the correct argument to a macro (ordered)
+% \end{enumerate}
+%
+% First we need to decide which \kv\ parsing macro we want to do this with,
+% |\ekvset| or |\ekvparse|. For this example we also want to show the
+% usage of |\ekvsneak|, hence we'll choose |\ekvset|. And we'll have to use
+% |\ekvset| such that it builds a parsable list for our macro internals. To
+% gain back control after |\ekvset| is done we have to put an internal of our
+% macro at the start of that list, so we use an internal key that uses
+% |\ekvsneakPre| after any user input.
+%
+% To ignore duplicates will be easy if the value of the key used last will be
+% put first in the list, so the following will use |\ekvsneakPre| for the
+% user-level keys. If we wanted some key for which the first usage should be the
+% binding one we would use |\ekvsneak| instead for that key.
+%
+% Providing default values can be done in different ways, we'll use a simple
+% approach in which we'll just put the outcome of our keys if they were used
+% with default values before the parsing list terminator.
+%
+% Ordering the keys can be done simply by searching for a specific token for
+% each argument which acts like a flag, so our sneaked out values will include
+% specific tokens acting as markers.
+%
+% Now that we have answers for our technical problems, we have to decide what
+% our example macro should do. How about we define a macro that calculates the
+% sine of a number and rounds that to a specified precision? As a small extra
+% this macro should understand input in radian and degree and the used
+% trigonometric function should be selectable as well. For the hard part of this
+% task (expandably evaluating trigonometric functions) we'll use the \pkg{xfp}
+% package.
+%
+% First we set up our keys according to our earlier considerations and set up
+% the user facing macro |\sine|. The end marker of the parsing list will be a
+% |\sine@stop| token, which we don't need to define and we put our defaults
+% right before it. The user macro |\sine| uses |\ekvoptargTF| to check for the
+% optional argument short cutting to the final step if no optional argument was
+% found. This way we safe some time in this case, though we have to specify the
+% default values twice.
+%
+% \begin{lstlisting}
+% \RequirePackage{xfp}
+% \makeatletter
+% \ekvdef{expex}{f}{\ekvsneakPre{\f{#1}}}
+% \ekvdef{expex}{round}{\ekvsneakPre{\rnd{#1}}}
+% \ekvdefNoVal{expex}{degree}{\ekvsneakPre{\deg{d}}}
+% \ekvdefNoVal{expex}{radian}{\ekvsneakPre{\deg{}}}
+% \ekvdefNoVal{expex}{internal}{\ekvsneakPre{\sine@rnd}}
+% \newcommand*\sine{\ekvoptargTF\sine@args{\sine@final{sin}{d}{3}}}
+% \newcommand*\sine@args[2]
+% {\ekvset{expex}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine@stop{#2}}
+% \end{lstlisting}
+%
+% Now we need to define some internal macros to extract the value of each key's
+% last usage (remember that this will be the group after the first special
+% flag-token). For that we use one delimited macro per key.
+% \begin{lstlisting}
+% \def\sine@rnd#1\rnd#2#3\sine@stop{\sine@deg#1#3\sine@stop{#2}}
+% \def\sine@deg#1\deg#2#3\sine@stop{\sine@f#1#3\sine@stop{#2}}
+% \def\sine@f#1\f#2#3\sine@stop{\sine@final{#2}}
+% \end{lstlisting}
+% After the macros |\sine@rnd|, |\sine@deg|, and |\sine@f| the macro
+% |\sine@final| will see
+% \texttt
+% {^^A
+% \cs[no-index]{sine@final}^^A
+% \marg{f}\marg{degree/radian}\marg{round}\marg{num}^^A
+% }.
+% Now |\sine@final| has to expandably deal with those arguments such that the
+% |\fpeval| macro of \pkg{xfp} gets the correct input. Luckily this is pretty
+% straight forward in this example. In |\fpeval| the trigonometric functions
+% have names such as |sin| or |cos| and the degree taking variants |sind| or
+% |cosd|. And since the |degree| key puts a |d| in |#2| and the |radian| key
+% leaves |#2| empty all we have to do to get the correct function name is stick
+% the two together.
+% \begin{lstlisting}
+% \newcommand*\sine@final[4]{\fpeval{round(#1#2(#4),#3)}}
+% \makeatother
+% \end{lstlisting}
+% Let's test our macro:\\
+% \begin{minipage}{.75\linewidth}
+% \begin{lstlisting}[gobble=4]
+% \sine{60}\par
+% \sine[round=10]{60}\par
+% \sine[f=cos,radian]{pi}\par
+% \edef\myval{\sine[f=tan]{1}}\texttt{\meaning\myval}
+% \end{lstlisting}
+% \end{minipage}^^A
+% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth}
+% \makeatletter
+% \ekvdef{expex}{f}{\ekvsneakPre{\f{#1}}}^^A
+% \ekvdef{expex}{round}{\ekvsneakPre{\rnd{#1}}}^^A
+% \ekvdefNoVal{expex}{degree}{\ekvsneakPre{\deg{d}}}^^A
+% \ekvdefNoVal{expex}{radian}{\ekvsneakPre{\deg{}}}^^A
+% \ekvdefNoVal{expex}{internal}{\ekvsneakPre{\sine@rnd}}^^A
+% \newcommand*\sine{\ekvoptargTF\sine@args{\sine@final{sin}{d}{3}}}
+% \newcommand*\sine@args[2]
+% {\ekvset{expex}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine@stop{#2}}
+% \def\sine@rnd#1\rnd#2#3\sine@stop{\sine@deg#1#3\sine@stop{#2}}^^A
+% \def\sine@deg#1\deg#2#3\sine@stop{\sine@f#1#3\sine@stop{#2}}^^A
+% \def\sine@f#1\f#2#3\sine@stop{\sine@final{#2}}^^A
+% \newcommand*\sine@final[4]{\fpeval{round(#1#2(#4),#3)}}^^A
+% \makeatother
+% \sine{60}\par
+% \sine[round=10]{60}\par
+% \sine[f=cos,radian]{pi}\par
+% \edef\myval{\sine[f=tan]{1}}\texttt{\meaning\myval}
+% \end{exresult}
+%
+% \paragraph{The same macro using \protect\expkvc}
+% Using \expkvc\ we can set up something equivalent with a bit less code. The
+% implementation chosen in \expkvc\ is more efficient than the example above and
+% way easier to code for the user.
+% \begin{lstlisting}
+% \makeatletter
+% \newcommand*\sine{\ekvoptargTF\sine@a{\sine@b{sin}{d}{3}}}
+% \ekvcSplitAndForward\sine@a\sine@b
+% {
+% f=sin,
+% unit=d,
+% round=3,
+% }
+% \ekvcSecondaryKeys\sine@a
+% {
+% nmeta degree={unit=d},
+% nmeta radian={unit={}},
+% }
+% \newcommand*\sine@b[4]{\fpeval{round(#1#2(#4),#3)}}
+% \makeatother
+% \end{lstlisting}
+% The resulting macro will behave just like the one previously defined, but will
+% have an additional |unit| key, since in \expkvc\ every argument must have a
+% value taking key which defines it.
+%
+%
+% \subsection{Error Messages}
+%
+% \expkv\ should only send messages in case of errors, there are no warnings and
+% no info messages. In this subsection those errors are listed.
+%
+% \subsubsection{Load Time}
+%
+% \file{expkv.tex} checks whether \eTeX\ and the |\expanded| primitive are
+% available. If it isn't, an error will be thrown using |\errmessage|:
+% \begin{lstlisting}
+% ! expkv Error: e-TeX and \expanded required.
+% \end{lstlisting}
+%
+% \subsubsection{Defining Keys}
+%
+% If you get any error from \expkv\ while you're trying to define a key, the
+% definition will be aborted and gobbled.
+%
+% If you try to define a key with an empty set name you'll get:
+% \begin{lstlisting}
+% ! expkv Error: empty set name not allowed.
+% \end{lstlisting}
+%
+% Similarly, if you try to define a key with an empty key name:
+% \begin{lstlisting}
+% ! expkv Error: empty key name not allowed.
+% \end{lstlisting}
+%
+% Both of these messages are done in a way that doesn't throw additional errors
+% due to |\global|, |\long|, etc., not being used correctly if you prefixed one
+% of the defining macros.
+%
+% \subsubsection{Using Keys}
+%
+% This subsubsection contains the errors thrown during |\ekvset|. The errors are
+% thrown in an expandable manner using |\ekverr|. In the following messages
+% \lstinline|<key>| gets replaced with the problematic key's name, and
+% \lstinline|<set>| with the corresponding set. If any errors during \kv\
+% handling are encountered, the entry in the comma separated list will be
+% omitted after the error is thrown and the next \kv\ pair will be parsed.
+%
+% If you're using an undefined key you'll get:
+% \begin{lstlisting}
+% Runaway argument?
+% ! expkv Error: unknown key `<key>' in set `<set>'
+% \end{lstlisting}
+%
+% If you're using a key for which only a normal version and no |NoVal| version
+% is defined, but don't provide a value, you'll get:
+% \begin{lstlisting}
+% Runaway argument?
+% ! expkv Error: missing value for `<key>' in set `<set>'
+% \end{lstlisting}
+%
+% If you're using a key for which only a |NoVal| version and no normal version
+% is defined, but provide a value, you'll get:
+% \begin{lstlisting}
+% Runaway argument?
+% ! expkv Error: unwanted value for `<key>' in 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}
+% Runaway 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}
+% Runaway argument?
+% ! expkv Error: no NoVal key `<key>' in sets {<set1>}{<set2>}...
+% \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
+% not be gracefully ignored). The error will look like
+% \begin{lstlisting}[language={}]
+% ! Missing \endcsname inserted.
+% <to be read again>
+% \! 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 } % setup
+% \setparameterlist{yax}{ height = 6 } % benchmark
+% \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\
+% Jonathan P. Spratte
+%
+% \medskip\noindent
+% 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
+% (at your option) any later version. The latest version of this license is in
+% the file:
+%
+% \url{http://www.latex-project.org/lppl.txt}
+%
+% \noindent
+% This work is ``maintained'' (as per LPPL maintenance status) by
+%
+% \mbox{Jonathan P. Spratte}.
+%
+% \end{documentation}^^A=<<
+%
+% \begin{implementation}^^A>>=
+%
+% \clearpage
+%
+% \section{Implementation}^^A>>=
+%^^A the LaTeX package >>=
+% \subsection{The \LaTeX\ Package}
+% First we set up the \LaTeX\ package. That one doesn't really do much except
+% |\input|ting the generic code and identifying itself as a package.
+% \gobbledocstriptag
+%<*pkg>
+% \begin{macrocode}
+\def\ekv@tmp
+ {%
+ \ProvidesFile{expkv.tex}%
+ [\ekvDate\space v\ekvVersion\space an expandable key=val implementation]%
+ }
+\input{expkv.tex}
+\ProvidesPackage{expkv}%
+ [\ekvDate\space v\ekvVersion\space an expandable key=val implementation]
+% \end{macrocode}
+% \gobbledocstriptag
+%</pkg>
+%^^A=<<
+%^^A the ConTeXt module >>=
+% \subsection{The \ConTeXt\ module}
+% This is pretty straight forward, we just have to change the error throwing
+% mechanism for \ConTeXt\ (the approach taken for plain and \LaTeX\ breaks in
+% \ConTeXt, effectively breaking \ConTeXt, dropping you in an interactive \TeX\
+% session with almost no means of escape).
+% \gobbledocstriptag
+%<*ctx>
+% \begin{macrocode}
+\writestatus{loading}{ConTeXt User Module / expkv}
+\unprotect
+\input expkv.tex
+\long\def\ekv@err@collect#1\par#2%
+ {\directlua{tex.error[[\detokenize{#2} Error: #1]]}}
+\writestatus{loading}
+ {ConTeXt User Module / expkv / Version \ekvVersion\space loaded}
+\protect\endinput
+% \end{macrocode}
+% \gobbledocstriptag
+%</ctx>
+%^^A=<<
+%^^A main file >>=
+% \subsection{The Generic Code}
+% The rest of this implementation will be the generic code.
+% \gobbledocstriptag
+%<*tex>
+%
+% We make sure that it's only input once:
+% \begin{macrocode}
+\expandafter\ifx\csname ekvVersion\endcsname\relax
+\else
+ \expandafter\endinput
+\fi
+% \end{macrocode}
+%
+% Check whether \eTeX\ and |\expanded| are available -- \expkv\ requires \eTeX.
+% \begin{macrocode}
+\begingroup
+ \edef\ekvtmpa{\string\expanded}
+ \edef\ekvtmpb{\meaning\expanded}
+ \expandafter
+\endgroup
+\ifx\ekvtmpa\ekvtmpb
+ \expandafter\let\csname ekv@expanded\endcsname\expanded
+ \expandafter\let\csname ekv@unexpanded\endcsname\unexpanded
+\else
+ \begingroup
+ \edef\ekvtmpa{\string\expanded}
+ \edef\ekvtmpb{\meaning\normalexpanded}
+ \expandafter
+ \endgroup
+ \ifx\ekvtmpa\ekvtmpb
+ \expandafter\let\csname ekv@expanded\endcsname\normalexpanded
+ \expandafter\let\csname ekv@unexpanded\endcsname\normalunexpanded
+ \else
+ \errmessage
+ {expkv Error: e-TeX and the \noexpand\expanded primitive required}%
+ \expandafter\endinput
+ \fi
+\fi
+% \end{macrocode}
+%
+% \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.9a}
+\def\ekvDate{2021-09-20}
+% \end{macrocode}
+% \end{macro}
+%
+% If the \LaTeX\ format is loaded we want to be a good file and report back who
+% we are, for this the package will have defined |\ekv@tmp| to use
+% |\ProvidesFile|, else this will expand to a |\relax| and do no harm.
+% \begin{macrocode}
+\csname ekv@tmp\endcsname
+% \end{macrocode}
+%
+% Store the category code of |@| to later be able to reset it and change it to
+% 11 for now.
+% \begin{macrocode}
+\expandafter\chardef\csname ekv@tmp\endcsname=\catcode`\@
+\catcode`\@=11
+% \end{macrocode}
+% |\ekv@tmp| might later be reused to gobble any prefixes which might be
+% provided to |\ekvdef| and similar in case the names are invalid, we just
+% temporarily use it here as means to store the current category code of |@| to
+% restore it at the end of the file, we never care for the actual definition of
+% it.
+%
+% \begin{macro}[internal]{\ekv@if@lastnamedcs}
+% If the primitive |\lastnamedcs| is available, we can be a bit faster than
+% without it. So we test for this and save the test's result in this macro.
+% \begin{macrocode}
+\begingroup
+ \edef\ekv@tmpa{\string \lastnamedcs}
+ \edef\ekv@tmpb{\meaning\lastnamedcs}
+ \ifx\ekv@tmpa\ekv@tmpb
+ \def\ekv@if@lastnamedcs{\long\def\ekv@if@lastnamedcs##1##2{##1}}
+ \else
+ \def\ekv@if@lastnamedcs{\long\def\ekv@if@lastnamedcs##1##2{##2}}
+ \fi
+ \expandafter
+\endgroup
+\ekv@if@lastnamedcs
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@empty}
+% Sometimes we have to introduce a token to prevent accidental brace stripping.
+% This token would then need to be removed by |\@gobble| or similar. Instead we
+% can use |\ekv@empty| which will just expand to nothing, that is faster than
+% gobbling an argument.
+% \begin{macrocode}
+\def\ekv@empty{}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {
+% \@gobble,\@firstofone,\@firstoftwo,\@secondoftwo,
+% \ekv@fi@gobble,\ekv@fi@firstofone,\ekv@fi@firstoftwo,\ekv@fi@secondoftwo,
+% \ekv@gobble@mark,\ekv@gobbleto@stop,\ekv@gobble@from@mark@to@stop
+% }
+% Since branching tests are often more versatile than |\if...\else...\fi|
+% constructs, we define helpers that are branching pretty fast. Also here are
+% some other utility functions that just grab some tokens. The ones that are
+% also contained in \LaTeX\ don't use the |ekv| prefix. Not all of the ones
+% defined here are really needed by \expkv\ but are provided because packages
+% like \expkvd\ or \expkvo\ need them (and I don't want to define them in each
+% package which might need them).
+% \begin{macrocode}
+\long\def\@gobble#1{}
+\long\def\@firstofone#1{#1}
+\long\def\@firstoftwo#1#2{#1}
+\long\def\@secondoftwo#1#2{#2}
+\long\def\ekv@fi@gobble\fi\@firstofone#1{\fi}
+\long\def\ekv@fi@firstofone\fi\@gobble#1{\fi#1}
+\long\def\ekv@fi@firstoftwo\fi\@secondoftwo#1#2{\fi#1}
+\long\def\ekv@fi@secondoftwo\fi\@firstoftwo#1#2{\fi#2}
+\def\ekv@gobble@mark\ekv@mark{}
+\long\def\ekv@gobbleto@stop#1\ekv@stop{}
+\long\def\ekv@gobble@from@mark@to@stop\ekv@mark#1\ekv@stop{}
+% \end{macrocode}
+% \end{macro}
+% As you can see |\ekv@gobbleto@stop| uses a special marker |\ekv@stop|. The
+% package will use three such markers, the one you've seen already, |\ekv@mark|
+% and |\ekv@nil|. Contrarily to how for instance \pkg{expl3} does things, we
+% don't define them, as we don't need them to have an actual meaning. This has
+% the advantage that if they somehow get expanded -- which should never happen
+% if things work out -- they'll throw an error directly.
+%
+% \begin{macro}[internal]
+% {
+% \ekv@ifempty,\ekv@ifempty@,\ekv@ifempty@true,\ekv@ifempty@false,
+% \ekv@ifempty@true@F,\ekv@ifempty@true@F@gobble,
+% \ekv@ifempty@true@F@gobbletwo
+% }
+% We can test for a lot of things building on an if-empty test, so lets define a
+% really fast one. Since some tests might have reversed logic (true if something
+% is not empty) we also set up macros for the reversed branches.
+% \begin{macrocode}
+\long\def\ekv@ifempty#1%
+ {%
+ \ekv@ifempty@\ekv@ifempty@A#1\ekv@ifempty@B\ekv@ifempty@true
+ \ekv@ifempty@A\ekv@ifempty@B\@secondoftwo
+ }
+\long\def\ekv@ifempty@#1\ekv@ifempty@A\ekv@ifempty@B{}
+\long\def\ekv@ifempty@true\ekv@ifempty@A\ekv@ifempty@B\@secondoftwo#1#2{#1}
+\long\def\ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo#1#2{#2}
+\long\def\ekv@ifempty@true@F\ekv@ifempty@A\ekv@ifempty@B\@firstofone#1{}
+\long\def\ekv@ifempty@true@F@gobble\ekv@ifempty@A\ekv@ifempty@B\@firstofone#1#2%
+ {}
+\long\def\ekv@ifempty@true@F@gobbletwo
+ \ekv@ifempty@A\ekv@ifempty@B\@firstofone#1#2#3%
+ {}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@ifblank,\ekv@ifblank@}
+% The obvious test that can be based on an if-empty is if-blank, meaning a test
+% checking whether the argument is empty or consists only of spaces. Our version
+% here will be tweaked a bit, as we want to check this, but with one leading
+% |\ekv@mark| token that is to be ignored. The wrapper |\ekv@ifblank| will not
+% be used by \expkv\ for speed reasons but \expkvo\ uses it.
+% \begin{macrocode}
+\long\def\ekv@ifblank#1%
+ {%
+ \ekv@ifblank@#1\ekv@nil\ekv@ifempty@B\ekv@ifempty@true
+ \ekv@ifempty@A\ekv@ifempty@B\@secondoftwo
+ }
+\long\def\ekv@ifblank@\ekv@mark#1{\ekv@ifempty@\ekv@ifempty@A}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@ifdefined}
+% We'll need to check whether something is defined quite frequently, so why not
+% define a macro that does this. The following test is expandable and pretty
+% fast. The version with |\lastnamedcs| is the fastest version to test for an
+% undefined macro I know of (that considers both undefined macros and those with
+% the meaning |\relax|).
+% \begin{macrocode}
+\ekv@if@lastnamedcs
+ {%
+ \long\def\ekv@ifdefined#1{\ifcsname#1\endcsname\ekv@ifdef@\fi\@secondoftwo}
+ \def\ekv@ifdef@\fi\@secondoftwo
+ {%
+ \fi
+ \expandafter\ifx\lastnamedcs\relax
+ \ekv@fi@secondoftwo
+ \fi
+ \@firstoftwo
+ }
+ }
+ {%
+ \long\def\ekv@ifdefined#1%
+ {%
+ \ifcsname#1\endcsname\ekv@ifdef@\fi\ekv@ifdef@false#1\endcsname\relax
+ \ekv@fi@secondoftwo
+ \fi
+ \@firstoftwo
+ }
+ \def\ekv@ifdef@\fi\ekv@ifdef@false{\fi\expandafter\ifx\csname}
+ \long\def\ekv@ifdef@false
+ #1\endcsname\relax\ekv@fi@secondoftwo\fi\@firstoftwo#2#3%
+ {#3}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@strip,\ekv@strip@a,\ekv@strip@b,\ekv@strip@c}
+% We borrow some ideas of \pkg{expl3}'s \pkg{l3tl} to strip spaces
+% from keys and values. This |\ekv@strip| also strips one level of outer braces
+% \emph{after} stripping spaces, so an input of | {abc} | becomes |abc| after
+% stripping. It should be used with |#1| prefixed by |\ekv@mark|. Also this
+% implementation at most strips \emph{one} space from both sides (which should
+% be fine most of the time, since \TeX\ reads consecutive spaces as a single one
+% during tokenisation).
+% \begin{macrocode}
+\def\ekv@strip#1%
+ {%
+ \long\def\ekv@strip##1%
+ {%
+ \ekv@strip@a
+ ##1\ekv@nil
+ \ekv@mark#1%
+ #1\ekv@nil
+ }%
+ \long\def\ekv@strip@a##1\ekv@mark#1{\ekv@strip@b##1\ekv@mark}%
+ }
+\ekv@strip{ }
+\long\def\ekv@strip@b#1 \ekv@nil{\ekv@strip@c#1\ekv@nil}
+\long\def\ekv@strip@c\ekv@mark#1\ekv@nil\ekv@mark#2\ekv@nil#3{#3{#1}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {\ekv@exparg,\ekv@exparg@,\ekv@expargtwice,\ekv@expargtwice@,\ekv@zero}
+% To reduce some code doublets while gaining some speed (and also as convenience
+% for other packages in the family), it is often useful to expand the first
+% token in a definition once. Let's define a wrapper for this.
+%
+% Also, to end a |\romannumeral| expansion, we want to use |\z@|, which is
+% contained in both plain \TeX\ and \LaTeX, but we use a private name for it to
+% make it easier to spot and hence easier to manage.
+% \begin{macrocode}
+\let\ekv@zero\z@
+\long\def\ekv@exparg#1#2{\expandafter\ekv@exparg@\expandafter{#2}{#1}}
+\long\def\ekv@exparg@#1#2{#2{#1}}%
+\long\def\ekv@expargtwice#1#2{\expandafter\ekv@expargtwice@\expandafter{#2}{#1}}
+\def\ekv@expargtwice@{\expandafter\ekv@exparg@\expandafter}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvcsvloop}
+% \begin{macro}[internal]{\ekv@csv@loop@active,\ekv@csv@loop@active@end}
+% An |\ekvcsvloop| will just loop over a csv list in a simple manner. First we
+% split at active commas (gives better performance this way), next we have to
+% check whether we're at the end of the list (checking for |\ekv@stop|). If
+% not we go on splitting at commas of category other.
+% \begin{macrocode}
+\begingroup
+\def\ekvcsvloop#1{%
+\endgroup
+\long\def\ekvcsvloop##1##2%
+ {\ekv@csv@loop@active{##1}\ekv@mark##2#1\ekv@stop#1}
+% \end{macrocode}
+% This does the same as |\ekv@csv@loop| but for active commas.
+% \begin{macrocode}
+\long\def\ekv@csv@loop@active##1##2#1%
+ {%
+ \ekv@gobble@from@mark@to@stop##2\ekv@csv@loop@active@end\ekv@stop
+ \ekv@csv@loop{##1}##2,\ekv@stop,%
+ }%
+\long\def\ekv@csv@loop@active@end
+ \ekv@stop
+ \ekv@csv@loop##1\ekv@mark\ekv@stop,\ekv@stop,%
+ {}%
+}
+% \end{macrocode}
+% Do the definitions with the weird catcode.
+% \begin{macrocode}
+\catcode`\,=13
+\ekvcsvloop,
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@csv@loop,\ekv@csv@loop@do,\ekv@csv@loop@end}
+% We use temporary macros and an |\expandafter| chain to preexpand
+% |\ekv@strip| here. After splitting at other commas we check again for end
+% the end of the sublist, check for blank elements which should be ignored,
+% and else strip spaces and execute the user code (protecting it from further
+% expanding with |\unexpanded|).
+% \begin{macrocode}
+\def\ekv@csv@loop#1%
+ {%
+ \long\def\ekv@csv@loop##1##2,%
+ {%
+ \ekv@gobble@from@mark@to@stop##2\ekv@csv@loop@end\ekv@stop
+ \ekv@ifblank@##2\ekv@nil\ekv@ifempty@B\ekv@csv@loop@blank
+ \ekv@ifempty@A\ekv@ifempty@B
+ #1\ekv@csv@loop@do{##1}%
+ }%
+ }
+\expandafter\ekv@csv@loop\expandafter{\ekv@strip{#2}}
+\long\def\ekv@csv@loop@do#1#2{\ekv@unexpanded{#2{#1}}\ekv@csv@loop{#2}\ekv@mark}
+\def\ekv@csv@loop@end#1%
+ {%
+ \long\def\ekv@csv@loop@end
+ \ekv@stop
+ \ekv@ifblank@\ekv@mark\ekv@stop\ekv@nil\ekv@ifempty@B\ekv@csv@loop@blank
+ \ekv@ifempty@A\ekv@ifempty@B
+ #1\ekv@csv@loop@do##1%
+ {\ekv@csv@loop@active{##1}\ekv@mark}%
+ }
+\expandafter\ekv@csv@loop@end\expandafter{\ekv@strip{\ekv@mark\ekv@stop}}
+\long\expandafter\def\expandafter\ekv@csv@loop@blank
+ \expandafter\ekv@ifempty@A\expandafter\ekv@ifempty@B
+ \ekv@strip{\ekv@mark#1}\ekv@csv@loop@do#2%
+ {\ekv@csv@loop{#2}\ekv@mark}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekv@name,\ekv@name@set,\ekv@name@key}
+% The keys will all follow the same naming scheme, so we define it here.
+% \begin{macrocode}
+\def\ekv@name@set#1{ekv#1(}
+\long\def\ekv@name@key#1{#1)}
+\edef\ekv@name
+ {%
+ \ekv@unexpanded\expandafter{\ekv@name@set{#1}}%
+ \ekv@unexpanded\expandafter{\ekv@name@key{\detokenize{#2}}}%
+ }
+\long\ekv@exparg{\def\ekv@name#1#2}{\ekv@name}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@undefined@set}
+% We can misuse the macro name we use to expandably store the set-name in a
+% single token -- since this increases performance drastically, especially for
+% long set-names -- to throw a more meaningful error message in case a set isn't
+% defined. The name of |\ekv@undefined@set| is a little bit misleading, as it is
+% called in either case inside of |\csname|, but the result will be a control
+% sequence with meaning |\relax| if the set is undefined, hence will break the
+% |\csname| building the key-macro which will throw the error message.
+% \begin{macrocode}
+\def\ekv@undefined@set#1{! expkv Error: Set `#1' undefined.}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@checkvalid}
+% We place some restrictions on the allowed names, though, namely sets and
+% keys are not allowed to be empty -- blanks are fine (meaning \mbox{set-
+% or} key-names consisting of spaces). The |\def\ekv@tmp| gobbles any \TeX\
+% prefixes which would otherwise throw errors. This will, however, break the
+% package if an |\outer| has been gobbled this way. I consider that good,
+% because keys shouldn't be defined |\outer| anyways.
+% \begin{macrocode}
+\edef\ekv@checkvalid
+ {%
+ \ekv@unexpanded\expandafter{\ekv@ifempty{#1}}%
+ \ekv@unexpanded
+ {{%
+ \def\ekv@tmp{}%
+ \errmessage{expkv Error: empty set name not allowed}%
+ }}%
+ {%
+ \ekv@unexpanded\expandafter{\ekv@ifempty{#2}}%
+ \ekv@unexpanded
+ {%
+ {%
+ \def\ekv@tmp{}%
+ \errmessage{expkv Error: empty key name not allowed}%
+ }%
+ \@secondoftwo
+ }%
+ }%
+ \ekv@unexpanded{\@gobble}%
+ }
+\ekv@exparg{\protected\def\ekv@checkvalid#1#2}{\ekv@checkvalid}%
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvifdefined,\ekvifdefinedNoVal}
+% And provide user-level macros to test whether a key is defined.
+% \begin{macrocode}
+\ekv@expargtwice{\long\def\ekvifdefined#1#2}%
+ {\expandafter\ekv@ifdefined\expandafter{\ekv@name{#1}{#2}}}
+\ekv@expargtwice{\long\def\ekvifdefinedNoVal#1#2}%
+ {\expandafter\ekv@ifdefined\expandafter{\ekv@name{#1}{#2}N}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \ekvdef,\ekvdefNoVal,\ekvlet,\ekvletNoVal,\ekvletkv,\ekvletkvNoVal,
+% \ekvdefunknown,\ekvdefunknownNoVal
+% }
+% Set up the key defining macros |\ekvdef| etc. We use temporary macros to set
+% these up with a few expansions already done.
+% \begin{macrocode}
+\def\ekvdef#1#2#3#4%
+ {%
+ \protected\long\def\ekvdef##1##2##3%
+ {#1{\expandafter\def\csname#2\endcsname####1{##3}#3}}%
+ \protected\long\def\ekvdefNoVal##1##2##3%
+ {#1{\expandafter\def\csname#2N\endcsname{##3}#3}}%
+ \protected\long\def\ekvlet##1##2##3%
+ {#1{\expandafter\let\csname#2\endcsname##3#3}}%
+ \protected\long\def\ekvletNoVal##1##2##3%
+ {#1{\expandafter\let\csname#2N\endcsname##3#3}}%
+ \ekv@expargtwice{\protected\long\def\ekvdefunknown##1##2}%
+ {%
+ \romannumeral
+ \expandafter\ekv@exparg@\expandafter
+ {%
+ \expandafter\expandafter\expandafter
+ \def\expandafter\csname\ekv@name{##1}{}u\endcsname####1####2{##2}%
+ #3%
+ }%
+ {\ekv@zero\ekv@checkvalid{##1}.}%
+ }%
+ \ekv@expargtwice{\protected\long\def\ekvdefunknownNoVal##1##2}%
+ {%
+ \romannumeral
+ \expandafter\ekv@exparg@\expandafter
+ {%
+ \expandafter\expandafter\expandafter
+ \def\expandafter\csname\ekv@name{##1}{}uN\endcsname####1{##2}%
+ #3%
+ }%
+ {\ekv@zero\ekv@checkvalid{##1}.}%
+ }%
+ \protected\long\def\ekvletkv##1##2##3##4%
+ {%
+ #1%
+ {%
+ \expandafter\let\csname#2\expandafter\endcsname
+ \csname#4\endcsname
+ #3%
+ }%
+ }%
+ \protected\long\def\ekvletkvNoVal##1##2##3##4%
+ {%
+ #1%
+ {%
+ \expandafter\let\csname#2N\expandafter\endcsname
+ \csname#4N\endcsname
+ #3%
+ }%
+ }%
+ }
+\edef\ekvdefNoVal
+ {%
+ {\ekv@unexpanded\expandafter{\ekv@checkvalid{#1}{#2}}}%
+ {\ekv@unexpanded\expandafter{\ekv@name{#1}{#2}}}%
+ {%
+ \ekv@unexpanded{\expandafter\ekv@defsetmacro\csname}%
+ \ekv@unexpanded\expandafter{\ekv@undefined@set{#1}\endcsname{#1}}%
+ }%
+ {\ekv@unexpanded\expandafter{\ekv@name{#3}{#4}}}%
+ }
+\expandafter\ekvdef\ekvdefNoVal
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvredirectunknown,\ekvredirectunknownNoVal}
+% \begin{macro}[internal]
+% {
+% \ekv@defredirectunknown,\ekv@redirectunknown@aux,
+% \ekv@redirectunknownNoVal@aux
+% }
+% The redirection macros prepare the unknown function by looping over the
+% provided list of sets and leaving a |\ekv@redirect@kv| or |\ekv@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@defredirectunknown
+ \ekv@redirect@kv
+ \ekv@err@redirect@kv@notfound
+ {\long\ekvdefunknown}%
+ \ekv@redirectunknown@aux
+ }
+\protected\def\ekvredirectunknownNoVal
+ {%
+ \ekv@defredirectunknown
+ \ekv@redirect@k
+ \ekv@err@redirect@k@notfound
+ \ekvdefunknownNoVal
+ \ekv@redirectunknownNoVal@aux
+ }
+\protected\def\ekv@defredirectunknown#1#2#3#4#5#6%
+ {%
+ \begingroup
+ \edef\ekv@tmp
+ {%
+ \ekvcsvloop#1{#6}%
+ \ekv@unexpanded{#2}%
+ {\ekvcsvloop{}{#5,#6}}%
+ }%
+ \ekv@expargtwice
+ {\endgroup#3{#5}}%
+ {\expandafter#4\ekv@tmp\ekv@stop}%
+ }
+\def\ekv@redirectunknown@aux#1{#1{##1}{##2}}
+\def\ekv@redirectunknownNoVal@aux#1{#1{##1}}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {
+% \ekv@redirect@k,\ekv@redirect@k@a,\ekv@redirect@k@a@,
+% \ekv@redirect@k@b,\ekv@redirect@k@c,\ekv@redirect@k@d,
+% \ekv@redirect@kv,\ekv@redirect@kv@a,\ekv@redirect@kv@a@,
+% \ekv@redirect@kv@b,\ekv@redirect@kv@c,\ekv@redirect@kv@d
+% }
+% The redirect code works by some simple loop over all the sets, which we
+% already preprocessed in |\ekv@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@name| constellations ready.
+% \begin{macrocode}
+\def\ekv@redirect@k#1#2#3#4%
+ {%
+ \ekv@if@lastnamedcs
+ {%
+ \def\ekv@redirect@k##1##2##3%
+ {%
+ \ifcsname#1\endcsname\ekv@redirect@k@a\fi
+ ##3{##1}%
+ }%
+ \def\ekv@redirect@k@a\fi{\fi\expandafter\ekv@redirect@k@b\lastnamedcs}%
+ \long\def\ekv@redirect@kv##1##2##3##4%
+ {%
+ \ifcsname#2\endcsname\ekv@redirect@kv@a\fi\@gobble{##1}%
+ ##4{##1}{##2}%
+ }%
+ \def\ekv@redirect@kv@a\fi\@gobble
+ {\fi\expandafter\ekv@redirect@kv@b\lastnamedcs}%
+ }
+ {%
+ \def\ekv@redirect@k##1##2##3%
+ {%
+ \ifcsname#1\endcsname\ekv@redirect@k@a\fi\ekv@redirect@k@a@
+ #1\endcsname
+ ##3{##1}%
+ }%
+ \def\ekv@redirect@k@a@#3\endcsname{}%
+ \def\ekv@redirect@k@a\fi\ekv@redirect@k@a@
+ {\fi\expandafter\ekv@redirect@k@b\csname}%
+ \long\def\ekv@redirect@kv##1##2##3##4%
+ {%
+ \ifcsname#2\endcsname\ekv@redirect@kv@a\fi\ekv@redirect@kv@a@
+ #2\endcsname{##1}%
+ ##4{##1}{##2}%
+ }%
+ \long\def\ekv@redirect@kv@a@#4\endcsname##3{}%
+ \def\ekv@redirect@kv@a\fi\ekv@redirect@kv@a@
+ {\fi\expandafter\ekv@redirect@kv@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@redirect@kv#1\detokenize#2#3\ekv@stop{\ekv@unexpanded{#1#2#3}}
+\edef\ekv@redirect@kv
+ {%
+ {\expandafter\ekv@redirect@kv\ekv@name{#2}{#1}N\ekv@stop}%
+ {\expandafter\ekv@redirect@kv\ekv@name{#3}{#2}\ekv@stop}%
+ {\expandafter\ekv@redirect@kv\ekv@name{#1}{#2}N\ekv@stop}%
+ {\expandafter\ekv@redirect@kv\ekv@name{#1}{#2}\ekv@stop}%
+ }
+% \end{macrocode}
+% Everything is ready to make the real definitions.
+% \begin{macrocode}
+\expandafter\ekv@redirect@k\ekv@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@redirect@k@b#1%
+ {\ifx\relax#1\ekv@redirect@k@c\fi\ekv@redirect@k@d#1}
+\def\ekv@redirect@k@c\fi\ekv@redirect@k@d#1{\fi}
+\def\ekv@redirect@k@d#1#2\ekv@stop{#1}
+\def\ekv@redirect@kv@b#1%
+ {\ifx\relax#1\ekv@redirect@kv@c\fi\ekv@redirect@kv@d#1}
+\long\def\ekv@redirect@kv@c\fi\ekv@redirect@kv@d#1#2{\fi}
+\long\def\ekv@redirect@kv@d#1#2#3\ekv@stop{#1{#2}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@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
+% sequence.
+% \begin{macrocode}
+\edef\ekv@defsetmacro
+ {%
+ \ekv@unexpanded{\ifx#1\relax\edef#1##1}%
+ {%
+ \ekv@unexpanded\expandafter{\ekv@name@set{#2}}%
+ \ekv@unexpanded\expandafter{\ekv@name@key{##1}}%
+ }%
+ \ekv@unexpanded{\fi}%
+ }
+\ekv@exparg{\protected\def\ekv@defsetmacro#1#2}{\ekv@defsetmacro}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvifdefinedset}
+% \begin{macrocode}
+\ekv@expargtwice{\def\ekvifdefinedset#1}%
+ {\expandafter\ekv@ifdefined\expandafter{\ekv@undefined@set{#1}}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvset}
+% Set up |\ekvset|, which should not be affected by active commas and equal
+% signs. The equal signs are a bit harder to cope with and we'll do that later,
+% but the active commas can be handled by just doing two comma-splitting loops
+% one at actives one at others. That's why we define |\ekvset| here with a
+% temporary meaning just to set up the things with two different category codes.
+% |#1| will be a \texttt{,\textsubscript{13}} and |#2| will be a
+% \texttt{=\textsubscript{13}}.
+% \begin{macrocode}
+\begingroup
+\def\ekvset#1#2{%
+\endgroup
+\ekv@exparg{\long\def\ekvset##1##2}%
+ {%
+ \expandafter\expandafter\expandafter
+ \ekv@set\expandafter\csname\ekv@undefined@set{##1}\endcsname
+ \ekv@mark##2#1\ekv@stop#1{}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@set}
+% |\ekv@set| will split the \kv\ list at active commas. Then it has to check
+% whether there were unprotected other commas and resplit there.
+% \begin{macrocode}
+\long\def\ekv@set##1##2#1%
+ {%
+% \end{macrocode}
+% Test whether we're at the end, if so invoke |\ekv@endset|,
+% \begin{macrocode}
+ \ekv@gobble@from@mark@to@stop##2\ekv@endset\ekv@stop
+% \end{macrocode}
+% else go on with other commas.
+% \begin{macrocode}
+ \ekv@set@other##1##2,\ekv@stop,%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@endset}
+% |\ekv@endset| is a hungry little macro. It will eat everything that remains
+% of |\ekv@set| and unbrace the sneaked stuff.
+% \begin{macrocode}
+\long\def\ekv@endset
+ \ekv@stop\ekv@set@other##1\ekv@mark\ekv@stop,\ekv@stop,##2%
+ {##2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@eq@other,\ekv@eq@active}
+% Splitting at equal signs will be done in a way that checks whether there is
+% an equal sign and splits at the same time. This gets quite messy and the
+% code might look complicated, but this is pretty fast (faster than first
+% checking for an equal sign and splitting if one is found). The splitting
+% code will be adapted for |\ekvset| and |\ekvparse| to get the most speed,
+% but some of these macros don't require such adaptions. |\ekv@eq@other| and
+% |\ekv@eq@active| will split the argument at the first equal sign and insert
+% the macro which comes after the first following |\ekv@mark|. This allows for
+% fast branching based on \TeX's argument grabbing rules and we don't have to
+% split after the branching if the equal sign was there.
+% \begin{macrocode}
+\long\def\ekv@eq@other##1=##2\ekv@mark##3{##3##1\ekv@stop\ekv@mark##2}
+\long\def\ekv@eq@active##1#2##2\ekv@mark##3{##3##1\ekv@stop\ekv@mark##2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@set@other}
+% The macro |\ekv@set@other| is guaranteed to get only single \kv\ pairs.
+% \begin{macrocode}
+\long\def\ekv@set@other##1##2,%
+ {%
+% \end{macrocode}
+% First we test whether we're done.
+% \begin{macrocode}
+ \ekv@gobble@from@mark@to@stop##2\ekv@endset@other\ekv@stop
+% \end{macrocode}
+% If not we split at the equal sign of category other.
+% \begin{macrocode}
+ \ekv@eq@other##2\ekv@nil\ekv@mark\ekv@set@eq@other@a
+ =\ekv@mark\ekv@set@eq@active
+% \end{macrocode}
+% And insert the set name for the next recursion step of |\ekv@set@other|.
+% \begin{macrocode}
+ ##1%
+ \ekv@mark
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@set@eq@other@a, \ekv@set@eq@other@b}
+% The first of these two macros runs the split-test for equal signs of
+% category active. It will only be inserted if the \kv\ pair contained at
+% least one equal sign of category other and |##1| will contain everything up
+% to that equal sign.
+% \begin{macrocode}
+\long\def\ekv@set@eq@other@a##1\ekv@stop
+ {%
+ \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@set@eq@other@active
+ #2\ekv@mark\ekv@set@eq@other@b
+ }
+% \end{macrocode}
+% The second macro will have been called by |\ekv@eq@active| if no active
+% equal sign was found. All it does is remove the excess tokens of that
+% test and forward the \kv\ pair to |\ekv@set@pair|. Normally we would have to
+% also gobble an additional |\ekv@mark| after |\ekv@stop|, but this mark is
+% needed to delimit |\ekv@set@pair|'s argument anyway, so we just leave it
+% there.
+% \begin{macrocode}
+\ekv@exparg
+ {%
+ \long\def\ekv@set@eq@other@b
+ ##1\ekv@nil\ekv@mark\ekv@set@eq@other@active\ekv@stop\ekv@mark
+ ##2\ekv@nil=\ekv@mark\ekv@set@eq@active
+ }%
+ {\ekv@strip{##1}{\expandafter\ekv@set@pair\detokenize}\ekv@mark##2\ekv@nil}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@set@eq@other@active}
+% |\ekv@set@eq@other@active| will be called if the \kv\ pair was wrongly
+% split on an equal sign of category other but has an earlier equal sign of
+% category active. |##1| will be the contents up to the active equal sign and
+% |##2| everything that remains until the first found other equal sign. It has
+% to reinsert the equal sign and forward things to |\ekv@set@pair|.
+% \begin{macrocode}
+\ekv@exparg
+ {%
+ \long\def\ekv@set@eq@other@active
+ ##1\ekv@stop##2\ekv@nil#2\ekv@mark
+ \ekv@set@eq@other@b\ekv@mark##3=\ekv@mark\ekv@set@eq@active
+ }%
+ {\ekv@strip{##1}{\expandafter\ekv@set@pair\detokenize}\ekv@mark##2=##3}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@set@eq@active, \ekv@set@eq@active@}
+% |\ekv@set@eq@active| will be called when there was no equal sign of category
+% other in the \kv\ pair. It removes the excess tokens of the prior test and
+% split-checks for an active equal sign.
+% \begin{macrocode}
+\long\def\ekv@set@eq@active
+ ##1\ekv@nil\ekv@mark\ekv@set@eq@other@a\ekv@stop\ekv@mark
+ {%
+ \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@set@eq@active@
+ #2\ekv@mark\ekv@set@noeq
+ }
+% \end{macrocode}
+% If an active equal sign was found in |\ekv@set@eq@active| we'll have to pass
+% the now split \kv\ pair on to |\ekv@set@pair|.
+% \begin{macrocode}
+\ekv@exparg
+ {\long\def\ekv@set@eq@active@##1\ekv@stop##2\ekv@nil#2\ekv@mark\ekv@set@noeq}%
+ {\ekv@strip{##1}{\expandafter\ekv@set@pair\detokenize}\ekv@mark##2\ekv@nil}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@set@noeq}
+% If no active equal sign was found by |\ekv@set@eq@active| there is no equal
+% sign contained in the parsed list entry. In that case we have to check
+% whether the entry is blank in order to ignore it (in which case we'll have
+% to gobble the set-name which was put after these tests by |\ekv@set@other|).
+% Else this is a |NoVal| key and the entry is passed on to |\ekv@set@key|.
+% \begin{macrocode}
+\edef\ekv@set@noeq
+ {%
+ \ekv@unexpanded
+ {%
+ \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@set@was@blank
+ \ekv@ifempty@A\ekv@ifempty@B
+ }%
+ \ekv@unexpanded\expandafter
+ {\ekv@strip{##1}{\expandafter\ekv@set@key\detokenize}\ekv@mark}%
+ }
+\ekv@exparg
+ {%
+ \long\def\ekv@set@noeq
+ ##1\ekv@nil\ekv@mark\ekv@set@eq@active@\ekv@stop\ekv@mark
+ }%
+ {\ekv@set@noeq}
+\expandafter\def\expandafter\ekv@set@was@blank
+ \expandafter\ekv@ifempty@A\expandafter\ekv@ifempty@B
+ \ekv@strip{\ekv@mark##1}##2\ekv@mark
+ {\ekv@set@other}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@endset@other}
+% All that's left for |\ekv@set@other| is the macro which breaks the recursion
+% loop at the end. This is done by gobbling all the remaining tokens.
+% \begin{macrocode}
+\long\def\ekv@endset@other
+ \ekv@stop
+ \ekv@eq@other\ekv@mark\ekv@stop\ekv@nil\ekv@mark\ekv@set@eq@other@a
+ =\ekv@mark\ekv@set@eq@active
+ {\ekv@set}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak}
+% Provide macros that can completely stop the parsing of |\ekvset|, who knows
+% what it'll be useful for.
+% \begin{macrocode}
+\long\def\ekvbreak##1##2\ekv@stop#1##3{##1}
+\long\def\ekvbreakPreSneak ##1##2\ekv@stop#1##3{##1##3}
+\long\def\ekvbreakPostSneak##1##2\ekv@stop#1##3{##3##1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvsneak,\ekvsneakPre}
+% One last thing we want to do for |\ekvset| is to provide macros that just
+% smuggle stuff after |\ekvset|'s effects.
+% \begin{macrocode}
+\long\def\ekvsneak##1##2\ekv@stop#1##3{##2\ekv@stop#1{##3##1}}
+\long\def\ekvsneakPre##1##2\ekv@stop#1##3{##2\ekv@stop#1{##1##3}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvparse}
+% Additionally to the |\ekvset| macro we also want to provide an |\ekvparse|
+% macro, that has the same scope as |\keyval_parse:NNn| from \pkg{expl3}.
+% This is pretty analogue to the |\ekvset| implementation, we just put an
+% |\unexpanded| here and there instead of other macros to stop the |\expanded|
+% on our output. The |\unexpanded\expanded{{...}}| ensures that the material
+% is in an alignment safe group at all time, and that it doesn't expand any
+% further in an |\edef| or |\expanded| context.
+% \begin{macrocode}
+\long\def\ekvparse##1##2##3%
+ {%
+ \ekv@unexpanded\ekv@expanded
+ {{\ekv@parse{##1}{##2}\ekv@mark##3#1\ekv@stop#1}}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse}
+% \begin{macrocode}
+\long\def\ekv@parse##1##2##3#1%
+ {%
+ \ekv@gobble@from@mark@to@stop##3\ekv@endparse\ekv@stop
+ \ekv@parse@other{##1}{##2}##3,\ekv@stop,%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@endparse}
+% \begin{macrocode}
+\long\def\ekv@endparse
+ \ekv@stop\ekv@parse@other##1\ekv@mark\ekv@stop,\ekv@stop,%
+ {}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@other}
+% \begin{macrocode}
+\long\def\ekv@parse@other##1##2##3,%
+ {%
+ \ekv@gobble@from@mark@to@stop##3\ekv@endparse@other\ekv@stop
+ \ekv@eq@other##3\ekv@nil\ekv@mark\ekv@parse@eq@other@a
+ =\ekv@mark\ekv@parse@eq@active
+ {##1}{##2}%
+ \ekv@mark
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@eq@other@a,\ekv@parse@eq@other@b}
+% \begin{macrocode}
+\long\def\ekv@parse@eq@other@a##1\ekv@stop
+ {%
+ \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@parse@eq@other@active
+ #2\ekv@mark\ekv@parse@eq@other@b
+ }
+\ekv@exparg
+ {%
+ \long\def\ekv@parse@eq@other@b
+ ##1\ekv@nil\ekv@mark\ekv@parse@eq@other@active\ekv@stop\ekv@mark
+ ##2\ekv@nil=\ekv@mark\ekv@parse@eq@active
+ }%
+ {\ekv@strip{##1}\ekv@parse@pair##2\ekv@nil}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@eq@other@active}
+% \begin{macrocode}
+\ekv@exparg
+ {%
+ \long\def\ekv@parse@eq@other@active
+ ##1\ekv@stop##2\ekv@nil#2\ekv@mark
+ \ekv@parse@eq@other@b\ekv@mark##3=\ekv@mark\ekv@parse@eq@active
+ }%
+ {\ekv@strip{##1}\ekv@parse@pair##2=##3}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@eq@active,\ekv@parse@eq@active@}
+% \begin{macrocode}
+\long\def\ekv@parse@eq@active
+ ##1\ekv@nil\ekv@mark\ekv@parse@eq@other@a\ekv@stop\ekv@mark
+ {%
+ \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@parse@eq@active@
+ #2\ekv@mark\ekv@parse@noeq
+ }
+\ekv@exparg
+ {\long\def\ekv@parse@eq@active@##1\ekv@stop##2#2\ekv@mark\ekv@parse@noeq}%
+ {\ekv@strip{##1}\ekv@parse@pair##2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@noeq}
+% \begin{macrocode}
+\edef\ekv@parse@noeq
+ {%
+ \ekv@unexpanded
+ {%
+ \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@parse@was@blank
+ \ekv@ifempty@A\ekv@ifempty@B
+ }%
+ \ekv@unexpanded\expandafter{\ekv@strip{##1}\ekv@parse@key}%
+ }
+\ekv@exparg
+ {%
+ \long\def\ekv@parse@noeq
+ ##1\ekv@nil\ekv@mark\ekv@parse@eq@active@\ekv@stop\ekv@mark
+ }%
+ {\ekv@parse@noeq}
+\expandafter\def\expandafter\ekv@parse@was@blank
+ \expandafter\ekv@ifempty@A\expandafter\ekv@ifempty@B
+ \ekv@strip{\ekv@mark##1}\ekv@parse@key
+ {\ekv@parse@other}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@endparse@other}
+% \begin{macrocode}
+\long\def\ekv@endparse@other
+ \ekv@stop
+ \ekv@eq@other\ekv@mark\ekv@stop\ekv@nil\ekv@mark\ekv@parse@eq@other@a
+ =\ekv@mark\ekv@parse@eq@active
+ {\ekv@parse}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@pair,\ekv@parse@pair@}
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@parse@pair##1##2\ekv@nil}%
+ {\ekv@strip{##2}\ekv@parse@pair@{##1}}
+\long\def\ekv@parse@pair@##1##2##3##4%
+ {%
+ \ekv@unexpanded{##4{##2}{##1}}%
+ \ekv@parse@other{##3}{##4}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@key}
+% \begin{macrocode}
+\long\def\ekv@parse@key##1##2%
+ {%
+ \ekv@unexpanded{##2{##1}}%
+ \ekv@parse@other{##2}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% Finally really setting things up with |\ekvset|'s temporary meaning:
+% \begin{macrocode}
+}
+\catcode`\,=13
+\catcode`\==13
+\ekvset,=
+% \end{macrocode}
+%
+% \begin{macro}{\ekvsetSneaked}
+% This macro can be defined just by expanding |\ekvsneak| once after expanding
+% |\ekvset|. To expand everything as much as possible early on we use a
+% temporary definition.
+% \begin{macrocode}
+\edef\ekvsetSneaked
+ {%
+ \ekv@unexpanded{\ekvsneak{#2}}%
+ \ekv@unexpanded\expandafter{\ekvset{#1}{#3}}%
+ }
+\ekv@expargtwice{\long\def\ekvsetSneaked#1#2#3}{\ekvsetSneaked}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvchangeset}
+% Provide a macro that is able to switch out the current \set\ in |\ekvset|.
+% 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@exparg{\def\ekvchangeset#1}%
+ {%
+ \expandafter\expandafter\expandafter
+ \ekv@changeset\expandafter\csname\ekv@undefined@set{#1}\endcsname\ekv@empty
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@changeset}
+% This macro does the real change-out of |\ekvchangeset|. |#2| will have a
+% leading |\ekv@empty| so that braces aren't stripped accidentally, but that
+% will not hurt and just expand to nothing in one step.
+% \begin{macrocode}
+\long\def\ekv@changeset#1#2\ekv@set@other#3{#2\ekv@set@other#1}
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}[internal]
+% {
+% \ekv@set@pair,\ekv@set@pair@a,\ekv@set@pair@b,\ekv@set@pair@c,
+% \ekv@set@pair@d,\ekv@set@pair@e
+% }
+% |\ekv@set@pair| gets invoked with the space and brace stripped and
+% |\detokenize|d key-name as its first, the value as the second, and the set
+% name as the third argument. It provides tests for the key-macros and
+% everything to be able to throw meaningful error messages if it isn't
+% defined. We have two routes here, one if |\lastnamedcs| is defined and one
+% if it isn't. The big difference is that if it is we can omit a |\csname| and
+% instead just expand |\lastnamedcs| once to get the control sequence.
+% If the macro is defined the value will be space and brace stripped and the
+% key-macro called. Else branch into the error handling provided by
+% |\ekv@set@pair|.
+% \begin{macrocode}
+\ekv@if@lastnamedcs
+ {%
+ \long\def\ekv@set@pair#1\ekv@mark#2\ekv@nil#3%
+ {%
+ \ifcsname #3{#1}\endcsname\ekv@set@pair@a\fi\@secondoftwo
+ {#2}%
+ {%
+ \ifcsname #3{}u\endcsname\ekv@set@pair@a\fi\@secondoftwo
+ {#2}%
+ {%
+ \ekv@ifdefined{#3{#1}N}%
+ \ekv@err@noarg
+ \ekv@err@unknown
+ #3%
+ }%
+ {#1}%
+ }%
+ \ekv@set@other#3%
+ }
+ \def\ekv@set@pair@a\fi\@secondoftwo
+ {\fi\expandafter\ekv@set@pair@b\lastnamedcs}
+ }
+ {%
+ \long\def\ekv@set@pair#1\ekv@mark#2\ekv@nil#3%
+ {%
+ \ifcsname #3{#1}\endcsname
+ \ekv@set@pair@a\fi\ekv@set@pair@c#3{#1}\endcsname
+ {#2}%
+ {%
+ \ifcsname #3{}u\endcsname
+ \ekv@set@pair@a\fi\ekv@set@pair@c#3{}u\endcsname
+ {#2}%
+ {%
+ \ekv@ifdefined{#3{#1}N}%
+ \ekv@err@noarg
+ \ekv@err@unknown
+ #3%
+ }%
+ {#1}%
+ }%
+ \ekv@set@other#3%
+ }
+ \def\ekv@set@pair@a\fi\ekv@set@pair@c{\fi\expandafter\ekv@set@pair@b\csname}
+ \long\def\ekv@set@pair@c#1\endcsname#2#3{#3}
+ }
+\long\def\ekv@set@pair@b#1%
+ {%
+ \ifx#1\relax
+ \ekv@set@pair@e
+ \fi
+ \ekv@set@pair@d#1%
+ }
+\ekv@exparg{\long\def\ekv@set@pair@d#1#2#3}{\ekv@strip{#2}#1}
+\long\def\ekv@set@pair@e\fi\ekv@set@pair@d#1#2#3{\fi#3}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {\ekv@set@key,\ekv@set@key@a,\ekv@set@key@b,\ekv@set@key@c}
+% Analogous to |\ekv@set@pair|, |\ekv@set@key| builds the |NoVal| key-macro and
+% provides an error-branch. |\ekv@set@key@| will test whether the key-macro is
+% defined and if so call it, else the errors are thrown.
+% \begin{macrocode}
+\ekv@if@lastnamedcs
+ {%
+ \long\def\ekv@set@key#1\ekv@mark#2%
+ {%
+ \ifcsname #2{#1}N\endcsname\ekv@set@key@a\fi\@firstofone
+ {%
+ \ifcsname #2{}uN\endcsname\ekv@set@key@a\fi\@firstofone
+ {%
+ \ekv@ifdefined{#2{#1}}%
+ \ekv@err@reqval
+ \ekv@err@unknown
+ #2%
+ }%
+ {#1}%
+ }%
+ \ekv@set@other#2%
+ }
+ \def\ekv@set@key@a\fi\@firstofone{\fi\expandafter\ekv@set@key@b\lastnamedcs}
+ }
+ {%
+ \long\def\ekv@set@key#1\ekv@mark#2%
+ {%
+ \ifcsname #2{#1}N\endcsname
+ \ekv@set@key@a\fi\ekv@set@key@c#2{#1}N\endcsname
+ {%
+ \ifcsname #2{}uN\endcsname
+ \ekv@set@key@a\fi\ekv@set@key@c#2{}uN\endcsname
+ {%
+ \ekv@ifdefined{#2{#1}}%
+ \ekv@err@reqval
+ \ekv@err@unknown
+ #2%
+ }%
+ {#1}%
+ }%
+ \ekv@set@other#2%
+ }
+ \def\ekv@set@key@a\fi\ekv@set@key@c{\fi\expandafter\ekv@set@key@b\csname}
+ \long\def\ekv@set@key@c#1N\endcsname#2{#2}
+ }
+\long\def\ekv@set@key@b#1%
+ {%
+ \ifx#1\relax
+ \ekv@fi@secondoftwo
+ \fi
+ \@firstoftwo#1%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvsetdef}
+% Provide a macro to define a shorthand to use |\ekvset| on a specified \set.
+% To gain the maximum speed |\ekvset| is expanded twice by
+% |\ekv@exparg| so that during runtime the macro storing the set name
+% is already built and one |\expandafter| doesn't have to be used.
+% \begin{macrocode}
+\ekv@expargtwice{\protected\def\ekvsetdef#1#2}%
+ {%
+ \romannumeral
+ \ekv@exparg{\ekv@zero\ekv@exparg{\long\def#1##1}}%
+ {\ekvset{#2}{##1}}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvsetSneakeddef,\ekvsetdefSneaked}
+% And do the same for |\ekvsetSneaked| in the two possible ways, with a fixed
+% sneaked argument and with a flexible one.
+% \begin{macrocode}
+\ekv@expargtwice{\protected\def\ekvsetSneakeddef#1#2}%
+ {%
+ \romannumeral
+ \ekv@exparg{\ekv@zero\ekv@exparg{\long\def#1##1##2}}%
+ {\ekvsetSneaked{#2}{##1}{##2}}%
+ }
+\ekv@expargtwice{\protected\def\ekvsetdefSneaked#1#2#3}%
+ {%
+ \romannumeral
+ \ekv@exparg{\ekv@zero\ekv@exparg{\long\def#1##1}}%
+ {\ekvsetSneaked{#2}{#3}{##1}}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@alignsafe,\ekv@endalignsafe}
+% These macros protect the usage of ampersands inside of alignment contexts.
+% \begin{macrocode}
+\begingroup
+\catcode`\^^@=2
+\@firstofone{\endgroup
+ \def\ekv@alignsafe{\romannumeral\iffalse{\fi`^^@ }
+}
+\def\ekv@endalignsafe{\ifnum`{=\ekv@zero}\fi}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvoptarg,\ekvoptargTF}
+% Provide macros to expandably collect an optional argument in brackets. The
+% macros here are pretty simple in nature compared to \pkg{xparse}'s
+% possibilities (they don't care for nested bracket levels).
+%
+% We start with a temporary definition to pre-expand |\ekv@alignsafe| (will be
+% |#1|) and |\ekv@endalignsafe| (will be |#2|). As |\ekv@alignsafe| starts with
+% a |\romannumeral| we use that to also control the number of steps needed
+% instead of adding another |\romannumeral|. For this we have to remove the
+% space token from the end of |\ekv@alignsafe|'s definition.
+% \begin{macrocode}
+\begingroup
+\def\ekvoptarg#1#2{%
+\endgroup
+% \end{macrocode}
+% The real definition starts an expansion context and afterwards grabs the
+% arguments. |#1| will be the next step, |#2| the default value, and |#3| might
+% be an opening bracket, or the mandatory argument. We check for the opening
+% bracket, if it is found grab the optional argument, else leave |#1{#2}| in the
+% input stream after ending the expansion context.
+% \begin{macrocode}
+\def\ekvoptarg{#1\ekv@optarg@a}
+\long\def\ekv@optarg@a##1##2##3%
+ {%
+ \ekv@optarg@if\ekv@mark##3\ekv@mark\ekv@optarg@b\ekv@mark[\ekv@mark
+ #2%
+ \@firstofone{ ##1}{##2}{##3}%
+ }%
+% \end{macrocode}
+% The other variant of this will do roughly the same. Here, |#1| will be the
+% next step if an optional argument is found, |#2| the next step else, and |#3|
+% might be the opening bracket or mandatory argument.
+% \begin{macrocode}
+\def\ekvoptargTF{#1\ekv@optargTF@a}
+\long\def\ekv@optargTF@a##1##2##3%
+ {%
+ \ekv@optarg@if\ekv@mark##3\ekv@mark\ekv@optargTF@b{##1}\ekv@mark[\ekv@mark
+ #2%
+ \@firstofone{ ##2}{##3}%
+ }
+% \end{macrocode}
+% The two macros to grab the optional argument have to remove the remainder of
+% the test and the wrong next step as well as grabbing the argument.
+% \begin{macrocode}
+\long\def\ekv@optarg@b\ekv@mark[\ekv@mark\ifnum`##1\fi\@firstofone##2##3##4##5]%
+ {#2##2{##5}}
+\long\def\ekv@optargTF@b
+ ##1\ekv@mark[\ekv@mark\ifnum`##2\fi\@firstofone##3##4##5]%
+ {#2 ##1{##5}}
+}
+% \end{macrocode}
+% Do the definitions and add the test macro. We use |\ekv@strip| to remove the
+% trailing space from the definition of |\ekv@alignsafe|.
+% \begin{macrocode}
+\ekv@exparg
+ {%
+ \expandafter\ekv@strip\expandafter
+ {\expandafter\ekv@mark\ekv@alignsafe}%
+ \ekvoptarg
+ }%
+ \ekv@endalignsafe
+\long\def\ekv@optarg@if#1\ekv@mark[\ekv@mark{}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekverr}
+% \begin{macro}[internal]{\ekv@err@collect,\ekv@err@cleanup}
+% Since |\ekvset| is fully expandable as long as the code of the keys is (which
+% is unlikely) we want to somehow throw expandable errors, in our case via
+% a runaway argument (to my knowledge the first version of this method was
+% implemented by Jean-François Burnol, many thanks to him). The first step is to
+% ensure that the second argument (which might contain user input) doesn't
+% contain tokens we use as delimiters (in this case |\par|), this will be done
+% by the front facing macro |\ekverr|. But first we set some other things up.
+%
+% We use a temporary definition for |\ekverr| to get multiple consecutive
+% spaces. Then we set up the macro that will collect the error and the macro
+% that will throw the error. The latter will have an unreasonable long name.
+% This way we can convey more information. Though the information in the macro
+% name is static and has to be somewhat general to fit every occurence. The
+% important bit is that the long named macro has a delimited argument and is
+% short which will throw the error at the |\par| at the end of
+% |\ekv@err@collect|. This macro has the drawback that it will only print nicely
+% if the |\newlinechar| is |^^J|.
+% \begin{macrocode}
+\def\ekv@err@cleanup\par{}
+\def\ekv@err@collect#1%
+ {%
+ \def\ekv@err@collect##1\par##2%
+ {%
+ \expandafter
+ \ekv@err@cleanup
+ #1! ##2 Error: ##1\par
+ }%
+ \def#1##1\thanks@jfbu{}%
+ }
+\def\ekverr{ }
+\expandafter\ekv@err@collect\csname <an-expandable-macro>^^J%
+ completed due to above exception. \ekverr If the error^^J%
+ summary is \ekverr not comprehensible \ekverr see the package^^J%
+ documentation.^^J%
+ I will try to recover now. \ekverr If you're in inter-^^J%
+ active mode hit <return> \ekverr at the ? prompt and I^^J%
+ continue hoping recovery\endcsname
+% \end{macrocode}
+% \begin{macrocode}
+\long\def\ekverr#1#2{\expandafter\ekv@err@collect\detokenize{#2}\par{#1}}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@err}
+% We define a shorthand to throw errors in \expkv.
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@err#1}{\ekverr{expkv}{#1}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {
+% \ekv@err@common,\ekv@err@common@,
+% \ekv@err@unknown,\ekv@err@noarg,\ekv@err@reqval
+% }
+% Now we can use |\ekv@err| to set up some error messages so that we can later
+% use those instead of the full strings.
+% \begin{macrocode}
+\long\def\ekv@err@common #1#2{\expandafter\ekv@err@common@\string#2{#1}}
+\ekv@exparg{\long\def\ekv@err@common@#1`#2' #3.#4#5}%
+ {\ekv@err{#4 `#5' in set `#2'}}
+\ekv@exparg{\long\def\ekv@err@unknown#1}{\ekv@err@common{unknown key}{#1}}
+\ekv@exparg{\long\def\ekv@err@noarg #1}
+ {\ekv@err@common{unwanted value for}{#1}}
+\ekv@exparg{\long\def\ekv@err@reqval #1}{\ekv@err@common{missing value for}{#1}}
+\ekv@exparg{\long\def\ekv@err@redirect@kv@notfound#1#2#3\ekv@stop}%
+ {\ekv@err{no key `#2' in sets #3}}
+\ekv@exparg{\def\ekv@err@redirect@k@notfound#1#2\ekv@stop}%
+ {\ekv@err{no NoVal key `#1' in sets #2}}
+% \end{macrocode}
+% \end{macro}
+%
+% Now everything that's left is to reset the category code of |@|.
+% \begin{macrocode}
+\catcode`\@=\ekv@tmp
+% \end{macrocode}
+%
+% \gobbledocstriptag
+%</tex>
+%^^A=<<
+%
+%^^A=<<
+%
+% \end{implementation}^^A=<<
+%
+% \clearpage
+% \PrintIndex
+%
+\endinput
+%
+^^A vim: ft=tex fdm=marker fmr=>>=,=<<