summaryrefslogtreecommitdiff
path: root/macros/generic/expkv-bundle/expkv.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/generic/expkv-bundle/expkv.dtx')
-rw-r--r--macros/generic/expkv-bundle/expkv.dtx2375
1 files changed, 2375 insertions, 0 deletions
diff --git a/macros/generic/expkv-bundle/expkv.dtx b/macros/generic/expkv-bundle/expkv.dtx
new file mode 100644
index 0000000000..1c883b4346
--- /dev/null
+++ b/macros/generic/expkv-bundle/expkv.dtx
@@ -0,0 +1,2375 @@
+% \iffalse meta-comment
+%
+% File: expkv.dtx Copyright (C) 2020-2023 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\expkvDocNoGenerate{}
+\input expkv-bundle.ins
+\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}}}
+\endbatchfile
+%</driver>^^A=<<
+% \fi
+%
+% \section{\expkv}
+%^^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. Additional
+% an |all| option is declared to load all the sub modules in one go.
+% \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]
+\let\ekv@tmp\@empty
+\DeclareOption{all}
+ {%
+ \def\ekv@tmp
+ {\RequirePackage{expkv-pop,expkv-cs,expkv-def,expkv-opt}}%
+ }
+\ProcessOptions\relax
+\ekv@tmp
+% \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{2.0}
+\def\ekvDate{2023-01-23}
+% \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,\@gobbletwo,\@gobblethree,
+% \@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\@gobbletwo#1#2{}
+\long\def\@gobblethree#1#2#3{}
+\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@,\ekv@ifblank@false,\ekv@ifblank@gobbletrue}
+% 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. But instead
+% of building atop |\ekv@ifempty| our version will utilise the fact that the
+% argument in none of our usages can contain |\ekv@stop| (due to the way we
+% check for end-of-list), and hence we can use an even faster check. This here
+% works due to the fact that \TeX\ will read at least one token (or a group) for
+% an undelimited parameter which might be the end marker of the delimited next
+% parameter. So if |#1| to |\ekv@ifblank| is indeed blank, |#1| of
+% |\ekv@ifblank@| will be the placed |\ekv@stop| and |#2| will be anything until
+% the next |\ekv@stop|. The wrapper |\ekv@ifblank| will not be used by \expkv\
+% for speed reasons but \expkvo\ uses it. Also, not only a |TF| variant is
+% provided but also a rather strange one that assumes that only the |F| branch
+% is provided and that this branch consists only of a single token.
+% \begin{macrocode}
+\long\def\ekv@ifblank#1%
+ {\ekv@ifblank@#1\ekv@stop\ekv@ifblank@false\ekv@stop\@firstoftwo}
+\long\def\ekv@ifblank@\ekv@mark#1#2\ekv@stop{}
+\long\def\ekv@ifblank@false\ekv@stop\@firstoftwo#1#2{#2}
+\def\ekv@ifblank@gobbletrue\ekv@stop#1{}
+% \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@strip@key,\ekv@strip@key@a,\ekv@strip@key@b,\ekv@strip@key@c}
+% Same idea and code as |\ekv@strip|, but the second argument to
+% |\ekv@strip@key| must be a single token and the result of the stripping will
+% be |\detokenize|d.
+% \begin{macrocode}
+\def\ekv@strip@key#1%
+ {%
+ \long\def\ekv@strip@key##1{\ekv@strip@key@a##1\ekv@nil\ekv@mark#1#1\ekv@nil}
+ \long\def\ekv@strip@key@a##1\ekv@mark#1{\ekv@strip@key@b##1\ekv@mark}
+ }
+\ekv@strip@key{ }
+\long\def\ekv@strip@key@b#1 \ekv@nil{\ekv@strip@key@c#1\ekv@nil}
+\long\def\ekv@strip@key@c\ekv@mark#1\ekv@nil\ekv@mark#2\ekv@nil#3%
+ {\expandafter#3\detokenize{#1}\ekv@mark{#1}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {
+% \ekv@exparg,\ekv@exparg@,
+% \ekv@expandedarg,\ekv@fexparg,\ekv@expandafter
+% \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. And while we're
+% at it, also define a helper for |f|-~and |e|-expansion as well.
+%
+% 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%
+ {\ekv@expanded{\ekv@unexpanded{#1}\expandafter}\expandafter{#2}}
+\long\def\ekv@expandafter#1{\ekv@expanded{\ekv@unexpanded{#1}\expandafter}}
+\long\def\ekv@exparg@#1#2{#2{#1}}%
+\long\def\ekv@expandedarg#1#2{\ekv@expanded{\ekv@unexpanded{#1}{#2}}}
+\long\def\ekv@fexparg#1#2%
+ {%
+ \ekv@expanded{\ekv@unexpanded{#1}\expandafter}\expandafter
+ {\romannumeral`\^^@#2}%
+ }
+\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@stop\ekv@ifblank@gobbletrue
+ \ekv@stop\ekv@csv@loop@blank
+ #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@stop\ekv@ifblank@gobbletrue
+ \ekv@stop\ekv@csv@loop@blank
+ #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
+ \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{}%
+ \ekv@errm{empty set name not allowed}%
+ }}%
+ {%
+ \ekv@unexpanded\expandafter{\ekv@ifempty{#2}}%
+ \ekv@unexpanded
+ {%
+ {%
+ \def\ekv@tmp{}%
+ \ekv@errm{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,\ekvletunknown,\ekvletunknownNoVal
+% }
+% 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#5#6%
+ {%
+ \protected\long\def\ekvdef##1##2##3%
+ {#1{\expandafter\def\csname#2\endcsname####1{##3}#4}}%
+ \protected\long\def\ekvdefNoVal##1##2##3%
+ {#1{\expandafter\def\csname#2N\endcsname{##3}#4}}%
+ \protected\long\def\ekvlet##1##2##3%
+ {#1{\expandafter\let\csname#2\endcsname##3#4}}%
+ \protected\long\def\ekvletNoVal##1##2##3%
+ {#1{\expandafter\let\csname#2N\endcsname##3#4}}%
+ \ekv@expargtwice{\protected\long\def\ekv@defunknown##1##2##3##4}%
+ {%
+ \romannumeral
+ \ekv@exparg
+ {\ekv@zero\ekv@checkvalid{##3}.}%
+ {%
+ \expandafter\expandafter\expandafter
+ \def\expandafter\csname\ekv@name{##3}{}u##1\endcsname##2{##4}%
+ #6%
+ }%
+ }%
+ \ekv@expargtwice{\protected\long\def\ekv@letunknown##1##2##3}%
+ {%
+ \romannumeral
+ \ekv@exparg
+ {\ekv@zero\ekv@checkvalid{##2}.}%
+ {%
+ \expandafter\expandafter\expandafter
+ \let\expandafter\csname\ekv@name{##2}{}u##1\endcsname##3%
+ #5%
+ }%
+ }%
+ \protected\long\def\ekvletkv##1##2##3##4%
+ {%
+ #1%
+ {%
+ \expandafter\let\csname#2\expandafter\endcsname
+ \csname#3\endcsname
+ #4%
+ }%
+ }%
+ \protected\long\def\ekvletkvNoVal##1##2##3##4%
+ {%
+ #1%
+ {%
+ \expandafter\let\csname#2N\expandafter\endcsname
+ \csname#3N\endcsname
+ #4%
+ }%
+ }%
+ }
+\edef\ekvdefNoVal
+ {%
+ {\ekv@unexpanded\expandafter{\ekv@checkvalid{#1}{#2}}}%
+ {\ekv@unexpanded\expandafter{\ekv@name{#1}{#2}}}%
+ {\ekv@unexpanded\expandafter{\ekv@name{#3}{#4}}}%
+ {%
+ \ekv@unexpanded{\expandafter\ekv@defsetmacro\csname}%
+ \ekv@unexpanded\expandafter{\ekv@undefined@set{#1}\endcsname{#1}}%
+ }%
+ {%
+ \ekv@unexpanded{\expandafter\ekv@defsetmacro\csname}%
+ \ekv@unexpanded\expandafter{\ekv@undefined@set{#2}\endcsname{#2}}%
+ }%
+ {%
+ \ekv@unexpanded{\expandafter\ekv@defsetmacro\csname}%
+ \ekv@unexpanded\expandafter{\ekv@undefined@set{#3}\endcsname{#3}}%
+ }%
+ }
+\expandafter\ekvdef\ekvdefNoVal
+\ekv@exparg{\protected\long\def\ekvdefunknown#1#2}%
+ {\ekv@defunknown{}{##1##2##3}{#1}{#2}}
+\ekv@exparg{\protected\long\def\ekvdefunknownNoVal#1#2}%
+ {\ekv@defunknown{N}{##1##2}{#1}{#2}}
+\ekv@exparg{\protected\long\def\ekvletunknown#1#2}%
+ {\ekv@letunknown{}{#1}{#2}}
+\ekv@exparg{\protected\long\def\ekvletunknownNoVal#1#2}%
+ {\ekv@letunknown{N}{#1}{#2}}
+\let\ekv@defunknown\ekv@undefined
+\let\ekv@letunknown\ekv@undefined
+% \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\ekv@defredirectunknown#1#2#3#4#5#6%
+ {%
+ \begingroup
+ \edef\ekv@tmp
+ {%
+ \ekvcsvloop#1{#6}%
+ \ekv@unexpanded{#2}%
+ {\ekvcsvloop{}{#5,#6}}%
+ }%
+ \ekv@expargtwice
+ {\endgroup\long#3{#5}}%
+ {\expandafter#4\ekv@tmp\ekv@stop}%
+ }
+\ekv@exparg{\protected\def\ekvredirectunknown#1#2}%
+ {%
+ \ekv@defredirectunknown
+ \ekv@redirect@kv
+ \ekv@err@redirect@kv@notfound
+ \ekvdefunknown
+ \ekv@redirectunknown@aux
+ {#1}{#2}%
+ }
+\ekv@exparg{\protected\def\ekvredirectunknownNoVal#1#2}%
+ {%
+ \ekv@defredirectunknown
+ \ekv@redirect@k
+ \ekv@err@redirect@k@notfound
+ \ekvdefunknownNoVal
+ \ekv@redirectunknownNoVal@aux
+ {#1}{#2}%
+ }
+\def\ekv@redirectunknown@aux#1{#1{##1}{##2}}
+\def\ekv@redirectunknownNoVal@aux#1{#1{##1}}
+\let\ekv@defredirectunknown\ekv@undefined
+% \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}[internal]{\ekv@ifexp}
+% We want to be able to include expansion control. This is started by a
+% specific structure, namely if the stuff to the left of the equals sign
+% contains a colon followed by a space followed by at least one non-space
+% token. There is an additional rule namely the contents in front of the
+% \verb*|: | marker mustn't contain spaces outside of braces, but
+% unfortunately we can test this only after we did the real split and not as
+% part of a fast test. The temporary meaning we set up here will get
+% \tkn{:}{13}\vissp{ } and \tkn{:}{11}\vissp{ } as |#1| and |#2|.
+%
+% The real definition will take three arguments (two curried), namely the
+% un-|\detokenize|d name as |#1|, what should be done if the \expnotation\
+% should start as |#2|, and what should be done if no \expnotation\ is found
+% as |#3|.
+% \begin{macrocode}
+\def\ekv@ifexp#1#2%
+ {%
+% \end{macrocode}
+% In the following explanations I use \tkn{:}{12}, \tkn{:}{13}, and
+% \tkn{:}{11}, but I always mean that token followed by a space.
+% \begin{macro}[internal]{\ekv@ifexp@split@o@or@other}
+% \tkn{:}{12} splitter also used to test for \tkn{:}{13} and \tkn{:}{11}
+% \begin{macrocode}
+ \long\def\ekv@ifexp@split@o@or@other
+ ##1: ##2\ekv@stop##3%
+ ##4#1##5\ekv@stop##6%
+ ##7#2##8\ekv@stop##9%
+ {##9##6##3##1\ekv@stop##2}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]{\ekv@ifexp@split@a@or@l}
+% \tkn{:}{13} splitter also used to test for \tkn{:}{11}
+% \begin{macrocode}
+ \long\def\ekv@ifexp@split@a@or@l
+ ##1#1##2\ekv@stop##3%
+ ##4#2##5\ekv@stop##6%
+ {##6##3##1\ekv@stop##2}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]
+% {\ekv@ifexp@split@a,\ekv@ifexp@split@l}
+% \tkn{:}{13} splitter
+% \begin{macrocode}
+ \long\def\ekv@ifexp@split@a##1#1##2\ekv@stop##3%
+ {##3##1\ekv@stop##2}%
+% \end{macrocode}
+% \tkn{:}{11} splitter
+% \begin{macrocode}
+ \long\def\ekv@ifexp@split@l##1#2##2\ekv@stop##3%
+ {##3##1\ekv@stop##2}%
+% \end{macrocode}
+% \end{macro}
+% To get good performance in the most common case (standard category codes
+% apply) we try to split on \tkn{:}{12}, due to the way this split works there
+% are a few possible outcomes:
+% \begin{enumerate}
+% \item |\ekv@ifexp@none| is the first token if no colon was
+% found at all
+% \item |\ekv@ifexp@o| is the first token if only \tkn{:}{12}
+% were part of \key
+% \item |\ekv@ifexp@a| is put in front of either of the two
+% aforementioned cases if there was at least one \tkn{:}{13} (in that case
+% we don't yet know which was the first colon)
+% \item |\ekv@ifexp@o| is put in front of any of the
+% aforementioned cases if there was at least one \tkn{:}{11} (in which
+% case we also don't know yet which was the first colon)
+% \end{enumerate}
+% \begin{macrocode}
+ \long\def\ekv@ifexp##1%
+ {%
+ \ekv@ifexp@split@o@or@other
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@o
+ : \ekv@stop\ekv@ifexp@none
+ ##1\ekv@stop\ekv@ifexp@a#1\ekv@stop{}%
+ ##1\ekv@stop\ekv@ifexp@l#2\ekv@stop{}%
+ }%
+% \end{macrocode}
+% \begin{macro}[internal]
+% {\ekv@ifexp@o,\ekv@ifexp@none}
+% If any of the next two are called we already know the outcome and can
+% directly expand to it.
+% \begin{macrocode}
+ \long\def\ekv@ifexp@o##1\ekv@stop
+ {\ekv@ifexp@done{##1}\ekv@mark}%
+ \long\def\ekv@ifexp@none
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@o\ekv@stop
+ ##2##3%
+ {##3}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]
+% {\ekv@ifexp@a,\ekv@ifexp@a@i,\ekv@ifexp@a@ii}
+% If this is called we know that there was no \tkn{:}{13}, if the first token
+% now is |\ekv@ifexp@none| there was no \tkn{:}{12} as well
+% \begin{macrocode}
+ \def\ekv@ifexp@a##1%
+ {%
+ \ekv@ifexp@a@i
+ ##1\ekv@ifexp@a@ii
+ \ekv@ifexp@none\ekv@ifexp@a@or@o
+ }%
+ \def\ekv@ifexp@a@i##1\ekv@ifexp@none{}%
+% \end{macrocode}
+% Easy route, no \tkn{:}{12}, simply grab and pack
+% \begin{macrocode}
+ \long\def\ekv@ifexp@a@ii
+ \ekv@ifexp@none\ekv@ifexp@a@or@o
+ ##1#1##2\ekv@stop\ekv@ifexp@o\ekv@stop
+ {\ekv@ifexp@done{##1}\ekv@mark##2}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]
+% {
+% \ekv@ifexp@a@or@o,
+% \ekv@ifexp@a@not@o,\ekv@ifexp@a@not@o@,
+% \ekv@ifexp@o@not@a
+% }
+% other route, there is a \tkn{:}{12}, re-split at \tkn{:}{13}
+% \begin{macrocode}
+ \long\def\ekv@ifexp@a@or@o##1\ekv@stop
+ {%
+ \ekv@ifexp@split@a
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@a@not@o
+ #1\ekv@stop\ekv@ifexp@o@not@a
+ }%
+% \end{macrocode}
+% \tkn{:}{13} came earlier than \tkn{:}{12}
+% \begin{macrocode}
+ \long\def\ekv@ifexp@a@not@o##1\ekv@stop
+ {\ekv@ifexp@a@not@o@{##1}\ekv@mark}
+ \long\def\ekv@ifexp@a@not@o@
+ ##1##2\ekv@nil#1\ekv@stop\ekv@ifexp@o@not@a
+ {\ekv@ifexp@done{##1}##2: }%
+% \end{macrocode}
+% \tkn{:}{12} came earlier than \tkn{:}{13}
+% \begin{macrocode}
+ \long\def\ekv@ifexp@o@not@a
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@a@not@o\ekv@stop
+ {\ekv@ifexp@done{##1}\ekv@mark}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]{\ekv@ifexp@l,\ekv@ifexp@l@i,\ekv@ifexp@l@ii}
+% Now this is the most complicated case, if |\ekv@ifexp@l| is
+% called there are a few possibilities:
+% \begin{enumerate}
+% \item |#1| is |\ekv@ifexp@none|: easiest route, only
+% \tkn{:}{11} was there
+% \item |#1| is |\ekv@ifexp@o|: complicated route, there was a
+% \tkn{:}{11} and a \tkn{:}{12}
+% \item |#1| is |\ekv@ifexp@a| and after that comes
+% |\ekv@ifexp@none|: more complicated, there was a
+% \tkn{:}{11} and a \tkn{:}{13}
+% \item |#1| is |\ekv@ifexp@a| and after that comes
+% |\ekv@ifexp@o|: cursed route, there were \tkn{:}{11},
+% \tkn{:}{12}, and \tkn{:}{13}
+% \end{enumerate}
+% \begin{macrocode}
+ \def\ekv@ifexp@l##1%
+ {%
+ \ekv@ifexp@l@i
+ ##1\ekv@ifexp@l@ii
+ \ekv@ifexp@none{}%
+ ##1\ekv@ifexp@l@or@o
+ \ekv@ifexp@o
+ \ekv@ifexp@l@or@a
+ }%
+ \def\ekv@ifexp@l@i
+ ##1\ekv@ifexp@none##2%
+ ##3\ekv@ifexp@o
+ {##2}%
+% \end{macrocode}
+% easiest route, just \tkn{:}{11}
+% \begin{macrocode}
+ \long\def\ekv@ifexp@l@ii
+ \ekv@ifexp@l@or@a##1#2##2\ekv@stop
+ \ekv@ifexp@o\ekv@stop#2\ekv@stop##3%
+ {\ekv@ifexp@done{##1}\ekv@mark##2}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]
+% {
+% \ekv@ifexp@l@or@o,
+% \ekv@ifexp@l@not@o,\ekv@ifexp@l@not@o@,
+% \ekv@ifexp@o@not@l
+% }
+% slightly harder, \tkn{:}{11} or \tkn{:}{12}, re-split at \tkn{:}{11}
+% \begin{macrocode}
+ \long\def\ekv@ifexp@l@or@o
+ \ekv@ifexp@o\ekv@ifexp@l@or@a
+ ##1\ekv@stop
+ {%
+ \ekv@ifexp@split@l
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@l@not@o
+ #2\ekv@stop\ekv@ifexp@o@not@l
+ }%
+% \end{macrocode}
+% \tkn{:}{11} came earlier than \tkn{:}{12}
+% \begin{macrocode}
+ \long\def\ekv@ifexp@l@not@o##1\ekv@stop
+ {\ekv@ifexp@l@not@o@{##1}\ekv@mark}
+ \long\def\ekv@ifexp@l@not@o@
+ ##1##2\ekv@nil#2\ekv@stop\ekv@ifexp@o@not@l
+ ##3#2\ekv@stop##4%
+ {\ekv@ifexp@done{##1}##2: ##3}%
+% \end{macrocode}
+% \tkn{:}{12} came earlier than \tkn{:}{11}
+% \begin{macrocode}
+ \long\def\ekv@ifexp@o@not@l
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@l@not@o\ekv@stop
+ ##2#2\ekv@stop##3%
+ {\ekv@ifexp@done{##1}\ekv@mark##2}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]
+% {
+% \ekv@ifexp@l@or@a,
+% \ekv@ifexp@l@or@a@not@o,
+% \ekv@ifexp@l@not@a,\ekv@ifexp@l@not@a@,
+% \ekv@ifexp@a@not@l
+% }
+% \tkn{:}{13} was in the mix with \tkn{:}{11}, we need to see whether there
+% was a \tkn{:}{12} as well
+% \begin{macrocode}
+ \def\ekv@ifexp@l@or@a##1%
+ {%
+% \end{macrocode}
+% don't get confused by the name, this gobbles until |@none|
+% \begin{macrocode}
+ \ekv@ifexp@a@i
+ ##1\ekv@ifexp@l@or@a@not@o
+ \ekv@ifexp@none
+ \ekv@ifexp@l@or@a@or@o
+ }%
+% \end{macrocode}
+% phew, no \tkn{:}{12} in the mix, we split at \tkn{:}{13} and see whether an
+% earlier \tkn{:}{11} is found
+% \begin{macrocode}
+ \long\def\ekv@ifexp@l@or@a@not@o
+ \ekv@ifexp@none\ekv@ifexp@l@or@a@or@o
+ ##1#1%
+ {%
+ \ekv@ifexp@split@l
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@l@not@a
+ #2\ekv@stop\ekv@ifexp@a@not@l
+ }%
+ \long\def\ekv@ifexp@l@not@a##1\ekv@stop
+ {\ekv@ifexp@l@not@a@{##1}\ekv@mark}%
+ \long\def\ekv@ifexp@l@not@a@
+ ##1##2\ekv@nil#2\ekv@stop\ekv@ifexp@a@not@l
+ ##3\ekv@stop\ekv@ifexp@o\ekv@stop#2\ekv@stop##4%
+ {\ekv@ifexp@done{##1}##2#1##3}%
+ \long\def\ekv@ifexp@a@not@l
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@l@not@a\ekv@stop
+ ##2\ekv@stop\ekv@ifexp@o\ekv@stop#2\ekv@stop##3%
+ {\ekv@ifexp@done{##1}\ekv@mark##2}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]
+% {
+% \ekv@ifexp@l@or@a@or@o,\ekv@ifexp@l@or@a@or@o@i,\ekv@ifexp@l@or@a@or@o@ii,
+% \ekv@ifexp@l@or@a@or@o@iii,\ekv@ifexp@l@or@a@or@o@iv,
+% \ekv@ifexp@o@not@l@or@a,
+% \ekv@ifexp@a@not@l@or@o,\ekv@ifexp@a@not@l@or@o@
+% }
+% ooh no, the ugliest path, we got ourself a \tkn{:}{11}, a \tkn{:}{12}, and a
+% \tkn{:}{13}, first grab everything left after splitting the first
+% \tkn{:}{12} and try a \tkn{:}{13} split
+% \begin{macrocode}
+ \long\def\ekv@ifexp@l@or@a@or@o##1\ekv@stop
+ {%
+ \ekv@ifexp@split@a@or@l
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@a@not@l@or@o
+ #1\ekv@stop\ekv@ifexp@o@not@l@or@a
+ ##1\ekv@stop\ekv@ifexp@l@or@a@or@o@i#2\ekv@stop{}%
+ }%
+% \end{macrocode}
+% \tkn{:}{12} splitting was correct
+% \begin{macrocode}
+ \long\def\ekv@ifexp@o@not@l@or@a
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@a@not@l@or@o\ekv@stop
+ ##2#2\ekv@stop##3%
+ {\ekv@ifexp@done{##1}\ekv@mark##2}%
+% \end{macrocode}
+% \tkn{:}{13} split is correct, \tkn{:}{12} has to be reverted
+% \begin{macrocode}
+ \long\def\ekv@ifexp@a@not@l@or@o##1\ekv@stop
+ {\ekv@ifexp@a@not@l@or@o@{##1}\ekv@mark}
+ \long\def\ekv@ifexp@a@not@l@or@o@
+ ##1##2\ekv@nil##3#2\ekv@stop##4%
+ {\ekv@ifexp@done{##1}##2: ##3}%
+% \end{macrocode}
+% there is still a \tkn{:}{11} left, we need to take care of \tkn{:}{13} as
+% well, but \tkn{:}{12} was wrong
+% \begin{macrocode}
+ \def\ekv@ifexp@l@or@a@or@o@i##1%
+ {%
+ \ekv@ifexp@l@or@a@or@o@ii
+ ##1\ekv@ifexp@l@or@a@or@o@iii
+ \ekv@ifexp@o@not@l@or@a
+ \ekv@ifexp@why
+ }%
+ \def\ekv@ifexp@l@or@a@or@o@ii
+ ##1\ekv@ifexp@o@not@l@or@a
+ {}%
+% \end{macrocode}
+% at least it's not absolutely the worst, \tkn{:}{11} is correct, revert the
+% \tkn{:}{12} split
+% \begin{macrocode}
+ \long\def\ekv@ifexp@l@or@a@or@o@iii
+ \ekv@ifexp@o@not@l@or@a\ekv@ifexp@why
+ ##1#2%
+ {\ekv@ifexp@l@or@a@or@o@iv{##1}\ekv@mark}%
+ \long\def\ekv@ifexp@l@or@a@or@o@iv
+ ##1##2\ekv@nil\ekv@stop\ekv@ifexp@a@not@l@or@o\ekv@stop
+ #2\ekv@stop##3##4#2\ekv@stop##5%
+ {\ekv@ifexp@done{##1}##2: ##4}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]
+% {
+% \ekv@ifexp@why,
+% \ekv@ifexp@why@l,\ekv@ifexp@why@l@i,\ekv@ifexp@why@l@ii,
+% \ekv@ifexp@why@a
+% }
+% why have you forsaken me? We know \tkn{:}{12} was wrong, but now we have
+% something that might be correctly split at \tkn{:}{13}, but might as well be
+% not
+% \begin{macrocode}
+ \long\def\ekv@ifexp@why##1\ekv@stop
+ {%
+ \ekv@ifexp@split@l
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@why@l
+ #2\ekv@stop\ekv@ifexp@why@a
+ }%
+% \end{macrocode}
+% \tkn{:}{11} is correct, rebuild stuff undoing the wrong \tkn{:}{13} split
+% \begin{macrocode}
+ \long\def\ekv@ifexp@why@l##1\ekv@stop
+ {\ekv@ifexp@why@l@i{##1}\ekv@mark}
+ \long\def\ekv@ifexp@why@l@i
+ ##1##2\ekv@nil#2\ekv@stop\ekv@ifexp@why@a
+ {\ekv@ifexp@why@l@ii{##1}##2#1}
+% \end{macrocode}
+% and undoing the wrong \tkn{:}{12} split
+% \begin{macrocode}
+ \long\def\ekv@ifexp@why@l@ii
+ ##1##2\ekv@nil#2\ekv@stop##3##4#2\ekv@stop##5%
+ {\ekv@ifexp@done{##1}##2: ##4}%
+% \end{macrocode}
+% \tkn{:}{13} is correct, we know there is a \tkn{:}{11} hiding somewhere, so
+% we don't need to protect against brace strip using a two-step grab here,
+% just undoing the wrong \tkn{:}{12} split is enough
+% \begin{macrocode}
+ \long\def\ekv@ifexp@why@a
+ ##1\ekv@nil\ekv@stop\ekv@ifexp@why@l\ekv@stop
+ ##2\ekv@nil#2\ekv@stop##3##4#2\ekv@stop##5%
+ {\ekv@ifexp@done{##1}\ekv@mark##2: ##4}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]
+% {
+% \ekv@ifexp@done,\ekv@ifexp@done@,
+% \ekv@ifexp@done@test,
+% \ekv@ifexp@panic
+% }
+% once the colon split is done we need to check that there are no spaces in
+% the now split of part, if there are \emph{panic} (we did all of this for
+% nothing), else we're finally completely done with the splitting business.
+% \begin{macrocode}
+ \ekv@exparg{\long\def\ekv@ifexp@done##1##2\ekv@nil}%
+ {%
+ \@firstofone
+ {\ekv@ifexp@done@test##1\ekv@nil\ekv@ifexp@panic} \ekv@nil{}%
+ ##2\ekv@stop\ekv@ifexp@done@{##1}{##2}\ekv@stop\@secondoftwo
+ }
+ \long\def\ekv@ifexp@done@test##1 ##2\ekv@nil##3##4##5##6\ekv@stop{##3}
+ \ekv@exparg{\long\def\ekv@ifexp@done@##1##2\ekv@stop\@secondoftwo##3}%
+ {\ekv@strip{##2}{##3{##1}}}%
+% \end{macrocode}
+% Panic in this case means give up on expansion parsing, instead the normal
+% action that would've been done if the expansion mark isn't found is
+% executed.
+% \begin{macrocode}
+ \long\def\ekv@ifexp@panic##1\@secondoftwo##2##3{##3}
+ }
+% \end{macrocode}
+% \end{macro}
+% We set up the strange category codes for colons and do the real definition
+% after the temporary one.
+% \begin{macrocode}
+\begingroup
+ \catcode`\~=13
+ \lccode`\~=`\:
+ \catcode`\z=11
+ \lccode`\z=`\:
+\lowercase{\endgroup
+\ekv@ifexp{~ }{z }}
+% \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@or@active,\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@or@active| and |\ekv@eq@active| will split the argument at
+% the first equal sign. |\ekv@eq@active| will insert the macro which comes
+% after the first following |\ekv@mark|, while |\ekv@eq@other@or@active| will
+% also check for an active equals sign or none.
+% Usage should look like this:
+% \begin{syntaxexample*}
+% |\ekv@eq@other@or@active|
+% \indent|#1\ekv@nil\ekv@mark|\meta{code for \tkn{=}{12}}
+% \indent\tkn{=}{12}|\ekv@mark|\meta{code for no equals sign}
+% \indent|#1\ekv@mark|\meta{code for \tkn{=}{13} or mixed}
+% \indent\tkn{=}{13}|\ekv@mark{}|
+% |\ekv@eq@active|
+% \indent|#1\ekv@nil\ekv@mark|\meta{code for \tkn{=}{13}}
+% \indent\tkn{=}{13}|\ekv@mark|\meta{code for no \tkn{=}{13}}
+% \end{syntaxexample*}
+% To prevent accidental brace stripping |#1| should be handed in with a
+% leading |\ekv@mark|, also that's what the |\ekv@nil| after the first |#1| is
+% used for. In |\ekv@eq@other@or@active| four cases are distinguished:
+% \begin{enumerate}
+% \item
+% Only equals signs of category 12 are found, then \meta{code for
+% \tkn{=}{12}} will be inserted.
+% \item
+% No equals sign is found, then \meta{code for no equals sign} will be
+% inserted.
+% \item
+% Only equals signs of category 13 are found, then \meta{code for
+% \tkn{=}{13} or mixed} will be inserted, directly followed by
+% \meta{code for no equals sign}.
+% \item
+% Equals signs of categories 12 and 13 are found, then \meta{code for
+% \tkn{=}{13} or mixed} directly followed by \meta{code for \tkn{=}{12}}
+% will be inserted.
+% \end{enumerate}
+% 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@or@active
+ ##1=##2\ekv@mark##3%
+ ##4#2##5\ekv@mark##6%
+ {##6##3##1\ekv@stop\ekv@mark##2}
+\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,\ekv@set@next@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@or@active
+ ##2\ekv@nil\ekv@mark\ekv@set@eq@other
+ =\ekv@mark\ekv@set@noeq
+ ##2\ekv@mark\ekv@set@eq@active
+ #2\ekv@mark{}%
+% \end{macrocode}
+% And put the set name after the splitting.
+% \begin{macrocode}
+ ##1%
+ \ekv@mark
+ }
+% \end{macrocode}
+% The second macro is used as a shortcut to gobble two arguments and do the
+% same as a following |\ekv@set@other|.
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@set@next@other##1##2\ekv@set@other##3##4,}%
+ {\ekv@set@other{##3}{##4},}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@endset@other}
+% Breaking the current inner loop for \tkn{,}{12} is done by gobbling all the
+% remaining tokens and grabbing the next split at an active comma
+% (incorporating the next |\ekv@set| call).
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@endset@other
+ \ekv@stop\ekv@eq@other@or@active
+ \ekv@mark\ekv@stop\ekv@nil\ekv@mark\ekv@set@eq@other
+ =\ekv@mark\ekv@set@noeq
+ \ekv@mark\ekv@stop\ekv@mark\ekv@set@eq@active#2\ekv@mark##1%
+ ##2##3#1}%
+ {\ekv@set{##2}{##3}#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@set@eq@other}
+% If this gets called we know that there is no \tkn{=}{13}, hence are done
+% with the splitting business. This doesn't even need much clean up, we can
+% just strip the \key-name and forward it to |\ekv@set@pair|.
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@set@eq@other##1\ekv@stop}%
+ {\ekv@strip@key{##1}\ekv@set@pair}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@set@if@noeq}
+% In the case that there was at least one \tkn{=}{13} we need a way to know
+% whether there also was a \tkn{=}{12}. This here can be used to check this by
+% only looking for |\ekv@set@noeq|.
+% \begin{macrocode}
+\def\ekv@set@if@noeq##1\ekv@set@noeq{}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@set@eq@active,\ekv@set@eq@active@}
+% The first token after this will either be |\ekv@set@noeq| or
+% |\ekv@set@eq@other|. We test this and branch accordingly.
+% \begin{macrocode}
+\def\ekv@set@eq@active##1%
+ {\ekv@set@if@noeq##1\ekv@set@eq@active@\ekv@set@noeq\ekv@set@eq@mixed}
+% \end{macrocode}
+% Since we know that there was no \tkn{=}{12} when the next macro is called we
+% can simply do the correct split without any checks and forward to
+% |\ekv@set@pair|.
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@set@eq@active@
+ \ekv@set@noeq\ekv@set@eq@mixed
+ ##1#2##2\ekv@mark
+ \ekv@set@eq@other\ekv@stop\ekv@mark#2\ekv@mark##3}%
+ {\ekv@strip@key{##1}\ekv@set@pair\ekv@mark##2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {\ekv@set@eq@mixed,\ekv@set@eq@mixed@o,\ekv@set@mixed@a}
+% In the mixed case we grab until the first split of \tkn{=}{12} already done,
+% and resplit at \tkn{=}{13} using the conditonal splitter described earlier.
+% \begin{macrocode}
+\long\def\ekv@set@eq@mixed##1\ekv@stop
+ {%
+ \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@set@eq@mixed@a
+ #2\ekv@mark\ekv@set@eq@mixed@o
+ }
+% \end{macrocode}
+% If there actually was no \tkn{=}{13} before the first \tkn{=}{12} everything
+% was fine. We pick up our first split and remove the gibberish left by the
+% conditional splitter.
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@set@eq@mixed@o
+ ##1\ekv@nil\ekv@mark\ekv@set@eq@mixed@a\ekv@stop\ekv@mark
+ ##2\ekv@nil#2\ekv@mark##3}%
+ {\ekv@strip@key{##1}\ekv@set@pair##2\ekv@nil}
+% \end{macrocode}
+% |\ekv@set@eq@mixed@a| will trigger if before the first \tkn{=}{12} there was
+% already a \tkn{=}{13}. So we use the new split and revert the erroneous
+% split at \tkn{=}{12}.
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@set@eq@mixed@a
+ ##1\ekv@stop
+ ##2\ekv@nil#2\ekv@mark\ekv@set@eq@mixed@o\ekv@mark
+ ##3#2\ekv@mark##4}%
+ {\ekv@strip@key{##1}\ekv@set@pair##2=##3}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@set@noeq,\ekv@set@was@blank}
+% If there was no equals sign the current element might in fact be blank.
+% Blank elements are ignored, hence we need to check for that, else we can
+% call |\ekv@set@key|.
+% \begin{macrocode}
+\ekv@expandedarg
+ {%
+ \long\def\ekv@set@noeq
+ ##1\ekv@nil\ekv@mark\ekv@set@eq@other\ekv@stop\ekv@mark
+ }%
+ {%
+ \ekv@unexpanded
+ {%
+ \ekv@ifblank@##1\ekv@stop\ekv@ifblank@gobbletrue
+ \ekv@stop\ekv@set@was@blank
+ }%
+ \ekv@unexpanded\expandafter{\ekv@strip@key{##1}\ekv@set@key}%
+ }
+% \end{macrocode}
+% If in fact the element was blank we remove the |\ekv@set@key| call and
+% instead do the next iteration of |\ekv@set@other|.
+% \begin{macrocode}
+\ekv@exparg{\long\expandafter\def\expandafter\ekv@set@was@blank
+ \ekv@strip@key{\ekv@mark##1}\ekv@set@key
+ ##2##3,}%
+ {\ekv@set@other{##2}{##3},}
+% \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@or@active
+ ##3\ekv@nil\ekv@mark\ekv@parse@eq@other
+ =\ekv@mark\ekv@parse@noeq
+ ##3\ekv@mark\ekv@parse@eq@active#2\ekv@mark{}%
+ {##1}{##2}%
+ \ekv@mark
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@endparse@other}
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@endparse@other
+ \ekv@stop\ekv@eq@other@or@active
+ \ekv@mark\ekv@stop\ekv@nil\ekv@mark\ekv@parse@eq@other
+ =\ekv@mark\ekv@parse@noeq
+ \ekv@mark\ekv@stop\ekv@mark\ekv@parse@eq@active#2\ekv@mark##1%
+ ##2##3##4#1}%
+ {\ekv@parse{##2}{##3}{##4}#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@eq@other}
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@parse@eq@other##1\ekv@stop}%
+ {\ekv@strip{##1}\ekv@parse@pair}%
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@if@noeq}
+% \begin{macrocode}
+\def\ekv@parse@if@noeq##1\ekv@parse@noeq{}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@eq@active,\ekv@parse@eq@active@}
+% \begin{macrocode}
+\def\ekv@parse@eq@active##1%
+ {%
+ \ekv@parse@if@noeq##1\ekv@parse@eq@active@\ekv@parse@noeq
+ \ekv@parse@eq@mixed
+ }
+\ekv@exparg{\long\def\ekv@parse@eq@active@
+ \ekv@parse@noeq\ekv@parse@eq@mixed
+ ##1#2##2\ekv@mark
+ \ekv@parse@eq@other\ekv@stop\ekv@mark#2\ekv@mark##3}%
+ {\ekv@strip{##1}\ekv@parse@pair\ekv@mark##2}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {\ekv@parse@eq@mixed,\ekv@parse@eq@mixed@o,\ekv@parse@eq@mixed@a}
+% \begin{macrocode}
+\long\def\ekv@parse@eq@mixed##1\ekv@stop
+ {%
+ \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@parse@eq@mixed@a
+ #2\ekv@mark\ekv@parse@eq@mixed@o
+ }
+\ekv@exparg{\long\def\ekv@parse@eq@mixed@a
+ ##1\ekv@stop
+ ##2\ekv@nil#2\ekv@mark\ekv@parse@eq@mixed@o\ekv@mark
+ ##3#2\ekv@mark##4}%
+ {\ekv@strip{##1}\ekv@parse@pair##2=##3}
+\ekv@exparg{\long\def\ekv@parse@eq@mixed@o
+ ##1\ekv@nil\ekv@mark\ekv@parse@eq@mixed@a\ekv@stop\ekv@mark
+ ##2\ekv@nil#2\ekv@mark##3}%
+ {\ekv@strip{##1}\ekv@parse@pair##2\ekv@nil}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@noeq,\ekv@parse@was@blank}
+% \begin{macrocode}
+\ekv@expandedarg
+ {%
+ \long\def\ekv@parse@noeq
+ ##1\ekv@nil\ekv@mark\ekv@parse@eq@other\ekv@stop\ekv@mark
+ }%
+ {%
+ \ekv@unexpanded
+ {%
+ \ekv@ifblank@##1\ekv@stop\ekv@ifblank@gobbletrue
+ \ekv@stop\ekv@parse@was@blank
+ }%
+ \ekv@unexpanded\expandafter{\ekv@strip{##1}\ekv@parse@key}%
+ }
+\ekv@exparg{\long\expandafter\def\expandafter\ekv@parse@was@blank
+ \ekv@strip{\ekv@mark##1}\ekv@parse@key
+ ##2##3##4,}%
+ {\ekv@parse@other{##2}{##3}{##4},}
+% \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}}
+\ekv@exparg{\long\def\ekv@parse@pair@##1##2##3##4}%
+ {%
+ \ekv@ifexp{##2}%
+ {\ekv@expansion@parse@pair{##1}{##4}}%
+ {\ekv@unexpanded{##4{##2}{##1}}}%
+ \ekv@parse@other{##3}{##4}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@key}
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@parse@key##1##2}%
+ {%
+ \ekv@ifexp{##1}%
+ {\ekv@expansion@parse@key{##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}
+% \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}
+% \end{macro}
+%
+% \begin{macro}{\ekvmorekv}
+% |\ekvmorekv| injects additional keys for consideration without needing a
+% nested |\ekvset|. The issue we're facing here is that we can only inject
+% after the next |\ekv@set@other| to not destroy the order of things, but in
+% theory the argument to |\ekvmorekv| could contain an active comma. Now we
+% have two options:
+% \begin{enumerate}
+% \item reread all keys until the end of the current |\ekv@set@other| loop
+% to change the commas there to an active one and insert |\ekv@set|
+% instead
+% \item loop over the argument of |\ekvmorekv| to remove all top-level
+% active commas and replace them with ones of category 12.
+% \end{enumerate}
+% The following implements the second approach. We use a temporary definition
+% of |\ekvmorekv| to get us |#1| for active commas. For the comma replacement
+% we set up a small loop. The |\ekv@empty| at the end of |\ekvmorekv|'s
+% definition protects against accidental brace loss in user code.
+% \begin{macrocode}
+\def\ekvmorekv#1%
+ {%
+ \long\def\ekvmorekv##1%
+ {%
+ \expandafter\ekv@morekv\ekv@expanded
+ {{\ekv@morekv@loop\ekv@empty##1\ekv@stop\ekv@morekv@done#1}}%
+ \ekv@empty
+ }%
+% \end{macrocode}
+% \begin{macro}[internal]{\ekv@parse@more,\ekv@parse@more@}
+% For the expansion control we also need a way to support reinsertion in
+% |\ekvparse|. Since there no user code is executed this is an internal macro.
+% It works in the same way but the argument order exchange is different. Also,
+% since this is only used internally, we know it is safe to omit the trailing
+% |\ekv@empty|.
+% \begin{macrocode}
+ \long\def\ekv@parse@more##1\@gobble##2%
+ {%
+ \expandafter\ekv@parse@more@\ekv@expanded
+ {{\ekv@morekv@loop\ekv@empty##1\ekv@stop\ekv@morekv@done#1}}%
+ }%
+ \long\def\ekv@parse@more@##1\ekv@parse@other##2##3\ekv@mark
+ {\ekv@parse@other{##2}{##3}\ekv@mark##1,}%
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]{\ekv@morekv}
+% This is just a simple change of argument order, injecting the new comma
+% separated list after the current |\ekv@set@other| call.
+% \begin{macrocode}
+ \long\def\ekv@morekv##1##2\ekv@set@other##3\ekv@mark
+ {##2\ekv@set@other##3\ekv@mark##1,}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[internal]
+% {\ekv@morekv@loop,\ekv@morekv@done,\ekv@morekv@wrap}
+% And here is the heavy lifting of the comma replacement. Each list element
+% until the next active comma is read and left as an argument for
+% |\ekv@morekv@wrap|, that will remove the leading |\ekv@empty|. The trick for
+% a fast end of the loop is that |\ekv@morekv@wrap| will read until the next
+% |\ekv@stop|, of which an additional one is put before |\ekv@morekv@done| so
+% that |wrap| will wrap the real end of the list and then |done| removes the
+% excess code.
+% \begin{macrocode}
+ \long\def\ekv@morekv@loop##1#1%
+ {\ekv@morekv@wrap##1\ekv@stop,\ekv@morekv@loop\ekv@empty}%
+ \long\def\ekv@morekv@wrap##1\ekv@stop{\ekv@unexpanded\expandafter{##1}}%
+ \def\ekv@morekv@done\ekv@stop,\ekv@morekv@loop\ekv@empty{}%
+ }
+\begingroup\catcode`\,=13 \@firstofone{\endgroup\ekvmorekv,}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+%
+% \begin{macro}[internal]
+% {
+% \ekv@def@expansion@rule,\ekv@def@expansion@rule@ea,
+% \ekv@expansion@rule@,\ekv@expansion@csname
+% }
+% All expansion rules share the same basic structure, they do their expansion
+% step and then call |\ekv@expansion@rule@| (this could be done faster by
+% letting each rule build the macro for the next rule in |\csname|, but that
+% would require a more complex argument grabbing structure of the rules, and
+% we'd need to use |\expanded{\unexpanded{\csname ...\endcsname}\expandafter}|
+% or similar to not do the expansion steps of the \val\ inside of |\csname|).
+% As a result of this the \val\ needs to be grabbed twice per expansion step.
+% \begin{macrocode}
+\protected\def\ekv@def@expansion@rule#1#2#3#4%
+ {%
+ \long\expandafter\def\csname ekv@expansion@rule@#1\endcsname##1#2%
+ {#3\ekv@expansion@rule@#4}%
+ }
+\protected\def\ekv@def@expansion@rule@ea#1#2%
+ {\ekv@def@expansion@rule{#1}{#2}\expandafter}
+% \end{macrocode}
+% Rules follow this naming scheme. Note that for an undefined rule an error is
+% thrown and the result of the |\csname| is |\ekv@expansion@rule@|.
+% \begin{macrocode}
+\long\def\ekv@expansion@csname#1\ekv@stop
+ {%
+ ekv@expansion@rule@%
+ \ifcsname ekv@expansion@rule@#1\endcsname
+ #1%
+ \else
+ \ekv@err@undefined@expansion{#1}%
+ \fi
+ }
+% \end{macrocode}
+% This auxiliary macro simply builds the next rule, it is used to start an
+% expansion chain, as well as for each defined or undefined rule.
+% \begin{macrocode}
+\long\def\ekv@expansion@rule@#1#2%
+ {\csname\expandafter\ekv@expansion@csname\string#2\ekv@stop\endcsname{#1}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@expansion@rule@\ekv@mark}
+% The expansion chain is ended by an internal rule that behaves very different
+% from the others. It'll test whether the |r|-rule was used (in which case the
+% third argument will not be empty, and also execute that reinsertion) or grab
+% the code for the next step after the expansion.
+% \begin{macrocode}
+\ekv@exparg
+ {%
+ \long\expandafter
+ \def\csname ekv@expansion@rule@\string\ekv@mark\endcsname
+ #1\ekv@stop#2#3#4#5%
+ }%
+ {%
+ \romannumeral\ekv@exparg{\expandafter\ekv@zero\ekv@ifempty{#3}{#5{#2}{#1}}}%
+ {\ekv@ifempty{#2}{#4{#1}}{\ekv@err@val@no@reinsert{#2}}}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {
+% \ekv@expansion@rule@o,\ekv@expansion@rule@e,\ekv@expansion@rule@c,
+% \ekv@expansion@rule@f,\ekv@expansion@rule@V,\ekv@expansion@rule@v,
+% \ekv@expansion@rule@s,\ekv@expansion@rule@b,\ekv@expansion@rule@\r,
+% \ekv@expansion@rule@g,\ekv@expansion@rule@p,\ekv@expansion@rule@P,
+% \ekv@expansion@rule@r,\ekv@expansion@rule@R
+% }
+% All the expansion rules are executed inside a |\csname| construct, so they
+% have to issue an |\endcsname| after the intended expansion took place. I
+% guess the code is pretty straight forward here.
+% \begin{macrocode}
+\ekv@def@expansion@rule@ea{o}{}{\expandafter{#1}}
+\ekv@def@expansion@rule@ea{e}{}{\ekv@expanded{{#1}}}
+\ekv@def@expansion@rule@ea{c}{}{\csname#1\endcsname}
+\ekv@def@expansion@rule@ea{f}{}{\expandafter{\romannumeral`\^^@#1}}
+\ekv@exparg{\ekv@def@expansion@rule{s}{}}{\ekv@strip{\ekv@mark#1}}{}
+\ekv@def@expansion@rule{b}{}{}{{{#1}}}
+\ekv@def@expansion@rule{\string\r}{#2\ekv@stop#3#4}{}{{#1}#2\ekv@stop{#3}{#4r}}
+\ekv@def@expansion@rule@ea{g}{}{\expandafter{\@gobble#1}}
+\ekv@def@expansion@rule{p}{#2}{}{{#2#1}}
+\ekv@def@expansion@rule{P}{#2}{}{{#1#2}}
+% \end{macrocode}
+% The |V| and |v| rules are a bit more complicated. They use some auxiliaries
+% but otherwise should be more or less obvious as well. The |v| one uses some
+% expansion to get a more meaningful error message in case the resulting
+% control sequence would be undefined (without actually using |\csname|, to
+% not accidentally define anything as |\relax|).
+% \begin{macrocode}
+\long\def\ekv@expansion@rule@V#1%
+ {%
+ \ifx\relax#1\ekv@err@erroneous@variable{#1}\ekv@expansion@@clean@V\fi
+ \ekv@expansion@@V{#1}%
+ }
+\ekv@expandedarg{\long\def\ekv@expansion@rule@v#1}%
+ {%
+ \ekv@unexpanded{\ekv@ifdefined{#1}%
+ {\expandafter\ekv@expansion@@V\csname#1\endcsname}}%
+ {%
+ \ekv@unexpanded{\expandafter\ekv@err@erroneous@variable\ekv@expanded}%
+ {{\expandafter\@gobble\string\\#1}}%
+ \ekv@unexpanded{\ekv@expansion@rule@}{}%
+ }%
+ }
+\ekv@exparg{\long\def\ekv@expansion@rule@r#1#2\ekv@stop#3#4}%
+ {\ekv@expansion@rule@v{#1}#2\ekv@stop{#3}{#4r}}
+\ekv@exparg{\long\def\ekv@expansion@rule@R#1#2\ekv@stop#3#4}%
+ {\ekv@expansion@rule@V{#1}#2\ekv@stop{#3}{#4r}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@expansion@@V,\ekv@expansion@@clean@V}
+% The actual |V| evaluation is pretty much the same as in \pkg{expl3}, but we
+% need to put braces around the expansion result, hence branch slightly
+% differently. In general, a macro will temporarily have the meaning of
+% |\relax| if hit by |\noexpand| and hence the \cs[no-index]{ifx} test will
+% result in false (as the meaning isn't the same now), so we can tell
+% whether it would expand directly. Else we assume this is a register type and
+% use |\the|.
+% \begin{macrocode}
+\long\def\ekv@expansion@@V#1%
+ {%
+ \expandafter\ifx\noexpand#1#1%
+ \ekv@fi@firstoftwo
+ \fi
+ \@secondoftwo
+ {\expandafter\ekv@expansion@rule@\expandafter{\the#1}}%
+ {\expandafter\ekv@expansion@rule@\expandafter{#1}}%
+ }
+\long\def\ekv@expansion@@clean@V\fi\ekv@expansion@@V#1%
+ {\fi\ekv@expansion@rule@{}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {\ekv@expansion@rule@\key,\ekv@expansion@rule@\ekv@key@after}
+% The |\key|-rule works by calling a nested expansion chain with swapped \key\
+% and \val. Instead of |\ekv@mark| marking the end of the expansion chain
+% we use a different final step called |\ekv@key@after|, that simply checks
+% whether an |r|-rule was executed (\emph{error}) and ends the current
+% expansion step in the outer chain.
+% \begin{macrocode}
+\ekv@def@expansion@rule{\string\key}{#2#3\ekv@stop#4}%
+ {}{{#4}#2\ekv@key@after\ekv@stop{#1}{}{#3}}
+\ekv@exparg{\ekv@def@expansion@rule{\string\ekv@key@after}{\ekv@stop#2#3#4}}%
+ {\ekv@ifempty{#3}{}\ekv@err@key@no@reinsert}
+ {{#2}#4\ekv@stop{#1}}
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}[internal]
+% {
+% \ekv@set@pair,\ekv@set@pair@,
+% \ekv@set@pair@relax,\ekv@set@pair@undefined,
+% \ekv@set@pair@u,\ekv@set@pair@u@relax,\ekv@set@pair@no@u
+% }
+% |\ekv@set@pair| gets invoked with the space and brace stripped key-name as
+% its first, the value as the second (delimited by |\ekv@nil|), 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@undefined|.
+% \begin{macrocode}
+\ekv@if@lastnamedcs
+ {%
+ \long\def\ekv@set@pair#1\ekv@mark#2#3\ekv@nil#4%
+ {%
+ \ifcsname#4{#1}\endcsname\expandafter\ekv@set@pair@\lastnamedcs\fi
+ \ekv@set@pair@undefined{#3}{#1}{#2}%
+ \ekv@set@other#4%
+ }
+ \ekv@exparg{\long\def\ekv@set@pair@undefined#1#2#3\ekv@set@other#4}%
+ {%
+ \romannumeral
+ \ekv@exparg
+ {\expandafter\ekv@zero\ekv@ifexp{#3}}%
+ {\ekv@strip{#1}\ekv@expansion@set@pair}%
+ {%
+ \ifcsname#4{}u\endcsname\expandafter\ekv@set@pair@u\lastnamedcs\fi
+ \ekv@set@pair@no@u{#1}{#2}{#3}%
+ }%
+ \ekv@set@other#4%
+ }
+ }
+ {%
+ \long\def\ekv@set@pair#1\ekv@mark#2#3\ekv@nil#4%
+ {%
+ \ifcsname#4{#1}\endcsname
+ \expandafter\ekv@set@pair@\csname#4{#1}\endcsname
+ \fi
+ \ekv@set@pair@undefined{#3}{#1}{#2}%
+ \ekv@set@other#4%
+ }
+ \ekv@exparg{\long\def\ekv@set@pair@undefined#1#2#3\ekv@set@other#4}%
+ {%
+ \romannumeral
+ \ekv@exparg
+ {\expandafter\ekv@zero\ekv@ifexp{#3}}%
+ {\ekv@strip{#1}\ekv@expansion@set@pair}%
+ {%
+ \ifcsname#4{}u\endcsname
+ \expandafter\ekv@set@pair@u\csname#4{}u\endcsname
+ \fi
+ \ekv@set@pair@no@u{#1}{#2}{#3}%
+ }%
+ \ekv@set@other#4%
+ }
+ }
+\ekv@expandedarg{\long\def\ekv@set@pair@#1\fi\ekv@set@pair@undefined#2}%
+ {%
+ \ekv@unexpanded{\fi\ifx#1\relax\ekv@set@pair@relax\fi}%
+ \ekv@unexpanded\expandafter{\ekv@strip{#2}#1\ekv@set@next@other}%
+ }
+\ekv@expandafter{\long\def\ekv@set@pair@relax\fi}%
+ \ekv@strip{#1}#2\ekv@set@next@other
+ {\fi\ekv@set@pair@undefined{#1}}
+\ekv@expandedarg{\long\def\ekv@set@pair@u#1\fi\ekv@set@pair@no@u#2}%
+ {%
+ \ekv@unexpanded
+ {%
+ \fi
+ \ifx\relax#1%
+ \ekv@set@pair@u@relax
+ \fi
+ }%
+ \ekv@unexpanded\expandafter{\ekv@strip{#2}#1}%
+ }
+\ekv@expandafter{\long\def\ekv@set@pair@u@relax\fi}\ekv@strip{\ekv@mark#1}#2%
+ {\fi\ekv@set@pair@no@u.}
+\long\def\ekv@set@pair@no@u#1#2#3\ekv@set@other#4%
+ {%
+ \ekv@ifdefined{#4{#2}N}%
+ \ekv@err@noarg
+ \ekv@err@unknown
+ #4{#2}{#3}%
+ \ekv@set@other#4%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {
+% \ekv@set@key,\ekv@set@key@,
+% \ekv@set@key@relax,\ekv@set@key@undefined,
+% \ekv@set@key@u,\ekv@set@key@u@relax,\ekv@set@key@no@u
+% }
+% Analogous to |\ekv@set@pair|, |\ekv@set@key| builds the \Nkey-macro and
+% provides an error-branch. It'll test whether the key-macro is defined and if
+% so call it, else there might be some \expnotation, or errors are thrown.
+% \begin{macrocode}
+\ekv@if@lastnamedcs
+ {%
+ \long\def\ekv@set@key#1\ekv@mark#2#3%
+ {%
+ \ifcsname#3{#1}N\endcsname\expandafter\ekv@set@key@\lastnamedcs\fi
+ \ekv@set@key@undefined{#1}{#2}%
+ \ekv@set@other#3%
+ }
+ \ekv@exparg{\long\def\ekv@set@key@undefined#1#2\ekv@set@other#3}%
+ {%
+ \ekv@ifexp{#2}%
+ \ekv@expansion@set@key
+ {%
+ \ifcsname#3{}uN\endcsname\expandafter\ekv@set@key@u\lastnamedcs\fi
+ \ekv@set@key@no@u{#1}{#2}%
+ }%
+ \ekv@set@other#3%
+ }
+ }
+ {%
+ \long\def\ekv@set@key#1\ekv@mark#2#3%
+ {%
+ \ifcsname#3{#1}N\endcsname
+ \expandafter\ekv@set@key@\csname#3{#1}N\endcsname
+ \fi
+ \ekv@set@key@undefined{#1}{#2}%
+ \ekv@set@other#3%
+ }
+ \ekv@exparg{\long\def\ekv@set@key@undefined#1#2\ekv@set@other#3}%
+ {%
+ \ekv@ifexp{#2}%
+ \ekv@expansion@set@key
+ {%
+ \ifcsname#3{}uN\endcsname
+ \expandafter\ekv@set@key@u\csname#3{}uN\endcsname
+ \fi
+ \ekv@set@key@no@u{#1}{#2}%
+ }%
+ \ekv@set@other#3%
+ }
+ }
+\long\def\ekv@set@key@#1\fi\ekv@set@key@undefined
+ {\fi\ifx#1\relax\ekv@set@key@relax\fi#1\ekv@set@next@other}
+\long\def\ekv@set@key@relax\fi#1\ekv@set@next@other{\fi\ekv@set@key@undefined}
+\long\def\ekv@set@key@u#1\fi\ekv@set@key@no@u
+ {\fi\ifx\relax#1\ekv@set@key@u@relax\fi#1}
+\def\ekv@set@key@u@relax\fi#1{\fi\ekv@set@key@no@u}
+\long\def\ekv@set@key@no@u#1#2\ekv@set@other#3%
+ {%
+ \ekv@ifdefined{#3{#1}}%
+ \ekv@err@reqval
+ \ekv@err@unknown
+ #3{#1}{#2}%
+ \ekv@set@other#3%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {
+% \ekv@expansion@set@key,\ekv@expansion@set@key@,
+% \ekv@expansion@set@pair,\ekv@expansion@set@pair@
+% }
+% These macros just pick up the pieces of the \expansion\ prefix as separate
+% arguments. The \val\ for the \expansion-rules is left empty. Once expansion
+% is done we loop back to |\ekv@set@key|, that'll pick up the processed \key\
+% and set it if it's now defined. The same trick is used for a \kv\ pair.
+% \begin{macrocode}
+\long\def\ekv@expansion@set@key#1#2#3%
+ {%
+ \ekv@expansion@rule@{#2}#1\ekv@mark\ekv@stop{}{}\ekvmorekv
+ \ekv@expansion@set@key@
+ }
+\long\def\ekv@expansion@set@key@#1#2\ekv@set@other#3%
+ {\expandafter\ekv@set@key\detokenize{#2}\ekv@mark{#2}#3}
+\long\def\ekv@expansion@set@pair#1#2#3#4%
+ {%
+ \ekv@expansion@rule@{#1}#2\ekv@mark\ekv@stop{#3}{}\ekvmorekv
+ \ekv@expansion@set@pair@
+ }
+\long\def\ekv@expansion@set@pair@#1#2\ekv@set@other#3%
+ {\expandafter\ekv@set@pair\detokenize{#1}\ekv@mark{#1}\ekv@mark{#2}\ekv@nil#3}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {
+% \ekv@expansion@parse@key,\ekv@expansion@parse@key@,
+% \ekv@expansion@parse@pair,\ekv@expansion@parse@pair@
+% }
+% In |\ekvparse| we don't need to resort to looping back into |\ekv@parse@key|
+% or the like, we can simply leave the result via |\ekv@unexpanded|.
+% \begin{macrocode}
+\long\def\ekv@expansion@parse@key#1#2#3#4%
+ {%
+ \ekv@expansion@rule@{#3}#2\ekv@mark\ekv@stop{}{}\ekv@parse@more
+ \ekv@expansion@parse@key@\@gobble{#1}%
+ }
+\long\def\ekv@expansion@parse@key@#1#2\@gobble#3{\ekv@unexpanded{#3{#2}}}
+\long\def\ekv@expansion@parse@pair#1#2#3#4#5%
+ {%
+ \ekv@expansion@rule@{#1}#3\ekv@mark\ekv@stop{#4}{}\ekv@parse@more
+ \ekv@expansion@parse@pair@\@gobble{#2}%
+ }
+\long\def\ekv@expansion@parse@pair@#1#2\@gobble#3{\ekv@unexpanded{#3{#1}{#2}}}
+% \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{\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}[internal]{\ekv@ifstar}
+% Internal helper to get an expandable |ifstar| implementation. This is pretty
+% straight forward.
+% \begin{macrocode}
+\long\def\ekv@ifstar#1#2#3%
+ {%
+ \if*\string#3%
+ \ekv@fi@firstoftwo
+ \fi
+ \@secondoftwo
+ {#1}%
+ {#2{#3}}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvcompile}
+% The idea of key-compilation is to preparse some list into a form that sets
+% that list very fast (but is frozen), in our case this means we do everything
+% except expanding the key's code. There are two levels of expansion possible,
+% either expand until the key-macro or expand the key-macro once as well (the
+% latter might break if the key-macros take additional arguments). The
+% following supports both by using a starred form.
+% \begin{macrocode}
+\protected\long\def\ekvcompile
+ {%
+ \ekv@alignsafe
+ \ekv@ifstar{\ekv@compile{}}{\ekv@compile{\expandafter\expandafter}}%
+ }
+% \end{macrocode}
+% \begin{macro}[internal]
+% {\ekv@compile,\ekv@compile@,\ekv@compile@key,\ekv@compile@pair}
+% |\ekvparse| starts with an |\unexpanded| that we don't need. Hence we use a
+% temporary definition to remove that. Else this is pretty straight forward.
+% There is a minor indirection (the parsing step of |\ekv@compile|) which is
+% necessary to grab any argument specification.
+% \begin{macrocode}
+\def\ekv@compile#1#2#3#{\ekv@compile@{#1}{#2#3}}
+\def\ekv@compile@#1#2#3{#2#3}
+\ekv@exparg{\protected\long\def\ekv@compile@#1#2#3#4}%
+ {%
+ \expandafter\ekv@expanded\expandafter
+ {%
+ \romannumeral\ekv@expargtwice{\ekv@zero\def\ekv@unexpanded{#2}}%
+ {%
+ \expandafter\ekv@compile@
+ \ekvparse
+ {\ekv@compile@key{#1}{#3}}%
+ {\ekv@compile@pair{#1}{#3}}%
+ {#4}%
+ }%
+ }%
+ \ekv@endalignsafe
+ }
+% \end{macrocode}
+% The compilation step just checks whether the keys are defined and leaves
+% their code (maybe expanded once).
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@compile@key#1#2#3}%
+ {%
+ \ekvifdefinedNoVal{#2}{#3}%
+ {\ekv@unexpanded#1\expandafter{\csname\ekv@name{#2}{#3}N\endcsname}}%
+ {%
+ \ekv@ifdefined{\ekv@name{#2}{}uN}
+ {%
+ \ekv@unexpanded#1\expandafter
+ {%
+ \csname\ekv@name{#2}{}uN\expandafter\endcsname
+ \expandafter{\detokenize{#3}}{#3}%
+ }%
+ }%
+ {%
+ \ekvifdefined{#2}{#3}%
+ {\ekv@err{missing value for `#3' in set `#2'}}%
+ {\ekv@err{unknown key `#3' in set `#2'}}%
+ }%
+ }%
+ }
+\ekv@exparg{\long\def\ekv@compile@pair#1#2#3#4}%
+ {%
+ \ekvifdefined{#2}{#3}%
+ {\ekv@unexpanded#1\expandafter{\csname\ekv@name{#2}{#3}\endcsname{#4}}}%
+ {%
+ \ekv@ifdefined{\ekv@name{#2}{}u}%
+ {%
+ \ekv@unexpanded#1\expandafter
+ {%
+ \csname\ekv@name{#2}{}u\ekv@expanded{\endcsname
+ {\ekv@unexpanded{#4}}{\detokenize{#3}}}{#3}%
+ }%
+ }%
+ {%
+ \ekvifdefinedNoVal{#2}{#3}%
+ {\ekv@err{unwanted value for `#3' in set `#2'}}%
+ {\ekv@err{unknown key `#3' in set `#2'}}%
+ }%
+ }%
+ }
+% \end{macrocode}
+% \end{macro}
+% \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,\ekv@errm}
+% We define shorthands to throw errors in \expkv.
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekv@err#1}{\ekverr{expkv}{#1}}
+\protected\long\def\ekv@errm#1{\errmessage{expkv Error: #1}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {
+% \ekv@err@common,\ekv@err@common@,
+% \ekv@err@unknown,\ekv@err@noarg,\ekv@err@reqval,
+% \ekv@err@undefined@expansion
+% }
+% 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#2}{\ekv@err@common{unknown key}{#1}}
+\ekv@exparg{\long\def\ekv@err@noarg#1#2}
+ {\ekv@err@common{unwanted value for}{#1}}
+\ekv@exparg{\long\def\ekv@err@reqval#1#2}%
+ {\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}}
+\ekv@exparg{\def\ekv@err@undefined@expansion#1\fi}%
+ {\expandafter\fi\ekv@err{Undefined expansion rule `#1'}}%
+\ekv@exparg{\def\ekv@err@erroneous@variable#1}%
+ {\ekv@err{Erroneous variable `#1' used}}
+\ekv@exparg{\def\ekv@err@key@no@reinsert}%
+ {\ekv@err{Reinsertion in \key expansion forbidden}}
+\ekv@exparg{\def\ekv@err@val@no@reinsert#1}%
+ {\ekv@err{Reinsertion with key `#1' forbidden}}
+% \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=<<