summaryrefslogtreecommitdiff
path: root/macros/generic/expkv
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2020-04-09 03:02:41 +0000
committerNorbert Preining <norbert@preining.info>2020-04-09 03:02:41 +0000
commit122d129e46fd990f791aeb4ff07eaa1b35b9af0d (patch)
tree1b1caee0842a9712355f81fef6de58fb73a75d79 /macros/generic/expkv
parent7d1508db81a1fcf4f891b3aef35d122fa88d2ec7 (diff)
CTAN sync 202004090302
Diffstat (limited to 'macros/generic/expkv')
-rw-r--r--macros/generic/expkv/README.md2
-rw-r--r--macros/generic/expkv/expkv.dtx514
-rw-r--r--macros/generic/expkv/expkv.pdfbin360009 -> 388809 bytes
3 files changed, 303 insertions, 213 deletions
diff --git a/macros/generic/expkv/README.md b/macros/generic/expkv/README.md
index 35bcaebf0f..552d95d2b1 100644
--- a/macros/generic/expkv/README.md
+++ b/macros/generic/expkv/README.md
@@ -1,7 +1,7 @@
-------------------------------------------------------------------------------
# expkv -- an expandable key=val implementation
-Version 2020-04-04 v0.5b
+Version 2020-04-07 v1.0
Released under the LaTeX Project Public License v1.3c or later
See http://www.latex-project.org/lppl.txt
diff --git a/macros/generic/expkv/expkv.dtx b/macros/generic/expkv/expkv.dtx
index 366db70123..97e7e2a21b 100644
--- a/macros/generic/expkv/expkv.dtx
+++ b/macros/generic/expkv/expkv.dtx
@@ -199,8 +199,8 @@ and the derived files expkv.pdf
% \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} for instance
-% is thrice as fast, but not expandable and it might strip braces it shouldn't
-% have stripped).
+% is three times as fast, but not expandable and it might strip braces it
+% shouldn't have stripped).
% \end{abstract}
%
% \tableofcontents
@@ -232,7 +232,7 @@ and the derived files expkv.pdf
% \item[def]
% a key-defining frontend for \expkv\ using a \kv\ syntax
% \item[cs]
-% define expandable \kv\ macros utilizing \expkv
+% define expandable \kv\ macros using \expkv
% \end{description}
% Note that while the package names are stylised with a vertical rule, their
% names are all lower case with a hyphen (\emph{e.g.}, \file{expkv-def}).
@@ -479,7 +479,7 @@ and the derived files expkv.pdf
% 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 two times faster and has a comparable
+% \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
@@ -499,53 +499,53 @@ and the derived files expkv.pdf
% \setkeys{foo}{bar={{baz}}}
% \end{lstlisting}
%
-% \paragraph{\pkg{xkeyval}} is roughly fourteen times slower, but it provides
+% \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 300 times slower -- which is funny, because
+% \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}].'' 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
+% [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
+% \pkg{catoptions} which is known to introduce bugs (e.g., see
% \url{https://tex.stackexchange.com/questions/461783}).
%
-% \paragraph{\pkg{l3keys}} is almost five times slower, but has an, imho, great
+% \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.
+% 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 a bit more than two times slower for one key,
-% but has an \emph{enormous} feature set. However, since adding additional keys
-% doesn't add as much needed time for \pkg{pgfkeys} compared to \expkv, it gets
-% faster than \expkv\ at around eight \kv\ pairs.
+% \paragraph{\pkg{pgfkeys}} is around $2.7$ times slower for one key,
+% but has an \emph{enormous} feature set.
% 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.
%
-% \paragraph{\pkg{kvsetkeys} with \pkg{kvdefinekeys}} is about three times
+% \paragraph{\pkg{kvsetkeys} with \pkg{kvdefinekeys}} is about $3.7$ 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 a bit slower for only a single value, but gets a
-% tad faster than \expkv\ at around 10 \kv\ pairs. It has a much bigger feature
-% set. Unfortunately it also suffers from the premature unbracing bug
-% \pkg{keyval} has.
+% \paragraph{\pkg{options}} is a $1.5$ times slower for only a single value. It
+% has a much bigger feature set. Unfortunately it also suffers from the
+% premature unbracing bug \pkg{keyval} has.
%
% \paragraph{\pkg{simplekv}} is hard to compare because I don't speak French (so
% I don't understand the documentation) and from what I can see, there is no
% direct way to define the equivalent test key. Nevertheless, I tested the
% closest possible equivalent of my test key while siding for \pkg{simplekv}'s
% design not forcing something into it it doesn't seem to be designed for. It is
-% more than five times slower and has hard to predict behaviour regarding brace
+% almost seven times slower and has hard to predict behaviour regarding brace
% and space stripping, similar to \pkg{keyval}. The tested definition was:
% \begin{lstlisting}[belowskip=0pt]
% \usepackage{simplekv}
@@ -553,7 +553,7 @@ and the derived files expkv.pdf
% \setKV[simplekv]{ height = 6 } % benchmarked
% \end{lstlisting}
%
-% \paragraph{\pkg{yax}} is over eighteen times slower. It has a pretty strange
+% \paragraph{\pkg{yax}} is over twenty times slower. It has a pretty strange
% syntax, imho, and again a direct equivalent is hard to define. It has the
% premature unbracing bug, too. Also somehow loading \pkg{yax} broke
% \pkg{options} for me. The tested definition was:
@@ -591,16 +591,16 @@ and the derived files expkv.pdf
% \toprule
% \rmfamily Package & {$p_1$} & {$p_0$} & {$T_0$}& BB & CF & Date \\
% \midrule
-% keyval & 13.539 & 1.844 & 6.969 & \yes & \yes & 2014-10-28 \\
-% \expkv & 26.411 & 2.391 & 12.610 & \no & \no & 2020-02-22 \\
-% options & 25.532 & 10.518 & 21.393 & \yes & \yes & 2015-03-01 \\
-% pgfkeys & 26.444 & 40.939 & 55.790 & \yes & \yes & 2020-01-08 \\
-% kvsetkeys & {\fnsym} & {\fnsym} & 41.620 & \no & \no & 2019-12-15 \\
-% l3keys & 114.456 & 35.357 & 52.667 & \no & \no & 2020-02-14 \\
-% simplekv & 160.523 & 10.809 & 8.698 & \yes & \yes & 2017-08-08 \\
-% xkeyval & 260.872 & 180.435 & 164.167 & \yes & \yes & 2014-12-03 \\
-% yax & 507.643 & 62.863 & 123.533 & \yes & \yes & 2010-01-22 \\
-% ltxkeys & 3932.759 & 4737.818 & 5883.000 & \no & \no & 2012-11-17 \\
+% keyval & 13.423 & 2.594 & 6.928 & \yes & \yes & 2014-10-28 \\
+% \expkv & 19.718 & 7.256 & 9.611 & \no & \no & 2020-04-07 \\
+% options & 23.436 & 15.618 & 19.920 & \yes & \yes & 2015-03-01 \\
+% pgfkeys & 24.577 & 46.244 & 52.770 & \yes & \yes & 2020-01-08 \\
+% kvsetkeys & {\fnsym} & {\fnsym} & 39.760 & \no & \no & 2019-12-15 \\
+% l3keys & 108.001 & 56.299 & 50.710 & \no & \no & 2020-02-25 \\
+% simplekv & 149.181 & 25.712 & 8.070 & \yes & \yes & 2017-08-08 \\
+% xkeyval & 248.777 & 234.420 & 161.900 & \yes & \yes & 2014-12-03 \\
+% yax & 443.584 & 170.471 & 115.700 & \yes & \yes & 2010-01-22 \\
+% ltxkeys & 3516.032 & 5254.811 & 5487.000 & \no & \no & 2012-11-17 \\
% \bottomrule
% \end{tabular}
% \par
@@ -609,13 +609,13 @@ 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{9.911}$, $p_1=\num{40.523}$, and $p_0=\num{61.500}$. Of course the
+% $p_2=\num{7.473}$, $p_1=\num{51.276}$, and $p_0=\num{49.568}$. 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 (the maximum ratio $p_2/p_1$
-% for the other packages is \num{9.697e-3}). If one extrapolates the fits
+% for the other packages is \num{3.478e-3}). 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{31.685}$, $p_1=\num{3267.368}$, and $p_0=\num{7177.582}$.
+% $p_2=\num{11.396}$, $p_1=\num{3276.713}$, and $p_0=\num{6132.315}$.
% \end{table}
%
% \subsection{Examples}
@@ -942,27 +942,28 @@ and the derived files expkv.pdf
% \gobbledocstriptag
%<*tex>
%
-% Check whether \eTeX\ is available -- \expkv\ requires \eTeX.
+% We make sure that it's only input once:
% \begin{macrocode}
-\begingroup\expandafter\expandafter\expandafter\endgroup
-\expandafter\ifx\csname numexpr\endcsname\relax
- \errmessage{expkv requires e-TeX}
+\expandafter\ifx\csname ekvVersion\endcsname\relax
+\else
\expandafter\endinput
\fi
% \end{macrocode}
%
-% We make sure that it's only input once:
+% Check whether \eTeX\ is available -- \expkv\ requires \eTeX.
% \begin{macrocode}
-\expandafter\ifx\csname ekvVersion\endcsname\relax
-\else
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname numexpr\endcsname\relax
+ \errmessage{expkv requires e-TeX}
\expandafter\endinput
\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{0.5b}
-\def\ekvDate{2020-04-04}
+\def\ekvVersion{1.0}
+\def\ekvDate{2020-04-07}
% \end{macrocode}
% \end{macro}
%
@@ -988,7 +989,8 @@ and the derived files expkv.pdf
% \begin{macro}[internal]
% {
% \@gobble,\@firstofone,\@firstoftwo,\@secondoftwo,
-% \ekv@gobbleto@stop,\ekv@fi@secondoftwo,\ekv@gobble@mark
+% \ekv@gobbleto@stop,\ekv@fi@secondoftwo,\ekv@gobble@mark,
+% \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
@@ -1002,6 +1004,7 @@ and the derived files expkv.pdf
\long\def\ekv@fi@secondoftwo\fi\@firstoftwo#1#2{\fi#2}
\long\def\ekv@gobbleto@stop#1\ekv@stop{}
\def\ekv@gobble@mark\ekv@mark{}
+\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
@@ -1014,7 +1017,8 @@ and the derived files expkv.pdf
% \begin{macro}[internal]
% {
% \ekv@ifempty,\ekv@ifempty@,\ekv@ifempty@true,\ekv@ifempty@false,
-% \ekv@ifempty@true@F
+% \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
@@ -1029,6 +1033,11 @@ and the derived files expkv.pdf
\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}
%
@@ -1059,54 +1068,6 @@ and the derived files expkv.pdf
}
% \end{macrocode}
% \end{macro}
- % \begin{macro}[internal]
-% {
-% \ekv@ifdefined@pair,\ekv@ifdefined@pair@,\ekv@ifdefined@key,
-% \ekv@ifdefined@key@
-% }
-% Since we can save some time if we only have to create the control sequence
-% once when we know beforehand how we want to use it, we build some other macros
-% for those cases (which we'll have quite often, once per key usage).
-% \begin{macrocode}
-\def\ekv@ifdefined@pair#1#2%
- {%
- \expandafter\ekv@ifdefined@pair@
- \csname
- \ifcsname #1{#2}\endcsname
- #1{#2}%
- \else
- relax%
- \fi
- \endcsname
- }
-\def\ekv@ifdefined@pair@#1%
- {%
- \ifx#1\relax
- \ekv@fi@secondoftwo
- \fi
- \@firstoftwo
- {\ekv@set@pair@#1\ekv@mark}%
- }
-\def\ekv@ifdefined@key#1#2%
- {%
- \expandafter\ekv@ifdefined@key@
- \csname
- \ifcsname #1{#2}N\endcsname
- #1{#2}N%
- \else
- relax%
- \fi
- \endcsname
- }
-\def\ekv@ifdefined@key@#1%
- {%
- \ifx#1\relax
- \ekv@fi@secondoftwo
- \fi
- \@firstoftwo#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.
@@ -1116,6 +1077,7 @@ and the derived files expkv.pdf
\def\ekv@name@key#1{#1)}
% \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
@@ -1128,10 +1090,12 @@ and the derived files expkv.pdf
\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).
+% or} key-names consisting of spaces). The |\def\ekv@tmp| gobbles any \TeX\
+% prefixes which would otherwise throw errors.
% \begin{macrocode}
\protected\def\ekv@checkvalid#1#2%
{%
@@ -1152,6 +1116,7 @@ and the derived files expkv.pdf
}
% \end{macrocode}
% \end{macro}
+%
% \begin{macro}{\ekvifdefined,\ekvifdefinedNoVal}
% And provide user-level macros to test whether a key is defined.
% \begin{macrocode}
@@ -1216,6 +1181,7 @@ and the derived files expkv.pdf
}
% \end{macrocode}
% \end{macro}
+%
% \begin{macro}[internal]{\ekv@defset}
% 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
@@ -1246,6 +1212,7 @@ and the derived files expkv.pdf
}
% \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.
@@ -1255,7 +1222,7 @@ and the derived files expkv.pdf
% \end{macrocode}
% Test whether we're at the end, if so invoke |\ekv@endset|,
% \begin{macrocode}
- \ekv@gobbleto@markstop##2\ekv@endset\ekv@mark\ekv@stop
+ \ekv@gobble@from@mark@to@stop##2\ekv@endset\ekv@stop
% \end{macrocode}
% else go on with other commas,
% \begin{macrocode}
@@ -1267,83 +1234,161 @@ and the derived files expkv.pdf
}
% \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@mark\ekv@stop\ekv@set@other##1,\ekv@stop,\ekv@set##2\ekv@mark
+ \ekv@stop\ekv@set@other##1,\ekv@stop,\ekv@set##2\ekv@mark
##3%
{##3}
% \end{macrocode}
% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@eq@other,\ekv@eq@active}
+% Splitting at equal signs will be done in a way that checks whether there is
+% an equal sign and splits at the same time. This gets quite messy and the
+% code might look complicated, but this is pretty fast (faster than first
+% checking for an equal sign and splitting if one is found). The splitting
+% code will be adapted for |\ekvset| and |\ekvparse| to get the most speed,
+% but some of these macros don't require such adaptions. |\ekv@eq@other| and
+% |\ekv@eq@active| will split the argument at the first equal sign and insert
+% the macro which comes after the first following |\ekv@mark|. This allows for
+% fast branching based on \TeX's argument grabbing rules and we don't have to
+% split after the branching if the equal sign was there.
+% \begin{macrocode}
+\long\def\ekv@eq@other##1=##2\ekv@mark##3##4\ekv@stop
+ {%
+ ##3##1\ekv@stop\ekv@mark##2%
+ }
+\long\def\ekv@eq@active##1#2##2\ekv@mark##3##4\ekv@stop
+ {%
+ ##3##1\ekv@stop\ekv@mark##2%
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}[internal]{\ekv@set@other}
-% The macro |\ekv@set@other| is guaranteed to get only single \kv\ pairs.
-% First we test whether we're done, if not split at equal signs. It is faster to
-% first split at category 12 equal signs and only after that on actives.
-% If there is no equal sign, we need to test whether we got a blank argument and
-% if not this is a |NoVal| key.
+% The macro |\ekv@set@other| is guaranteed to get only single \kv\ pairs.
% \begin{macrocode}
\long\def\ekv@set@other##1##2,%
{%
- \ekv@gobbleto@markstop##2\ekv@endset@other\ekv@mark\ekv@stop
- \ekv@ifhas@eq@other##2=\ekv@ifempty@B\ekv@ifempty@false
- \ekv@ifempty@A\ekv@ifempty@B\@firstoftwo
- {\ekv@set@eq@other##1##2\ekv@stop}%
- {%
- \ekv@ifhas@eq@active##2#2\ekv@ifempty@B\ekv@ifempty@false
- \ekv@ifempty@A\ekv@ifempty@B\@firstoftwo
- {\ekv@set@eq@active##1##2\ekv@stop}%
- {%
- \ekv@ifblank@##2\ekv@nil\ekv@ifempty@B\ekv@ifempty@true@F
- \ekv@ifempty@A\ekv@ifempty@B\@firstofone
- {\ekv@strip{##2}\ekv@set@key##1}%
- }%
- }%
- \ekv@set@other##1\ekv@mark%
+% \end{macrocode}
+% First we test whether we're done.
+% \begin{macrocode}
+ \ekv@gobble@from@mark@to@stop##2\ekv@endset@other\ekv@stop
+% \end{macrocode}
+% If not we split at the equal sign of category other.
+% \begin{macrocode}
+ \ekv@eq@other##2\ekv@nil\ekv@mark\ekv@set@eq@other@a
+ =\ekv@mark\ekv@set@eq@active\ekv@stop
+% \end{macrocode}
+% And insert the set name and the next recursion step of |\ekv@set@other|.
+% \begin{macrocode}
+ ##1%
+ \ekv@set@other##1\ekv@mark
}
% \end{macrocode}
% \end{macro}
-% \begin{macro}[internal]{\ekv@set@eq@other}
-% |\ekv@set@eq@other| might not be the correct break point, there might be an
-% active equal sign in the currently parsed key-name. If so, we have to resplit.
-% If the split is correct strip the key-name of outer spaces and braces and feed
-% it to |\ekv@set@pair|.
+%
+% \begin{macro}[internal]{\ekv@set@eq@other@a, \ekv@set@eq@other@b}
+% The first of these two macros runs the split-test for equal signs of
+% category active. It will only be inserted if the \kv\ pair contained at
+% least one equal sign of category other and |##1| will contain everything up
+% to that equal sign.
+% \begin{macrocode}
+\long\def\ekv@set@eq@other@a##1\ekv@stop
+ {%
+ \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@set@eq@other@active@a
+ #2\ekv@mark\ekv@set@eq@other@b\ekv@stop
+ }
+% \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|.
% \begin{macrocode}
-\long\def\ekv@set@eq@other##1##2=%
+\long\def\ekv@set@eq@other@b
+ ##1\ekv@nil\ekv@mark\ekv@set@eq@other@active@a\ekv@stop\ekv@mark
+ ##2\ekv@nil
{%
- \ekv@ifhas@eq@active##2#2\ekv@ifempty@B\ekv@ifempty@false
- \ekv@ifempty@A\ekv@ifempty@B\@firstoftwo
- {\ekv@set@eq@active##1##2=}%
- {\ekv@strip{##2}\ekv@set@pair##1}%
+ \ekv@strip{##1}\ekv@set@pair{##2}%
}
% \end{macrocode}
% \end{macro}
-% \begin{macro}[internal]{\ekv@set@eq@active}
-% We need to handle the active equal signs.
+%
+% \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
+% 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.
% \begin{macrocode}
-\long\def\ekv@set@eq@active##1##2#2%
+\long\def\ekv@set@eq@other@active@a##1\ekv@stop##2\ekv@nil\ekv@mark
{%
- \ekv@strip{##2}\ekv@set@pair##1%
+ \ekv@set@eq@other@active@b{##1}##2=%
+ }
+\long\def\ekv@set@eq@other@active@b##1##2\ekv@nil
+ {%
+ \ekv@strip{##1}\ekv@set@pair{##2}%
}
% \end{macrocode}
% \end{macro}
-% \begin{macro}[internal]
-% {\ekv@ifhas@eq@other,\ekv@ifhas@eq@active,\ekv@endset@other}
-% And we have to set up the testing macros for our equal signs and
-% |\ekv@endset@other|.
+%
+% \begin{macro}[internal]{\ekv@set@eq@active, \ekv@set@eq@active@}
+% |\ekv@set@eq@active| will be called when there was no equal sign of category
+% other in the \kv\ pair. It removes the excess tokens of the prior test and
+% split-checks for an active equal sign.
+% \begin{macrocode}
+\long\def\ekv@set@eq@active
+ ##1\ekv@nil\ekv@mark\ekv@set@eq@other@a\ekv@stop\ekv@mark
+ {%
+ \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@set@eq@active@
+ #2\ekv@mark\ekv@set@noeq\ekv@stop
+ }
+% \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##2\ekv@nil
+ {%
+ \ekv@strip{##1}\ekv@set@pair{##2}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@set@noeq}
+% If no active equal sign was found by |\ekv@set@eq@active| there is no equal
+% sign contained in the parsed list entry. In that case we have to check
+% whether the entry is blank in order to ignore it (in which case we'll have
+% to gobble the set-name which was put after these tests by |\ekv@set@other|).
+% Else this is a |NoVal| key and the entry is passed on to |\ekv@set@key|.
+% \begin{macrocode}
+\long\def\ekv@set@noeq##1\ekv@nil\ekv@mark\ekv@set@eq@active@\ekv@stop\ekv@mark
+ {%
+ \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}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@endset@other}
+% All that's left for |\ekv@set@other| is the macro which breaks the recursion
+% loop at the end. This is done by gobbling all the remaining tokens.
% \begin{macrocode}
-\long\def\ekv@ifhas@eq@other\ekv@mark##1={\ekv@ifempty@\ekv@ifempty@A}
-\long\def\ekv@ifhas@eq@active\ekv@mark##1#2{\ekv@ifempty@\ekv@ifempty@A}
\long\def\ekv@endset@other
- \ekv@mark\ekv@stop
- \ekv@ifhas@eq@other##1=\ekv@ifempty@B\ekv@ifempty@false
- \ekv@ifempty@A\ekv@ifempty@B\@firstoftwo
+ \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
{}
% \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.
@@ -1353,6 +1398,7 @@ and the derived files expkv.pdf
\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.
@@ -1381,90 +1427,124 @@ and the derived files expkv.pdf
}
% \end{macrocode}
% \end{macro}
+%
% \begin{macro}[internal]{\ekv@parse}
% \begin{macrocode}
\long\def\ekv@parse##1##2##3#1%
{%
- \ekv@gobbleto@markstop##3\ekv@endparse\ekv@mark\ekv@stop
+ \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}
+%
% \begin{macro}[internal]{\ekv@endparse}
% \begin{macrocode}
\long\def\ekv@endparse
- \ekv@mark\ekv@stop\ekv@parse@other##1,\ekv@stop,\ekv@parse##2\ekv@mark
+ \ekv@stop\ekv@parse@other##1,\ekv@stop,\ekv@parse##2\ekv@mark
{}
% \end{macrocode}
% \end{macro}
+%
% \begin{macro}[internal]{\ekv@parse@other}
% \begin{macrocode}
\long\def\ekv@parse@other##1##2##3,%
{%
- \ekv@gobbleto@markstop##3\ekv@endparse@other\ekv@mark\ekv@stop
- \ekv@ifhas@eq@other##3=\ekv@ifempty@B\ekv@ifempty@false
- \ekv@ifempty@A\ekv@ifempty@B\@firstoftwo
- {\ekv@parse@eq@other##3\ekv@stop##2}%
- {%
- \ekv@ifhas@eq@active##3#2\ekv@ifempty@B\ekv@ifempty@false
- \ekv@ifempty@A\ekv@ifempty@B\@firstoftwo
- {\ekv@parse@eq@active##3\ekv@stop##2}%
- {%
- \ekv@ifblank@##3\ekv@nil\ekv@ifempty@B\ekv@ifempty@true@F
- \ekv@ifempty@A\ekv@ifempty@B\@firstofone
- {\ekv@strip{##3}\ekv@parse@key##1}%
- }%
- }%
+ \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
+ ##1##2%
\ekv@parse@other##1##2\ekv@mark
}
% \end{macrocode}
% \end{macro}
-% \begin{macro}[internal]{\ekv@parse@eq@other}
+%
+% \begin{macro}[internal]{\ekv@parse@eq@other@a,\ekv@parse@eq@other@b}
+% \begin{macrocode}
+\long\def\ekv@parse@eq@other@a##1\ekv@stop
+ {%
+ \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@parse@eq@other@active@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
+ ##2\ekv@nil
+ {%
+ \ekv@strip{##1}\ekv@parse@pair{##2}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {\ekv@parse@eq@other@active@a,\ekv@parse@eq@other@active@b}
+% \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##2\ekv@nil
+ {%
+ \ekv@strip{##1}\ekv@parse@pair{##2}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekv@parse@eq@active,\ekv@parse@eq@active@}
% \begin{macrocode}
-\long\def\ekv@parse@eq@other##1=%
+\long\def\ekv@parse@eq@active
+ ##1\ekv@nil\ekv@mark\ekv@parse@eq@other@a\ekv@stop\ekv@mark
{%
- \ekv@ifhas@eq@active##1#2\ekv@ifempty@B\ekv@ifempty@false
- \ekv@ifempty@A\ekv@ifempty@B\@firstoftwo
- {\ekv@parse@eq@active##1=}%
- {\ekv@strip{##1}\ekv@parse@pair\ekv@mark}%
+ \ekv@eq@active##1\ekv@nil\ekv@mark\ekv@parse@eq@active@
+ #2\ekv@mark\ekv@parse@noeq\ekv@stop
+ }
+\long\def\ekv@parse@eq@active@##1##2\ekv@nil
+ {%
+ \ekv@strip{##1}\ekv@parse@pair{##2}%
}
% \end{macrocode}
% \end{macro}
-% \begin{macro}[internal]{\ekv@parse@eq@active}
+%
+% \begin{macro}[internal]{\ekv@parse@noeq}
% \begin{macrocode}
-\long\def\ekv@parse@eq@active##1#2%
+\long\def\ekv@parse@noeq
+ ##1\ekv@nil\ekv@mark\ekv@parse@eq@active@\ekv@stop\ekv@mark
{%
- \ekv@strip{##1}\ekv@parse@pair\ekv@mark
+ \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}%
}
% \end{macrocode}
% \end{macro}
+%
% \begin{macro}[internal]{\ekv@endparse@other}
% \begin{macrocode}
\long\def\ekv@endparse@other
- \ekv@mark\ekv@stop
- \ekv@ifhas@eq@other##1=\ekv@ifempty@B\ekv@ifempty@false
- \ekv@ifempty@A\ekv@ifempty@B\@firstoftwo
+ \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
{}
% \end{macrocode}
% \end{macro}
+%
% \begin{macro}[internal]{\ekv@parse@pair,\ekv@parse@pair@}
% \begin{macrocode}
-\long\def\ekv@parse@pair##1##2\ekv@stop
+\long\def\ekv@parse@pair##1##2%
{%
\ekv@strip{##2}\ekv@parse@pair@{##1}%
}
-\long\def\ekv@parse@pair@##1##2##3%
+\long\def\ekv@parse@pair@##1##2##3##4%
{%
- \unexpanded{##3{##2}{##1}}%
+ \unexpanded{##4{##2}{##1}}%
}
% \end{macrocode}
% \end{macro}
+%
% \begin{macro}[internal]{\ekv@parse@key}
% \begin{macrocode}
-\long\def\ekv@parse@key##1##2%
+\long\def\ekv@parse@key##1##2##3%
{%
\unexpanded{##2{##1}}%
}
@@ -1495,6 +1575,7 @@ and the derived files expkv.pdf
}
% \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
@@ -1508,58 +1589,59 @@ and the derived files expkv.pdf
% \end{macro}
%
%
-% \begin{macro}[internal]{\ekv@gobbleto@markstop}
-% The |\ekv@gobbleto@markstop| can be used to test for |\ekv@stop| similar to
-% our if-empty test, but instead of using tokens which are used nowhere else
-% (|\ekv@ifempty@A| and |\ekv@ifempty@B|) it uses |\ekv@mark| and |\ekv@stop|.
+% \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 error messages
+% provided by |\ekv@set@pair|.
% \begin{macrocode}
-\long\def\ekv@gobbleto@markstop#1\ekv@mark\ekv@stop{}
-% \end{macrocode}
-% \end{macro}
-%
-% \begin{macro}[internal]{\ekv@set@pair,\ekv@set@pair@}
-% |\ekv@set@pair| gets invoked with the space and brace stripped key-name as its
-% first argument, the set-macro as the second argument, and following that is
-% the key-value right delimited by an |\ekv@stop|.
-% \begin{macrocode}
-\long\def\ekv@set@pair#1#2%
+\long\def\ekv@set@pair#1#2#3%
{%
- \ekv@ifdefined@pair#2{#1}%
+ \expandafter\ekv@set@pair@
+ \csname
+ \ifcsname #3{#1}\endcsname
+ #3{#1}%
+ \else
+ relax%
+ \fi
+ \endcsname
+ {#2}%
{%
-% \end{macrocode}
-% This branch will be executed if the key is not defined as an argument grabbing
-% one. If so test whether there is a |NoVal| key of the same name or whether the
-% key is unknown. Throw a meaningful error message and gobble the value.
-% \begin{macrocode}
- \ekv@ifdefined{#2{#1}N}%
+ \ekv@ifdefined{#3{#1}N}%
\ekv@err@noarg
\ekv@err@unknown
- #2{#1}%
- \ekv@gobbleto@stop
+ #3{#1}%
}%
}
-% \end{macrocode}
-% |\ekv@ifdefined@pair| will call |\ekv@set@pair@| if the key is correctly
-% defined. This will then grab the value, strip outer spaces and braces from it
-% and feed it to the key-macro. Afterwards |\ekv@set@other| will take control
-% again.
-% \begin{macrocode}
-\long\def\ekv@set@pair@#1#2\ekv@stop
+\long\def\ekv@set@pair@#1#2%
{%
- \ekv@strip{#2}#1%
+ \ifx#1\relax
+ \ekv@fi@secondoftwo
+ \fi
+ \@firstoftwo
+ {\ekv@strip{#2}#1}%
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\ekv@set@key}
-% Analogous to |\ekv@set@pair|, |\ekv@set@key| lets |\ekv@ifdefined@key| test
-% whether a |NoVal| key is defined, else it'll throw a meaningful error message.
-% Since we don't have to grab any value |\ekv@ifdefined@key| will invoke the
-% key-macro and we're done here, |\ekv@set@other| will take over again.
+% 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@ifdefined@key#2{#1}%
+ \expandafter\ekv@set@key@
+ \csname
+ \ifcsname #2{#1}N\endcsname
+ #2{#1}N%
+ \else
+ relax%
+ \fi
+ \endcsname
{%
\ekv@ifdefined{#2{#1}}%
\ekv@err@reqval
@@ -1567,6 +1649,13 @@ and the derived files expkv.pdf
#2{#1}%
}%
}
+\def\ekv@set@key@#1%
+ {%
+ \ifx#1\relax
+ \ekv@fi@secondoftwo
+ \fi
+ \@firstoftwo#1%
+ }
% \end{macrocode}
% \end{macro}
%
@@ -1590,6 +1679,7 @@ and the derived files expkv.pdf
\def\ekv@err@{\expandafter\ekv@gobbleto@stop}
% \end{macrocode}
% \end{macro}
+%
% \begin{macro}[internal]
% {
% \ekv@err@common,\ekv@err@common@,
diff --git a/macros/generic/expkv/expkv.pdf b/macros/generic/expkv/expkv.pdf
index 5c40e805bd..c6512d943b 100644
--- a/macros/generic/expkv/expkv.pdf
+++ b/macros/generic/expkv/expkv.pdf
Binary files differ