From 594d9d83472299aca8bfbe9f123b418cbc40d4ca Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Sun, 11 Oct 2020 20:49:36 +0000 Subject: expkv (11oct20) git-svn-id: svn://tug.org/texlive/trunk@56634 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/latex/expkv/README.md | 2 +- Master/texmf-dist/doc/latex/expkv/expkv.pdf | Bin 397919 -> 410888 bytes Master/texmf-dist/source/latex/expkv/expkv.dtx | 961 +++++++++++++++---------- Master/texmf-dist/tex/generic/expkv/expkv.tex | 592 +++++++++------ 4 files changed, 953 insertions(+), 602 deletions(-) diff --git a/Master/texmf-dist/doc/latex/expkv/README.md b/Master/texmf-dist/doc/latex/expkv/README.md index c885f8a0d06..730b302b5a4 100644 --- a/Master/texmf-dist/doc/latex/expkv/README.md +++ b/Master/texmf-dist/doc/latex/expkv/README.md @@ -1,7 +1,7 @@ ------------------------------------------------------------------------------- # expkv -- an expandable key=val implementation -Version 2020-07-04 v1.4 +Version 2020-10-10 v1.5 Released under the LaTeX Project Public License v1.3c or later See http://www.latex-project.org/lppl.txt diff --git a/Master/texmf-dist/doc/latex/expkv/expkv.pdf b/Master/texmf-dist/doc/latex/expkv/expkv.pdf index 627341a78ff..4396ddfee2f 100644 Binary files a/Master/texmf-dist/doc/latex/expkv/expkv.pdf and b/Master/texmf-dist/doc/latex/expkv/expkv.pdf differ diff --git a/Master/texmf-dist/source/latex/expkv/expkv.dtx b/Master/texmf-dist/source/latex/expkv/expkv.dtx index f804e6ee0fe..3cd8479be56 100644 --- a/Master/texmf-dist/source/latex/expkv/expkv.dtx +++ b/Master/texmf-dist/source/latex/expkv/expkv.dtx @@ -70,6 +70,7 @@ and the derived files expkv.pdf \RequirePackage{array} \RequirePackage{collcell} \RequirePackage{siunitx} +\DeclareSIUnit\ops{ops} \RequirePackage{xcolor} \RequirePackage{caption} \RequirePackage{microtype} @@ -102,6 +103,19 @@ and the derived files expkv.pdf \definecolor{expkvred}{HTML}{9F393D} \colorlet{expkvgrey}{black!75} \makeatletter +\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 @@ -158,7 +172,7 @@ and the derived files expkv.pdf \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} +\DoNotIndex{\@,\unexpanded,\string,\expanded,\detokenize,\meaning,\lastnamedcs} \DoNotIndex{\ifcsname} \DoNotIndex{\ifx} \DoNotIndex{\else} @@ -182,7 +196,10 @@ and the derived files expkv.pdf {^^A \huge\expkv \\[\medskipamount] - \Large an expandable \kv\ implementation^^A + \Large an {\expFormat}andable + \meta{{\kvstyle k}\kern-.05em ey}=^^A + \meta{{\kvstyle v}\kern-.05em alue} + implementation^^A }{expkv - an expandable = implementation}^^A } \date{\ekvDate\space v\ekvVersion} @@ -198,10 +215,10 @@ and the derived files expkv.pdf % \begin{abstract} % \noindent\parfillskip=0pt % \expkv\ provides a small interface for \kv\ parsing. The parsing macro is -% fully expandable, the \meta{code} of your keys might be not. \expkv\ is pretty -% fast, but not the fastest available \kv\ solution (\pkg{keyval} is one and a -% half times as fast, but not expandable and it might strip braces it shouldn't -% have stripped). +% \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 @@ -259,7 +276,7 @@ and the derived files expkv.pdf % 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 -% \cs[no-index]{detokenize}d. +% |\detokenize|d. % % \begin{function}{\ekvdef} % \begin{syntax} @@ -297,7 +314,8 @@ and the derived files expkv.pdf % \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. +% whether that second key exists (but take a look at +% \cs[no-index]{ekvifdefined}). % \end{function} % % \begin{function}{\ekvletkvNoVal} @@ -305,7 +323,8 @@ and the derived files expkv.pdf % \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. +% whether that second key exists (but take a look at +% \cs[no-index]{ekvifdefinedNoVal}). % \end{function} % % \begin{function}{\ekvdefunknown} @@ -349,18 +368,59 @@ and the derived files expkv.pdf % |\ekvset| should be nestable. % \end{function} % +% \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} +% % \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 a bit faster than -% the idiomatic definition:\par +% 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} % +% \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} +% +% \begin{function}{\ekvsetdefSneaked} +% \begin{syntax} +% \cs{ekvsetSneakeddef}\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} +% % \begin{function}{\ekvparse} % \begin{syntax} % \cs{ekvparse}\meta{cs1}\meta{cs2}\{\kv,\ldots\} @@ -457,8 +517,9 @@ and the derived files expkv.pdf % \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. A small usage example is shown in -% \autoref{sec:sneakex}. +% 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} % % \begin{function}{\ekvchangeset} @@ -481,20 +542,17 @@ and the derived files expkv.pdf % \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 default definition of |\ekv@name@set| is -% ``\texttt{\csname ekv@name@set\endcsname{\set}}'' and the default of -% |\ekv@name@key| is ``\texttt{\csname ekv@name@key\endcsname{\key}}''. -% The complete name is build using |\ekv@name| which is equivalent to -% \texttt -% {^^A -% \cs[no-index]{ekv@name@set}\marg{set}\cs{ekv@name@key}^^A -% \{\cs[no-index]{detokenize}\marg{key}\}^^A -% }. +% 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 -% \cs[no-index]{\csname ekv@name\endcsname{\set}{\key}N}. You might redefine -% |\ekv@name@set| and |\ekv@name@key| locally but \emph{don't redefine} -% |\ekv@name|! +% \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} % % \subsubsection{Bugs} @@ -510,7 +568,8 @@ and the derived files expkv.pdf % \pkg{l3benchmark} package. The key and its usage should be equivalent to % \begin{lstlisting} % \protected\ekvdef{test}{height}{\def\myheight{#1}} -% \ekvset{test}{ height = 6 } +% \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 @@ -527,14 +586,17 @@ and the derived files expkv.pdf % 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). -% -% \paragraph{\pkg{keyval}} is about $1.6$ times faster and has a comparable -% feature set just a slightly different way how it handles keys without values. -% That might be considered a drawback, as it limits the versatility, but also as -% an advantage, as it might reduce doubled code. Keep in mind that as soon as -% someone loads \pkg{xkeyval} the performance of \pkg{keyval} gets replaced by -% \pkg{xkeyval}'s. +% |\keys_set:nn| of \pkg{expl3} (where the difference is much bigger). Comparing +% just the two, |\ekvparse| is a tad faster than |\keyval_parse:NNn| because of +% the two tests (for empty key names and only a single equal sign) which are +% omitted. +% +% \paragraph{\pkg{keyval}} is about \SIrange{30}{40}{\percent} faster and has a +% comparable feature set just a slightly different way how it handles keys +% without values. That might be considered a drawback, as it limits the +% versatility, but also as an advantage, as it might reduce doubled code. Keep +% in mind that as soon as someone loads \pkg{xkeyval} the performance of +% \pkg{keyval} gets replaced by \pkg{xkeyval}'s. % % Also \pkg{keyval} has a bug, which unfortunately can't really be resolved % without breaking backwards compatibility for \emph{many} documents, namely it @@ -545,49 +607,58 @@ and the derived files expkv.pdf % \begin{lstlisting}[belowskip=0pt] % \setkeys{foo}{bar=baz} % \setkeys{foo}{bar= {baz}} -% \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 seventeen 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 over 370 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 four 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 +% \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}). -% -% \paragraph{\pkg{l3keys}} is around six 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. Note -% that this comparison uses the version contained in \TeX Live 2019 (frozen) -% which is a bit slower than versions starting with \TeX Live 2020. -% -% \paragraph{\pkg{pgfkeys}} is around $2.7$ times slower for one key, -% but has an \emph{enormous} feature set. +% \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~$8$. Also I -% don't know whether this might introduce new bugs. +% 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 $3.7$ times +% \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 $1.5$ times slower for only a single value. It -% has a much bigger feature set. Unfortunately it also suffers from the +% \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 @@ -597,10 +668,11 @@ and the derived files expkv.pdf % 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 tokenized. Regarding unknown keys it got a +% 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. It is around $10\,\%$ faster than \expkv. +% stores |true| for that \key. For up to three keys, \expkv\ is a bit faster, +% for more keys \pkg{simplekv} takes the lead. % % \paragraph{\pkg{yax}} is over twenty times slower. It has a pretty strange % syntax, imho, and again a direct equivalent is hard to define. It has the @@ -640,16 +712,16 @@ and the derived files expkv.pdf % \toprule % \rmfamily Package & {$p_1$} & {$p_0$} & {$T_0$}& BB & CF & Date \\ % \midrule -% keyval & 13.694 & 1.504 & 7.008 & \yes & \yes & 2014-10-28 \\ -% simplekv & 18.739 & 5.325 & 17.68 & \yes & \yes & 2020-04-27 \\ -% \expkv & 22.007 & 3.100 & 10.095 & \no & \no & 2020-06-21 \\ -% options & 24.351 & 12.010 & 20.38 & \yes & \yes & 2015-03-01 \\ -% pgfkeys & 24.621 & 45.474 & 53.25 & \yes & \yes & 2020-06-17 \\ -% kvsetkeys & {\fnsym} & {\fnsym} & 40.0 & \no & \no & 2019-12-15 \\ -% l3keys & 92.103 & 32.724 & 38.05 & \no & \no & 2020-06-18 \\ -% xkeyval & 257.125 & 173.704 & 164.5 & \yes & \yes & 2014-12-03 \\ -% yax & 440.234 & 76.276 & 113.9 & \yes & \yes & 2010-01-22 \\ -% ltxkeys & 3448.339 & 4470.031 & 5241.0 & \no & \no & 2012-11-17 \\ +% 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 @@ -658,12 +730,12 @@ and the derived files expkv.pdf % \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.811}$, $p_1=\num{36.015}$, and $p_0=\num{81.792}$. Of course the +% $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{29.496}$, $p_1=\num{2828.933}$, and $p_0=\num{6741.189}$. +% $p_2=\num{23.500}$, $p_1=\num{2906.634}$, and $p_0=\num{6547.489}$. % \end{table} % % \subsection{Examples} @@ -672,7 +744,7 @@ and the derived files expkv.pdf % % 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 initialization. Now we want to be able to change that dimension +% 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 @@ -1010,8 +1082,8 @@ and the derived files expkv.pdf % \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.4} -\def\ekvDate{2020-07-04} +\def\ekvVersion{1.5} +\def\ekvDate{2020-10-10} % \end{macrocode} % \end{macro} % @@ -1034,6 +1106,34 @@ and the derived files expkv.pdf % 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, @@ -1112,28 +1212,89 @@ and the derived files expkv.pdf % % \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, slower than -% the typical expandable test for undefined control sequences, but faster for -% defined ones. Since we want to be as fast as possible for correct input, this -% is to be preferred. +% 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} -\def\ekv@ifdefined#1% +\ekv@if@lastnamedcs {% - \expandafter - \ifx\csname\ifcsname #1\endcsname #1\else relax\fi\endcsname\relax - \ekv@fi@secondoftwo - \fi - \@firstoftwo + \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 + } + } + {% + \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 + \ekv@stop + }% + \long\def\ekv@strip@a##1\ekv@mark#1{\ekv@strip@b##1\ekv@mark}% + \long\def\ekv@strip@b##1#1\ekv@nil {\ekv@strip@c##1\ekv@nil}% + } +\ekv@strip{ } +\long\def\ekv@strip@c\ekv@mark#1\ekv@nil\ekv@mark#2\ekv@nil\ekv@stop#3{#3{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@expB@unbraceA,\ekv@expB@unbraceA@} +% To reduce some code doublets while gaining some speed, it is often useful to +% expand the first token in a definition once. Let's define a wrapper for this. +% \begin{macrocode} +\long\def\ekv@expB@unbraceA#1#2% + {% + \expandafter\ekv@expB@unbraceA@\expandafter{#2}{#1}% } +\long\def\ekv@expB@unbraceA@#1#2{#2{#1}}% % \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#1#2{\ekv@name@set{#1}\ekv@name@key{\detokenize{#2}}} \def\ekv@name@set#1{ekv#1(} \def\ekv@name@key#1{#1)} +\edef\ekv@name + {% + \unexpanded\expandafter{\ekv@name@set{#1}}% + \unexpanded\expandafter{\ekv@name@key{\detokenize{#2}}}% + } +\ekv@expB@unbraceA{\def\ekv@name#1#2}{\ekv@name} % \end{macrocode} % \end{macro} % @@ -1158,31 +1319,38 @@ and the derived files expkv.pdf % package if an |\outer| has been gobbled this way. I consider that good, % because keys shouldn't be defined |\outer| anyways. % \begin{macrocode} -\protected\def\ekv@checkvalid#1#2% +\edef\ekv@checkvalid {% - \ekv@ifempty{#1}% - {% + \unexpanded\expandafter{\ekv@ifempty{#1}}% + \unexpanded + {{% \def\ekv@tmp{}% \errmessage{expkv Error: empty set name not allowed}% - }% + }}% {% - \ekv@ifempty{#2}% + \unexpanded\expandafter{\ekv@ifempty{#2}}% + \unexpanded {% - \def\ekv@tmp{}% - \errmessage{expkv Error: empty key name not allowed}% + {% + \def\ekv@tmp{}% + \errmessage{expkv Error: empty key name not allowed}% + }% + \@secondoftwo }% - \@secondoftwo }% - \@gobble + \unexpanded{\@gobble}% } +\ekv@expB@unbraceA{\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} -\def\ekvifdefined#1#2{\ekv@ifdefined{\ekv@name{#1}{#2}}} -\def\ekvifdefinedNoVal#1#2{\ekv@ifdefined{\ekv@name{#1}{#2}N}} +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\def\ekvifdefined#1#2}}% + {\expandafter\ekv@ifdefined\expandafter{\ekv@name{#1}{#2}}} +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\def\ekvifdefinedNoVal#1#2}}% + {\expandafter\ekv@ifdefined\expandafter{\ekv@name{#1}{#2}N}} % \end{macrocode} % \end{macro} % @@ -1191,74 +1359,71 @@ and the derived files expkv.pdf % \ekvdef,\ekvdefNoVal,\ekvlet,\ekvletNoVal,\ekvletkv,\ekvletkvNoVal, % \ekvdefunknown,\ekvdefunknownNoVal % } -% Set up the key defining macros |\ekvdef| etc. +% Set up the key defining macros |\ekvdef| etc. We use temporary macros to set +% these up with a few expansions already done. % \begin{macrocode} -\protected\long\def\ekvdef#1#2#3% +\def\ekvdef#1#2#3#4% {% - \ekv@checkvalid{#1}{#2}% + \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\def\ekvlet##1##2##3% + {#1{\expandafter\let\csname#2\endcsname##3#3}}% + \protected\def\ekvletNoVal##1##2##3% + {#1{\expandafter\let\csname#2N\endcsname##3#3}}% + \ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{% + \protected\long\def\ekvdefunknown##1##2}}}% {% - \expandafter\def\csname\ekv@name{#1}{#2}\endcsname##1{#3}% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% - }% - } -\protected\long\def\ekvdefNoVal#1#2#3% - {% - \ekv@checkvalid{#1}{#2}% - {% - \expandafter\def\csname\ekv@name{#1}{#2}N\endcsname{#3}% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% - }% - } -\protected\def\ekvlet#1#2#3% - {% - \ekv@checkvalid{#1}{#2}% - {% - \expandafter\let\csname\ekv@name{#1}{#2}\endcsname#3% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% - }% - } -\protected\def\ekvletNoVal#1#2#3% - {% - \ekv@checkvalid{#1}{#2}% - {% - \expandafter\let\csname\ekv@name{#1}{#2}N\endcsname#3% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% + \expandafter\ekv@expB@unbraceA@\expandafter + {% + \expandafter\expandafter\expandafter + \def\expandafter\csname\ekv@name{##1}{}u\endcsname####1####2{##2}% + #3% + }% + {\ekv@checkvalid{##1}.}% }% - } -\protected\def\ekvletkv#1#2#3#4% - {% - \ekv@checkvalid{#1}{#2}% + \ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{% + \protected\long\def\ekvdefunknownNoVal##1##2}}}% {% - \expandafter\let\csname\ekv@name{#1}{#2}\expandafter\endcsname - \csname\ekv@name{#3}{#4}\endcsname - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% + \expandafter\ekv@expB@unbraceA@\expandafter + {% + \expandafter\expandafter\expandafter + \def\expandafter\csname\ekv@name{##1}{}uN\endcsname####1{##2}% + #3% + }% + {\ekv@checkvalid{##1}.}% }% - } -\protected\def\ekvletkvNoVal#1#2#3#4% - {% - \ekv@checkvalid{#1}{#2}% + \protected\def\ekvletkv##1##2##3##4% {% - \expandafter\let\csname\ekv@name{#1}{#2}N\expandafter\endcsname - \csname\ekv@name{#3}{#4}N\endcsname - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% + #1% + {% + \expandafter\let\csname#2\expandafter\endcsname + \csname#4\endcsname + #3% + }% }% - } -\protected\def\ekvdefunknown#1#2% - {% - \ekv@checkvalid{#1}{.}% + \protected\def\ekvletkvNoVal##1##2##3##4% {% - \expandafter\def\csname\ekv@name{#1}{}u\endcsname##1##2{#2}% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% + #1% + {% + \expandafter\let\csname#2N\expandafter\endcsname + \csname#4N\endcsname + #3% + }% }% } -\protected\def\ekvdefunknownNoVal#1#2% +\edef\ekvdefNoVal {% - \ekv@checkvalid{#1}{.}% - {% - \expandafter\def\csname\ekv@name{#1}{}uN\endcsname##1{#2}% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% - }% - } + {\unexpanded\expandafter{\ekv@checkvalid{#1}{#2}}}% + {\unexpanded\expandafter{\ekv@name{#1}{#2}}}% + {% + \unexpanded{\expandafter\ekv@defsetmacro\csname}% + \unexpanded\expandafter{\ekv@undefined@set{#1}\endcsname{#1}}% + }% + {\unexpanded\expandafter{\ekv@name{#3}{#4}}}% + }% +\expandafter\ekvdef\ekvdefNoVal % \end{macrocode} % \end{macro} % @@ -1267,20 +1432,24 @@ and the derived files expkv.pdf % into a control sequence pretty early, so we have to define that control % sequence. % \begin{macrocode} -\protected\def\ekv@defsetmacro#1#2% +\edef\ekv@defsetmacro {% - \ifx#1\relax - \edef#1##1{\ekv@name@set{#2}\ekv@name@key{\noexpand\detokenize{##1}}}% - \fi + \unexpanded{\ifx#1\relax\edef#1##1}% + {% + \unexpanded\expandafter{\ekv@name@set{#2}}% + \unexpanded\expandafter{\ekv@name@key{##1}}% + }% + \unexpanded{\fi}% } +\ekv@expB@unbraceA{\protected\def\ekv@defsetmacro#1#2}{\ekv@defsetmacro} % \end{macrocode} % \end{macro} % % \begin{macro}{\ekvifdefinedset} % \begin{macrocode} -\def\ekvifdefinedset#1% +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\def\ekvifdefinedset#1}}% {% - \ekv@ifdefined{\ekv@undefined@set{#1}}% + \expandafter\ekv@ifdefined\expandafter{\ekv@undefined@set{#1}}% } % \end{macrocode} % \end{macro} @@ -1294,11 +1463,13 @@ and the derived files expkv.pdf % |#1| will be a \texttt{,\textsubscript{13}} and |#2| will be a % \texttt{=\textsubscript{13}}. % \begin{macrocode} +\begingroup \def\ekvset#1#2{% \endgroup -\long\def\ekvset##1##2% +\ekv@expB@unbraceA{\long\def\ekvset##1##2}% {% - \expandafter\ekv@set\csname\ekv@undefined@set{##1}\endcsname + \expandafter\expandafter\expandafter + \ekv@set\expandafter\csname\ekv@undefined@set{##1}\endcsname \ekv@mark##2#1\ekv@stop#1{}% } % \end{macrocode} @@ -1315,13 +1486,9 @@ and the derived files expkv.pdf % \begin{macrocode} \ekv@gobble@from@mark@to@stop##2\ekv@endset\ekv@stop % \end{macrocode} -% else go on with other commas, +% else go on with other commas. % \begin{macrocode} \ekv@set@other##1##2,\ekv@stop,% -% \end{macrocode} -% and get the next active comma delimited \kv\ pair. -% \begin{macrocode} - \ekv@set##1\ekv@mark } % \end{macrocode} % \end{macro} @@ -1331,9 +1498,8 @@ and the derived files expkv.pdf % of |\ekv@set| and unbrace the sneaked stuff. % \begin{macrocode} \long\def\ekv@endset - \ekv@stop\ekv@set@other##1,\ekv@stop,\ekv@set##2\ekv@mark - ##3% - {##3} + \ekv@stop\ekv@set@other##1\ekv@mark\ekv@stop,\ekv@stop,##2% + {##2} % \end{macrocode} % \end{macro} % @@ -1349,11 +1515,11 @@ and the derived files expkv.pdf % 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##4\ekv@stop +\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##4\ekv@stop +\long\def\ekv@eq@active##1#2##2\ekv@mark##3% {% ##3##1\ekv@stop\ekv@mark##2% } @@ -1373,12 +1539,12 @@ and the derived files expkv.pdf % 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\ekv@stop + =\ekv@mark\ekv@set@eq@active % \end{macrocode} -% And insert the set name and the next recursion step of |\ekv@set@other|. +% And insert the set name for the next recursion step of |\ekv@set@other|. % \begin{macrocode} ##1% - \ekv@set@other##1\ekv@mark + \ekv@mark } % \end{macrocode} % \end{macro} @@ -1391,39 +1557,41 @@ and the derived files expkv.pdf % \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@a - #2\ekv@mark\ekv@set@eq@other@b\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|. +% 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} -\long\def\ekv@set@eq@other@b - ##1\ekv@nil\ekv@mark\ekv@set@eq@other@active@a\ekv@stop\ekv@mark +\ekv@expB@unbraceA {% - \ekv@strip{##1}\ekv@set@pair - } + \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@a,\ekv@set@eq@other@active@b} -% |\ekv@set@eq@other@active@a| will be called if the \kv\ pair was wrongly +% \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 passes things on to -% |\ekv@set@eq@other@active@b| which calls |\ekv@set@pair| on the then -% correctly split \kv\ pair. +% to reinsert the equal sign and forward things to |\ekv@set@pair|. % \begin{macrocode} -\long\def\ekv@set@eq@other@active@a##1\ekv@stop##2\ekv@nil\ekv@mark +\ekv@expB@unbraceA {% - \ekv@set@eq@other@active@b{##1}##2=% - } -\long\def\ekv@set@eq@other@active@b##1% - {% - \ekv@strip{##1}\ekv@set@pair - } + \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} % @@ -1436,15 +1604,16 @@ and the derived files expkv.pdf ##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\ekv@stop + #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} -\long\def\ekv@set@eq@active@##1\ekv@stop +\ekv@expB@unbraceA + {\long\def\ekv@set@eq@active@##1\ekv@stop##2\ekv@nil#2\ekv@mark\ekv@set@noeq}% {% - \ekv@strip{##1}\ekv@set@pair + \ekv@strip{##1}{\expandafter\ekv@set@pair\detokenize}\ekv@mark##2\ekv@nil } % \end{macrocode} % \end{macro} @@ -1456,12 +1625,31 @@ and the derived files expkv.pdf % 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} -\long\def\ekv@set@noeq##1\ekv@nil\ekv@mark\ekv@set@eq@active@\ekv@stop\ekv@mark +\edef\ekv@set@noeq + {% + \unexpanded + {% + \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@set@was@blank + \ekv@ifempty@A\ekv@ifempty@B + }% + \unexpanded\expandafter + {\ekv@strip{##1}{\expandafter\ekv@set@key\detokenize}\ekv@mark}% + } +\ekv@expB@unbraceA + {% + \long\def\ekv@set@noeq + ##1\ekv@nil\ekv@mark\ekv@set@eq@active@\ekv@stop\ekv@mark + }% + {\ekv@set@noeq} +\def\ekv@set@was@blank##1% {% - \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@ifempty@true@F@gobble - \ekv@ifempty@A\ekv@ifempty@B\@firstofone - {\ekv@strip{##1}\ekv@set@key}% + \def\ekv@set@was@blank + \ekv@ifempty@A\ekv@ifempty@B + \ekv@strip@a\ekv@mark####1\ekv@nil\ekv@mark##1##1\ekv@nil\ekv@stop + ####2\ekv@mark + {\ekv@set@other}% } +\ekv@set@was@blank{ } % \end{macrocode} % \end{macro} % @@ -1471,11 +1659,9 @@ and the derived files expkv.pdf % \begin{macrocode} \long\def\ekv@endset@other \ekv@stop - \ekv@eq@other##1\ekv@nil\ekv@mark\ekv@set@eq@other@a - =\ekv@mark\ekv@set@eq@active\ekv@stop - ##2% - \ekv@set@other##3\ekv@mark - {} + \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} % @@ -1524,7 +1710,6 @@ and the derived files expkv.pdf {% \ekv@gobble@from@mark@to@stop##3\ekv@endparse\ekv@stop \ekv@parse@other##1##2##3,\ekv@stop,% - \ekv@parse##1##2\ekv@mark } % \end{macrocode} % \end{macro} @@ -1532,7 +1717,7 @@ and the derived files expkv.pdf % \begin{macro}[internal]{\ekv@endparse} % \begin{macrocode} \long\def\ekv@endparse - \ekv@stop\ekv@parse@other##1,\ekv@stop,\ekv@parse##2\ekv@mark + \ekv@stop\ekv@parse@other##1\ekv@mark\ekv@stop,\ekv@stop,% {} % \end{macrocode} % \end{macro} @@ -1543,9 +1728,9 @@ and the derived files expkv.pdf {% \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\ekv@stop + =\ekv@mark\ekv@parse@eq@active ##1##2% - \ekv@parse@other##1##2\ekv@mark + \ekv@mark } % \end{macrocode} % \end{macro} @@ -1554,28 +1739,28 @@ and the derived files expkv.pdf % \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@a - #2\ekv@mark\ekv@parse@eq@other@b\ekv@stop + \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@parse@eq@other@active + #2\ekv@mark\ekv@parse@eq@other@b } -\long\def\ekv@parse@eq@other@b - ##1\ekv@nil\ekv@mark\ekv@parse@eq@other@active@a\ekv@stop\ekv@mark +\ekv@expB@unbraceA {% - \ekv@strip{##1}\ekv@parse@pair - } + \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@a,\ekv@parse@eq@other@active@b} +% \begin{macro}[internal]{\ekv@parse@eq@other@active} % \begin{macrocode} -\long\def\ekv@parse@eq@other@active@a##1\ekv@stop##2\ekv@nil\ekv@mark - {% - \ekv@parse@eq@other@active@b{##1}##2=% - } -\long\def\ekv@parse@eq@other@active@b##1% +\ekv@expB@unbraceA {% - \ekv@strip{##1}\ekv@parse@pair - } + \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} % @@ -1585,24 +1770,42 @@ and the derived files expkv.pdf ##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@stop + #2\ekv@mark\ekv@parse@noeq } -\long\def\ekv@parse@eq@active@##1\ekv@stop +\ekv@expB@unbraceA + {\long\def\ekv@parse@eq@active@##1\ekv@stop##2#2\ekv@mark\ekv@parse@noeq}% {% - \ekv@strip{##1}\ekv@parse@pair + \ekv@strip{##1}\ekv@parse@pair##2% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\ekv@parse@noeq} % \begin{macrocode} -\long\def\ekv@parse@noeq - ##1\ekv@nil\ekv@mark\ekv@parse@eq@active@\ekv@stop\ekv@mark +\edef\ekv@parse@noeq + {% + \unexpanded + {% + \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@parse@was@blank + \ekv@ifempty@A\ekv@ifempty@B + }% + \unexpanded\expandafter{\ekv@strip{##1}\ekv@parse@key}% + } +\ekv@expB@unbraceA + {% + \long\def\ekv@parse@noeq + ##1\ekv@nil\ekv@mark\ekv@parse@eq@active@\ekv@stop\ekv@mark + }% + {\ekv@parse@noeq} +\def\ekv@parse@was@blank##1% {% - \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@ifempty@true@F@gobbletwo - \ekv@ifempty@A\ekv@ifempty@B\@firstofone - {\ekv@strip{##1}\ekv@parse@key}% + \def\ekv@parse@was@blank + \ekv@ifempty@A\ekv@ifempty@B + \ekv@strip@a\ekv@mark####1\ekv@nil\ekv@mark##1##1\ekv@nil\ekv@stop + \ekv@parse@key + {\ekv@parse@other}% } +\ekv@parse@was@blank{ } % \end{macrocode} % \end{macro} % @@ -1610,23 +1813,22 @@ and the derived files expkv.pdf % \begin{macrocode} \long\def\ekv@endparse@other \ekv@stop - \ekv@eq@other##1\ekv@nil\ekv@mark\ekv@parse@eq@other@a - =\ekv@mark\ekv@parse@eq@active\ekv@stop - ##2% - \ekv@parse@other##3\ekv@mark - {} + \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} -\long\def\ekv@parse@pair##1##2\ekv@nil +\ekv@expB@unbraceA{\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% {% \unexpanded{##4{##2}{##1}}% + \ekv@parse@other##3##4% } % \end{macrocode} % \end{macro} @@ -1636,6 +1838,7 @@ and the derived files expkv.pdf \long\def\ekv@parse@key##1##2##3% {% \unexpanded{##2{##1}}% + \ekv@parse@other##2##3% } % \end{macrocode} % \end{macro} @@ -1643,12 +1846,26 @@ and the derived files expkv.pdf % Finally really setting things up with |\ekvset|'s temporary meaning: % \begin{macrocode} } -\begingroup \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 + {% + \unexpanded{\ekvsneak{#2}}% + \unexpanded\expandafter{\ekvset{#1}{#3}}% + } +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\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 is slow (by comparison, it should be slightly faster than @@ -1658,111 +1875,147 @@ and the derived files expkv.pdf % switch out the set expandably, so this works similar to the |\ekvsneak| macros % reading and reinserting the remainder of the \kv\ list. % \begin{macrocode} -\def\ekvchangeset#1% +\ekv@expB@unbraceA{\def\ekvchangeset#1}% {% - \expandafter\ekv@changeset\csname\ekv@undefined@set{#1}\endcsname\ekv@mark + \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|. We introduced an -% |\ekv@mark| to not accidentally remove some braces which we have to remove -% again. +% 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#4\ekv@set#5% +\long\def\ekv@changeset#1#2\ekv@set@other#3% {% - \ekv@gobble@mark#2\ekv@set@other#1#4\ekv@set#1% + #2\ekv@set@other#1% } % \end{macrocode} % \end{macro} % % -% \begin{macro}[internal]{\ekv@set@pair} -% |\ekv@set@pair| gets invoked with the space and brace stripped key-name as -% its first argument, the value as the second argument, and the set name as -% the third argument. It builds the key-macro name and provides everything to -% be able to throw meaningful error messages if it isn't defined. -% |\ekv@set@pair@| will space and brace strip the value if the macro is -% defined and call the key-macro. Else it'll branch into the check whether an -% unknown key handler is defined for this set and that one will branch into -% the error messages provided by |\ekv@set@pair| if it isn't. +% \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} -\long\def\ekv@set@pair#1#2\ekv@nil#3% +\ekv@if@lastnamedcs {% - \expandafter\ekv@set@pair@ - \csname - \ifcsname #3{#1}\endcsname - #3{#1}% - \else - relax% - \fi - \endcsname - {#2}% + \long\def\ekv@set@pair#1\ekv@mark#2\ekv@nil#3% {% - \expandafter\ekv@set@pair@ - \csname - \ifcsname #3{}u\endcsname - #3{}u% - \else - relax% - \fi - \endcsname + \ifcsname #3{#1}\endcsname\ekv@set@pair@a\fi\@secondoftwo {#2}% {% - \ekv@ifdefined{#3{#1}N}% - \ekv@err@noarg - \ekv@err@unknown - #3% + \ifcsname #3{}u\endcsname\ekv@set@pair@a\fi\@secondoftwo + {#2}% + {% + \ekv@ifdefined{#3{#1}N}% + \ekv@err@noarg + \ekv@err@unknown + #3% + }% + {#1}% }% - {#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@#1#2% +\long\def\ekv@set@pair@b#1% {% \ifx#1\relax - \ekv@fi@secondoftwo + \ekv@set@pair@e \fi - \@firstoftwo - {\ekv@strip{#2}#1}% + \ekv@set@pair@d#1% } +\ekv@expB@unbraceA{\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} +% \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} -\long\def\ekv@set@key#1#2% +\ekv@if@lastnamedcs {% - \expandafter\ekv@set@key@ - \csname - \ifcsname #2{#1}N\endcsname - #2{#1}N% - \else - relax% - \fi - \endcsname + \long\def\ekv@set@key#1\ekv@mark#2% {% - \expandafter\ekv@set@key@ - \csname - \ifcsname #2{}uN\endcsname - #2{}uN% - \else - relax% - \fi - \endcsname + \ifcsname #2{#1}N\endcsname\ekv@set@key@a\fi\@firstofone {% - \ekv@ifdefined{#2{#1}}% - \ekv@err@reqval - \ekv@err@unknown - #2% + \ifcsname #2{}uN\endcsname\ekv@set@key@a\fi\@firstofone + {% + \ekv@ifdefined{#2{#1}}% + \ekv@err@reqval + \ekv@err@unknown + #2% + }% + {#1}% }% - {#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} } -\def\ekv@set@key@#1% +\long\def\ekv@set@key@b#1% {% \ifx#1\relax \ekv@fi@secondoftwo @@ -1774,24 +2027,34 @@ and the derived files expkv.pdf % % \begin{macro}{\ekvsetdef} % Provide a macro to define a shorthand to use |\ekvset| on a specified \set. -% The first macro expands |\ekvset| twice, such that everything which can be -% done up to this point is done. +% To gain the maximum speed |\ekvset| is expanded twice by +% |\ekv@expB@unbraceA| so that during runtime the macro storing the set name +% is already built and one |\expandafter| doesn't have to be used. % \begin{macrocode} -\protected\def\ekvsetdef#1#2% +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{% +\protected\def\ekvsetdef#1#2}}}}% {% - \expandafter\expandafter\expandafter - \ekv@setdef\expandafter\expandafter\expandafter{\ekvset{#2}{##1}}#1% + \ekv@expB@unbraceA{\ekv@expB@unbraceA{\long\def#1##1}}% + {\ekvset{#2}{##1}}% } % \end{macrocode} % \end{macro} % -% \begin{macro}[internal]{\ekv@setdef} -% This auxiliary macro defines the shorthand macro after |\ekvset| got -% expanded as far as possible. +% \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} -\protected\def\ekv@setdef#1#2% +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{% +\protected\def\ekvsetSneakeddef#1#2}}}}% + {% + \ekv@expB@unbraceA{\ekv@expB@unbraceA{\long\def#1##1##2}}% + {\ekvsetSneaked{#2}{##1}{##2}}% + } +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{% +\protected\def\ekvsetdefSneaked#1#2#3}}}}% {% - \long\def#2##1{#1}% + \ekv@expB@unbraceA{\ekv@expB@unbraceA{\long\def#1##1}}% + {\ekvsetSneaked{#2}{#3}{##1}}% } % \end{macrocode} % \end{macro} @@ -1827,44 +2090,12 @@ and the derived files expkv.pdf % \begin{macrocode} \long\def\ekv@err@common #1#2{\expandafter\ekv@err@common@\string#2{#1}} \long\def\ekv@err@common@#1`#2' #3.#4#5{\ekv@err{#4 (`#5', set `#2')}} -\long\def\ekv@err@unknown#1#2{\ekv@err@common{unknown key}#1{#2}} -\long\def\ekv@err@noarg #1#2{\ekv@err@common{value forbidden}#1{#2}} -\long\def\ekv@err@reqval #1#2{\ekv@err@common{value required}#1{#2}} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[internal]{\ekv@strip,\ekv@strip@a,\ekv@strip@b,\ekv@strip@c} -% Finally 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. -% \begin{macrocode} -\def\ekv@strip#1% - {% - \long\def\ekv@strip##1% - {% - \ekv@strip@a - ##1% - \ekv@nil - \ekv@mark#1% - #1\ekv@nil{}% - \ekv@stop - }% - \long\def\ekv@strip@a##1\ekv@mark#1##2\ekv@nil##3% - {% - \ekv@strip@b##3##1##2\ekv@nil - }% - \long\def\ekv@strip@b##1#1\ekv@nil - {% - \ekv@strip@c##1\ekv@nil - }% - \long\def\ekv@strip@c\ekv@mark##1\ekv@nil##2\ekv@stop##3% - {% - ##3{##1}% - }% - } -\ekv@strip{ } +\ekv@expB@unbraceA{\long\def\ekv@err@unknown#1}% + {\ekv@err@common{unknown key}{#1}} +\ekv@expB@unbraceA{\long\def\ekv@err@noarg #1}% + {\ekv@err@common{value forbidden}{#1}} +\ekv@expB@unbraceA{\long\def\ekv@err@reqval #1}% + {\ekv@err@common{value required}{#1}} % \end{macrocode} % \end{macro} % diff --git a/Master/texmf-dist/tex/generic/expkv/expkv.tex b/Master/texmf-dist/tex/generic/expkv/expkv.tex index 84898302138..5897ca8c924 100644 --- a/Master/texmf-dist/tex/generic/expkv/expkv.tex +++ b/Master/texmf-dist/tex/generic/expkv/expkv.tex @@ -39,11 +39,23 @@ \errmessage{expkv requires e-TeX} \expandafter\endinput \fi -\def\ekvVersion{1.4} -\def\ekvDate{2020-07-04} +\def\ekvVersion{1.5} +\def\ekvDate{2020-10-10} \csname ekv@tmp\endcsname \expandafter\chardef\csname ekv@tmp\endcsname=\catcode`\@ \catcode`\@=11 +\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 +\def\ekv@empty{} \long\def\@gobble#1{} \long\def\@firstofone#1{#1} \long\def\@firstoftwo#1#2{#1} @@ -74,135 +86,184 @@ \ekv@ifempty@A\ekv@ifempty@B\@secondoftwo } \long\def\ekv@ifblank@\ekv@mark#1{\ekv@ifempty@\ekv@ifempty@A} -\def\ekv@ifdefined#1% +\ekv@if@lastnamedcs {% - \expandafter - \ifx\csname\ifcsname #1\endcsname #1\else relax\fi\endcsname\relax - \ekv@fi@secondoftwo - \fi - \@firstoftwo + \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 + } } -\def\ekv@name#1#2{\ekv@name@set{#1}\ekv@name@key{\detokenize{#2}}} -\def\ekv@name@set#1{ekv#1(} -\def\ekv@name@key#1{#1)} -\def\ekv@undefined@set#1{! expkv Error: Set `#1' undefined.} -\protected\def\ekv@checkvalid#1#2% {% - \ekv@ifempty{#1}% - {% - \def\ekv@tmp{}% - \errmessage{expkv Error: empty set name not allowed}% - }% + \def\ekv@ifdefined#1% {% - \ekv@ifempty{#2}% - {% - \def\ekv@tmp{}% - \errmessage{expkv Error: empty key name not allowed}% - }% - \@secondoftwo - }% - \@gobble + \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} } -\def\ekvifdefined#1#2{\ekv@ifdefined{\ekv@name{#1}{#2}}} -\def\ekvifdefinedNoVal#1#2{\ekv@ifdefined{\ekv@name{#1}{#2}N}} -\protected\long\def\ekvdef#1#2#3% +\def\ekv@strip#1% {% - \ekv@checkvalid{#1}{#2}% + \long\def\ekv@strip##1% {% - \expandafter\def\csname\ekv@name{#1}{#2}\endcsname##1{#3}% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% + \ekv@strip@a + ##1\ekv@nil + \ekv@mark#1% + #1\ekv@nil + \ekv@stop }% + \long\def\ekv@strip@a##1\ekv@mark#1{\ekv@strip@b##1\ekv@mark}% + \long\def\ekv@strip@b##1#1\ekv@nil {\ekv@strip@c##1\ekv@nil}% } -\protected\long\def\ekvdefNoVal#1#2#3% +\ekv@strip{ } +\long\def\ekv@strip@c\ekv@mark#1\ekv@nil\ekv@mark#2\ekv@nil\ekv@stop#3{#3{#1}} +\long\def\ekv@expB@unbraceA#1#2% {% - \ekv@checkvalid{#1}{#2}% - {% - \expandafter\def\csname\ekv@name{#1}{#2}N\endcsname{#3}% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% - }% + \expandafter\ekv@expB@unbraceA@\expandafter{#2}{#1}% } -\protected\def\ekvlet#1#2#3% +\long\def\ekv@expB@unbraceA@#1#2{#2{#1}}% +\def\ekv@name@set#1{ekv#1(} +\def\ekv@name@key#1{#1)} +\edef\ekv@name {% - \ekv@checkvalid{#1}{#2}% - {% - \expandafter\let\csname\ekv@name{#1}{#2}\endcsname#3% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% - }% + \unexpanded\expandafter{\ekv@name@set{#1}}% + \unexpanded\expandafter{\ekv@name@key{\detokenize{#2}}}% } -\protected\def\ekvletNoVal#1#2#3% +\ekv@expB@unbraceA{\def\ekv@name#1#2}{\ekv@name} +\def\ekv@undefined@set#1{! expkv Error: Set `#1' undefined.} +\edef\ekv@checkvalid {% - \ekv@checkvalid{#1}{#2}% + \unexpanded\expandafter{\ekv@ifempty{#1}}% + \unexpanded + {{% + \def\ekv@tmp{}% + \errmessage{expkv Error: empty set name not allowed}% + }}% {% - \expandafter\let\csname\ekv@name{#1}{#2}N\endcsname#3% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% + \unexpanded\expandafter{\ekv@ifempty{#2}}% + \unexpanded + {% + {% + \def\ekv@tmp{}% + \errmessage{expkv Error: empty key name not allowed}% + }% + \@secondoftwo + }% }% - } -\protected\def\ekvletkv#1#2#3#4% - {% - \ekv@checkvalid{#1}{#2}% + \unexpanded{\@gobble}% + } +\ekv@expB@unbraceA{\protected\def\ekv@checkvalid#1#2}{\ekv@checkvalid}% +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\def\ekvifdefined#1#2}}% + {\expandafter\ekv@ifdefined\expandafter{\ekv@name{#1}{#2}}} +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\def\ekvifdefinedNoVal#1#2}}% + {\expandafter\ekv@ifdefined\expandafter{\ekv@name{#1}{#2}N}} +\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\def\ekvlet##1##2##3% + {#1{\expandafter\let\csname#2\endcsname##3#3}}% + \protected\def\ekvletNoVal##1##2##3% + {#1{\expandafter\let\csname#2N\endcsname##3#3}}% + \ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{% + \protected\long\def\ekvdefunknown##1##2}}}% {% - \expandafter\let\csname\ekv@name{#1}{#2}\expandafter\endcsname - \csname\ekv@name{#3}{#4}\endcsname - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% + \expandafter\ekv@expB@unbraceA@\expandafter + {% + \expandafter\expandafter\expandafter + \def\expandafter\csname\ekv@name{##1}{}u\endcsname####1####2{##2}% + #3% + }% + {\ekv@checkvalid{##1}.}% }% - } -\protected\def\ekvletkvNoVal#1#2#3#4% - {% - \ekv@checkvalid{#1}{#2}% + \ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{% + \protected\long\def\ekvdefunknownNoVal##1##2}}}% {% - \expandafter\let\csname\ekv@name{#1}{#2}N\expandafter\endcsname - \csname\ekv@name{#3}{#4}N\endcsname - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% + \expandafter\ekv@expB@unbraceA@\expandafter + {% + \expandafter\expandafter\expandafter + \def\expandafter\csname\ekv@name{##1}{}uN\endcsname####1{##2}% + #3% + }% + {\ekv@checkvalid{##1}.}% }% - } -\protected\def\ekvdefunknown#1#2% - {% - \ekv@checkvalid{#1}{.}% + \protected\def\ekvletkv##1##2##3##4% {% - \expandafter\def\csname\ekv@name{#1}{}u\endcsname##1##2{#2}% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% + #1% + {% + \expandafter\let\csname#2\expandafter\endcsname + \csname#4\endcsname + #3% + }% }% - } -\protected\def\ekvdefunknownNoVal#1#2% - {% - \ekv@checkvalid{#1}{.}% + \protected\def\ekvletkvNoVal##1##2##3##4% {% - \expandafter\def\csname\ekv@name{#1}{}uN\endcsname##1{#2}% - \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}% + #1% + {% + \expandafter\let\csname#2N\expandafter\endcsname + \csname#4N\endcsname + #3% + }% }% } -\protected\def\ekv@defsetmacro#1#2% +\edef\ekvdefNoVal {% - \ifx#1\relax - \edef#1##1{\ekv@name@set{#2}\ekv@name@key{\noexpand\detokenize{##1}}}% - \fi + {\unexpanded\expandafter{\ekv@checkvalid{#1}{#2}}}% + {\unexpanded\expandafter{\ekv@name{#1}{#2}}}% + {% + \unexpanded{\expandafter\ekv@defsetmacro\csname}% + \unexpanded\expandafter{\ekv@undefined@set{#1}\endcsname{#1}}% + }% + {\unexpanded\expandafter{\ekv@name{#3}{#4}}}% + }% +\expandafter\ekvdef\ekvdefNoVal +\edef\ekv@defsetmacro + {% + \unexpanded{\ifx#1\relax\edef#1##1}% + {% + \unexpanded\expandafter{\ekv@name@set{#2}}% + \unexpanded\expandafter{\ekv@name@key{##1}}% + }% + \unexpanded{\fi}% } -\def\ekvifdefinedset#1% +\ekv@expB@unbraceA{\protected\def\ekv@defsetmacro#1#2}{\ekv@defsetmacro} +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\def\ekvifdefinedset#1}}% {% - \ekv@ifdefined{\ekv@undefined@set{#1}}% + \expandafter\ekv@ifdefined\expandafter{\ekv@undefined@set{#1}}% } +\begingroup \def\ekvset#1#2{% \endgroup -\long\def\ekvset##1##2% +\ekv@expB@unbraceA{\long\def\ekvset##1##2}% {% - \expandafter\ekv@set\csname\ekv@undefined@set{##1}\endcsname + \expandafter\expandafter\expandafter + \ekv@set\expandafter\csname\ekv@undefined@set{##1}\endcsname \ekv@mark##2#1\ekv@stop#1{}% } \long\def\ekv@set##1##2#1% {% \ekv@gobble@from@mark@to@stop##2\ekv@endset\ekv@stop \ekv@set@other##1##2,\ekv@stop,% - \ekv@set##1\ekv@mark } \long\def\ekv@endset - \ekv@stop\ekv@set@other##1,\ekv@stop,\ekv@set##2\ekv@mark - ##3% - {##3} -\long\def\ekv@eq@other##1=##2\ekv@mark##3##4\ekv@stop + \ekv@stop\ekv@set@other##1\ekv@mark\ekv@stop,\ekv@stop,##2% + {##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##4\ekv@stop +\long\def\ekv@eq@active##1#2##2\ekv@mark##3% {% ##3##1\ekv@stop\ekv@mark##2% } @@ -210,51 +271,70 @@ {% \ekv@gobble@from@mark@to@stop##2\ekv@endset@other\ekv@stop \ekv@eq@other##2\ekv@nil\ekv@mark\ekv@set@eq@other@a - =\ekv@mark\ekv@set@eq@active\ekv@stop + =\ekv@mark\ekv@set@eq@active ##1% - \ekv@set@other##1\ekv@mark + \ekv@mark } \long\def\ekv@set@eq@other@a##1\ekv@stop {% - \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@set@eq@other@active@a - #2\ekv@mark\ekv@set@eq@other@b\ekv@stop + \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@set@eq@other@active + #2\ekv@mark\ekv@set@eq@other@b } -\long\def\ekv@set@eq@other@b - ##1\ekv@nil\ekv@mark\ekv@set@eq@other@active@a\ekv@stop\ekv@mark +\ekv@expB@unbraceA {% - \ekv@strip{##1}\ekv@set@pair - } -\long\def\ekv@set@eq@other@active@a##1\ekv@stop##2\ekv@nil\ekv@mark + \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} +\ekv@expB@unbraceA {% - \ekv@set@eq@other@active@b{##1}##2=% - } -\long\def\ekv@set@eq@other@active@b##1% - {% - \ekv@strip{##1}\ekv@set@pair - } + \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} \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\ekv@stop + #2\ekv@mark\ekv@set@noeq } -\long\def\ekv@set@eq@active@##1\ekv@stop +\ekv@expB@unbraceA + {\long\def\ekv@set@eq@active@##1\ekv@stop##2\ekv@nil#2\ekv@mark\ekv@set@noeq}% {% - \ekv@strip{##1}\ekv@set@pair + \ekv@strip{##1}{\expandafter\ekv@set@pair\detokenize}\ekv@mark##2\ekv@nil } -\long\def\ekv@set@noeq##1\ekv@nil\ekv@mark\ekv@set@eq@active@\ekv@stop\ekv@mark +\edef\ekv@set@noeq {% - \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@ifempty@true@F@gobble - \ekv@ifempty@A\ekv@ifempty@B\@firstofone - {\ekv@strip{##1}\ekv@set@key}% + \unexpanded + {% + \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@set@was@blank + \ekv@ifempty@A\ekv@ifempty@B + }% + \unexpanded\expandafter + {\ekv@strip{##1}{\expandafter\ekv@set@key\detokenize}\ekv@mark}% + } +\ekv@expB@unbraceA + {% + \long\def\ekv@set@noeq + ##1\ekv@nil\ekv@mark\ekv@set@eq@active@\ekv@stop\ekv@mark + }% + {\ekv@set@noeq} +\def\ekv@set@was@blank##1% + {% + \def\ekv@set@was@blank + \ekv@ifempty@A\ekv@ifempty@B + \ekv@strip@a\ekv@mark####1\ekv@nil\ekv@mark##1##1\ekv@nil\ekv@stop + ####2\ekv@mark + {\ekv@set@other}% } +\ekv@set@was@blank{ } \long\def\ekv@endset@other \ekv@stop - \ekv@eq@other##1\ekv@nil\ekv@mark\ekv@set@eq@other@a - =\ekv@mark\ekv@set@eq@active\ekv@stop - ##2% - \ekv@set@other##3\ekv@mark - {} + \ekv@eq@other\ekv@mark\ekv@stop\ekv@nil\ekv@mark\ekv@set@eq@other@a + =\ekv@mark\ekv@set@eq@active + {\ekv@set} \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} @@ -274,167 +354,229 @@ {% \ekv@gobble@from@mark@to@stop##3\ekv@endparse\ekv@stop \ekv@parse@other##1##2##3,\ekv@stop,% - \ekv@parse##1##2\ekv@mark } \long\def\ekv@endparse - \ekv@stop\ekv@parse@other##1,\ekv@stop,\ekv@parse##2\ekv@mark + \ekv@stop\ekv@parse@other##1\ekv@mark\ekv@stop,\ekv@stop,% {} \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\ekv@stop + =\ekv@mark\ekv@parse@eq@active ##1##2% - \ekv@parse@other##1##2\ekv@mark + \ekv@mark } \long\def\ekv@parse@eq@other@a##1\ekv@stop {% - \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@parse@eq@other@active@a - #2\ekv@mark\ekv@parse@eq@other@b\ekv@stop - } -\long\def\ekv@parse@eq@other@b - ##1\ekv@nil\ekv@mark\ekv@parse@eq@other@active@a\ekv@stop\ekv@mark - {% - \ekv@strip{##1}\ekv@parse@pair + \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@parse@eq@other@active + #2\ekv@mark\ekv@parse@eq@other@b } -\long\def\ekv@parse@eq@other@active@a##1\ekv@stop##2\ekv@nil\ekv@mark +\ekv@expB@unbraceA {% - \ekv@parse@eq@other@active@b{##1}##2=% - } -\long\def\ekv@parse@eq@other@active@b##1% + \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} +\ekv@expB@unbraceA {% - \ekv@strip{##1}\ekv@parse@pair - } + \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} \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@stop + #2\ekv@mark\ekv@parse@noeq + } +\ekv@expB@unbraceA + {\long\def\ekv@parse@eq@active@##1\ekv@stop##2#2\ekv@mark\ekv@parse@noeq}% + {% + \ekv@strip{##1}\ekv@parse@pair##2% } -\long\def\ekv@parse@eq@active@##1\ekv@stop +\edef\ekv@parse@noeq {% - \ekv@strip{##1}\ekv@parse@pair + \unexpanded + {% + \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@parse@was@blank + \ekv@ifempty@A\ekv@ifempty@B + }% + \unexpanded\expandafter{\ekv@strip{##1}\ekv@parse@key}% } -\long\def\ekv@parse@noeq - ##1\ekv@nil\ekv@mark\ekv@parse@eq@active@\ekv@stop\ekv@mark +\ekv@expB@unbraceA + {% + \long\def\ekv@parse@noeq + ##1\ekv@nil\ekv@mark\ekv@parse@eq@active@\ekv@stop\ekv@mark + }% + {\ekv@parse@noeq} +\def\ekv@parse@was@blank##1% {% - \ekv@ifblank@##1\ekv@nil\ekv@ifempty@B\ekv@ifempty@true@F@gobbletwo - \ekv@ifempty@A\ekv@ifempty@B\@firstofone - {\ekv@strip{##1}\ekv@parse@key}% + \def\ekv@parse@was@blank + \ekv@ifempty@A\ekv@ifempty@B + \ekv@strip@a\ekv@mark####1\ekv@nil\ekv@mark##1##1\ekv@nil\ekv@stop + \ekv@parse@key + {\ekv@parse@other}% } +\ekv@parse@was@blank{ } \long\def\ekv@endparse@other \ekv@stop - \ekv@eq@other##1\ekv@nil\ekv@mark\ekv@parse@eq@other@a - =\ekv@mark\ekv@parse@eq@active\ekv@stop - ##2% - \ekv@parse@other##3\ekv@mark - {} -\long\def\ekv@parse@pair##1##2\ekv@nil + \ekv@eq@other\ekv@mark\ekv@stop\ekv@nil\ekv@mark\ekv@parse@eq@other@a + =\ekv@mark\ekv@parse@eq@active + {\ekv@parse} +\ekv@expB@unbraceA{\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% {% \unexpanded{##4{##2}{##1}}% + \ekv@parse@other##3##4% } \long\def\ekv@parse@key##1##2##3% {% \unexpanded{##2{##1}}% + \ekv@parse@other##2##3% } } -\begingroup \catcode`\,=13 \catcode`\==13 \ekvset,= -\def\ekvchangeset#1% +\edef\ekvsetSneaked {% - \expandafter\ekv@changeset\csname\ekv@undefined@set{#1}\endcsname\ekv@mark + \unexpanded{\ekvsneak{#2}}% + \unexpanded\expandafter{\ekvset{#1}{#3}}% } -\long\def\ekv@changeset#1#2\ekv@set@other#3#4\ekv@set#5% +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\long\def\ekvsetSneaked#1#2#3}}% + {\ekvsetSneaked} +\ekv@expB@unbraceA{\def\ekvchangeset#1}% {% - \ekv@gobble@mark#2\ekv@set@other#1#4\ekv@set#1% + \expandafter\expandafter\expandafter + \ekv@changeset\expandafter\csname\ekv@undefined@set{#1}\endcsname\ekv@empty } -\long\def\ekv@set@pair#1#2\ekv@nil#3% +\long\def\ekv@changeset#1#2\ekv@set@other#3% {% - \expandafter\ekv@set@pair@ - \csname - \ifcsname #3{#1}\endcsname - #3{#1}% - \else - relax% - \fi - \endcsname - {#2}% + #2\ekv@set@other#1% + } +\ekv@if@lastnamedcs + {% + \long\def\ekv@set@pair#1\ekv@mark#2\ekv@nil#3% {% - \expandafter\ekv@set@pair@ - \csname - \ifcsname #3{}u\endcsname - #3{}u% - \else - relax% - \fi - \endcsname + \ifcsname #3{#1}\endcsname\ekv@set@pair@a\fi\@secondoftwo {#2}% {% - \ekv@ifdefined{#3{#1}N}% - \ekv@err@noarg - \ekv@err@unknown - #3% + \ifcsname #3{}u\endcsname\ekv@set@pair@a\fi\@secondoftwo + {#2}% + {% + \ekv@ifdefined{#3{#1}N}% + \ekv@err@noarg + \ekv@err@unknown + #3% + }% + {#1}% }% - {#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@#1#2% +\long\def\ekv@set@pair@b#1% {% \ifx#1\relax - \ekv@fi@secondoftwo + \ekv@set@pair@e \fi - \@firstoftwo - {\ekv@strip{#2}#1}% + \ekv@set@pair@d#1% } -\long\def\ekv@set@key#1#2% +\ekv@expB@unbraceA{\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} +\ekv@if@lastnamedcs {% - \expandafter\ekv@set@key@ - \csname - \ifcsname #2{#1}N\endcsname - #2{#1}N% - \else - relax% - \fi - \endcsname + \long\def\ekv@set@key#1\ekv@mark#2% {% - \expandafter\ekv@set@key@ - \csname - \ifcsname #2{}uN\endcsname - #2{}uN% - \else - relax% - \fi - \endcsname + \ifcsname #2{#1}N\endcsname\ekv@set@key@a\fi\@firstofone {% - \ekv@ifdefined{#2{#1}}% - \ekv@err@reqval - \ekv@err@unknown - #2% + \ifcsname #2{}uN\endcsname\ekv@set@key@a\fi\@firstofone + {% + \ekv@ifdefined{#2{#1}}% + \ekv@err@reqval + \ekv@err@unknown + #2% + }% + {#1}% }% - {#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} } -\def\ekv@set@key@#1% +\long\def\ekv@set@key@b#1% {% \ifx#1\relax \ekv@fi@secondoftwo \fi \@firstoftwo#1% } -\protected\def\ekvsetdef#1#2% +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{% +\protected\def\ekvsetdef#1#2}}}}% {% - \expandafter\expandafter\expandafter - \ekv@setdef\expandafter\expandafter\expandafter{\ekvset{#2}{##1}}#1% + \ekv@expB@unbraceA{\ekv@expB@unbraceA{\long\def#1##1}}% + {\ekvset{#2}{##1}}% + } +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{% +\protected\def\ekvsetSneakeddef#1#2}}}}% + {% + \ekv@expB@unbraceA{\ekv@expB@unbraceA{\long\def#1##1##2}}% + {\ekvsetSneaked{#2}{##1}{##2}}% } -\protected\def\ekv@setdef#1#2% +\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{\ekv@expB@unbraceA{% +\protected\def\ekvsetdefSneaked#1#2#3}}}}% {% - \long\def#2##1{#1}% + \ekv@expB@unbraceA{\ekv@expB@unbraceA{\long\def#1##1}}% + {\ekvsetSneaked{#2}{#3}{##1}}% } \begingroup \edef\ekv@err @@ -451,34 +593,12 @@ \def\ekv@err@{\expandafter\ekv@gobbleto@stop} \long\def\ekv@err@common #1#2{\expandafter\ekv@err@common@\string#2{#1}} \long\def\ekv@err@common@#1`#2' #3.#4#5{\ekv@err{#4 (`#5', set `#2')}} -\long\def\ekv@err@unknown#1#2{\ekv@err@common{unknown key}#1{#2}} -\long\def\ekv@err@noarg #1#2{\ekv@err@common{value forbidden}#1{#2}} -\long\def\ekv@err@reqval #1#2{\ekv@err@common{value required}#1{#2}} -\def\ekv@strip#1% - {% - \long\def\ekv@strip##1% - {% - \ekv@strip@a - ##1% - \ekv@nil - \ekv@mark#1% - #1\ekv@nil{}% - \ekv@stop - }% - \long\def\ekv@strip@a##1\ekv@mark#1##2\ekv@nil##3% - {% - \ekv@strip@b##3##1##2\ekv@nil - }% - \long\def\ekv@strip@b##1#1\ekv@nil - {% - \ekv@strip@c##1\ekv@nil - }% - \long\def\ekv@strip@c\ekv@mark##1\ekv@nil##2\ekv@stop##3% - {% - ##3{##1}% - }% - } -\ekv@strip{ } +\ekv@expB@unbraceA{\long\def\ekv@err@unknown#1}% + {\ekv@err@common{unknown key}{#1}} +\ekv@expB@unbraceA{\long\def\ekv@err@noarg #1}% + {\ekv@err@common{value forbidden}{#1}} +\ekv@expB@unbraceA{\long\def\ekv@err@reqval #1}% + {\ekv@err@common{value required}{#1}} \catcode`\@=\ekv@tmp %% %% -- cgit v1.2.3