From a028b56d2f5651b6aeefd34cc75b550617fa9989 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Sat, 11 Apr 2020 22:21:50 +0000 Subject: expkv (10apr20) git-svn-id: svn://tug.org/texlive/trunk@54654 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/source/latex/expkv/expkv.dtx | 618 +++++++++++++++---------- 1 file changed, 375 insertions(+), 243 deletions(-) (limited to 'Master/texmf-dist/source/latex/expkv') diff --git a/Master/texmf-dist/source/latex/expkv/expkv.dtx b/Master/texmf-dist/source/latex/expkv/expkv.dtx index 0f0491164e6..3fae5459b08 100644 --- a/Master/texmf-dist/source/latex/expkv/expkv.dtx +++ b/Master/texmf-dist/source/latex/expkv/expkv.dtx @@ -74,6 +74,7 @@ and the derived files expkv.pdf \RequirePackage{caption} \RequirePackage{microtype} \RequirePackage{accsupp} +\RequirePackage{enumitem} \lstset { ,flexiblecolumns=false @@ -122,27 +123,32 @@ and the derived files expkv.pdf } {expkv}^^A } -\newcommand*\expkvd +\newcommand*\expkvpkg[1] {^^A \texorpdfstring {^^A \mbox {^^A - \BeginAccSupp{ActualText=expkv-def}^^A - \rmfamily - \bfseries - {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A - \lower.493ex - \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A - \kern-.18em{\color{expkvred}v}^^A - {\color{expkvgrey}^^A - \kern.05em\rule[-.1ex]{.08em}{1.2ex}\kern.05em\textsc{def}^^A - }^^A + \BeginAccSupp{ActualText=expkv-#1}^^A + \href{https://github.com/Skillmon/tex_expkv-#1} + {^^A + \rmfamily + \bfseries + {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A + \lower.493ex + \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A + \kern-.18em{\color{expkvred}v}^^A + {\color{expkvgrey}^^A + \kern.05em\rule[-.1ex]{.08em}{1.2ex}\kern.05em\textsc{#1}^^A + }^^A + }^^A \EndAccSupp{}^^A }^^A } - {expkv-def}^^A + {expkv-#1}^^A } +\newcommand*\expkvd{\expkvpkg{def}} +\newcommand*\expkvc{\expkvpkg{cs}} \newcommand\kv{\meta{key}=\meta{value}} \newcommand\key{\meta{key}} \newcommand\val{\meta{value}} @@ -150,7 +156,7 @@ and the derived files expkv.pdf \hypersetup{linkcolor=red!80!black,urlcolor=purple!80!black} \DoNotIndex{\def,\edef,\,,\=,\begingroup,\catcode,\chardef,\csname,\endcsname} \DoNotIndex{\endgroup,\endinput,\errmessage,\expandafter,\input,\let,\long} -\DoNotIndex{\noexpand,\protected,\ProvidesFile,\ProvidesPackage,\relax,\space} +\DoNotIndex{\protected,\ProvidesFile,\ProvidesPackage,\relax,\space} \DoNotIndex{\@,\unexpanded,\string,\expanded} \DoNotIndex{\ifcsname} \DoNotIndex{\ifx} @@ -192,8 +198,8 @@ and the derived files expkv.pdf % \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} for instance -% is thrice as fast, but not expandable and it might strip braces it shouldn't +% 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). % \end{abstract} % @@ -220,13 +226,22 @@ and the derived files expkv.pdf % |\ProvidesPackage| and setting things up such that \file{expkv.tex} will use % |\ProvidesFile|. % +% In the \expkv\ family are other packages contained which provide additional +% functionality. Those packages currently are: +% \begin{description}[format=\expkvpkg] +% \item[def] +% a key-defining frontend for \expkv\ using a \kv\ syntax +% \item[cs] +% define expandable \kv\ macros using \expkv +% \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}). +% % \subsection{Setting up Keys}\label{sec:define} % \expkv\ provides a rather simple approach to setting up keys, similar to -% \pkg{keyval}. However there is an auxiliary package named -% \href{https://github.com/Skillmon/tex_expkv-def}{\expkvd} (the files use -% \file{expkv-def} instead because \string| isn't allowed in package names by -% CTAN) which provides a more sophisticated interface, similar to well -% established packages like \pkg{pgfkeys} or \pkg{l3keys}. +% \pkg{keyval}. However there is an auxiliary package named \expkvd\ which +% provides a more sophisticated interface, similar to well established packages +% like \pkg{pgfkeys} or \pkg{l3keys}. % % Keys in \expkv\ (as in almost all other \kv\ implementations) belong to a % \emph{set} such that different sets can contain keys of the same name. Unlike @@ -236,7 +251,8 @@ and the derived files expkv.pdf % user level). % % The following macros are available to define new keys. Those macros containing -% ``|def|'' in their name can be prefixed by anything allowed to prefix |\def|, +% ``|def|'' in their name can be prefixed by anything allowed to prefix |\def| +% (but \emph{don't} use |\outer|, keys defined with it won't ever be usable), % prefixes allowed for |\let| can prefix those with ``|let|'' in their name, % accordingly. Neither \set\ nor \key\ are allowed to be empty for new keys and % must not contain a |\par| or tokens that expand to it -- they must be legal @@ -464,7 +480,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 @@ -484,52 +500,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 -% it aims to be ``[\ldots] faster 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 \url{https://tex.stackexchange.com/questions/461783}). -% -% \paragraph{\pkg{l3keys}} is almost five times slower, but has an, imho, great +% \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 +% \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. +% 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 $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} @@ -537,7 +554,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: @@ -575,16 +592,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 @@ -593,13 +610,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} @@ -683,7 +700,7 @@ and the derived files expkv.pdf % To ignore duplicates will be easy if the value of the key used last will be % put first in the list, so the following will use |\ekvsneakPre| for the % user-level keys. If we wanted some key for which the first usage should be the -% the binding one we would use |\ekvsneak| instead for that key. +% binding one we would use |\ekvsneak| instead for that key. % % Providing default values can be done in different ways, we'll use a simple % approach in which we'll just put the outcome of our keys if they were used @@ -777,6 +794,30 @@ and the derived files expkv.pdf % \edef\myval{\sine{f=tan}{1}}\texttt{\meaning\myval} % \end{minipage} % +% \paragraph{The same macro using \protect\expkvc} +% Using \expkvc\ we can set up something equivalent with a bit less code. The +% implementation chosen in \expkvc\ is more efficient than the example above and +% way easier to code. +% \begin{lstlisting} +% \makeatletter +% \ekvcSplitAndForward\sine\sine@ +% { +% f=sin, +% unit=d, +% round=3, +% } +% \ekvcSecondaryKeys\sine +% { +% nmeta degree={unit=d}, +% nmeta radian={unit={}}, +% } +% \newcommand*\sine@[4]{\fpeval{round(#1#2(#4),#3)}} +% \makeatother +% \end{lstlisting} +% The resulting macro will behave just like the one previously defined, but will +% have an additional |unit| key, since in \expkvc\ every argument must have a +% value taking key which defines it. +% % % \subsection{Error Messages} % @@ -902,27 +943,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.5a} -\def\ekvDate{2020-02-27} +\def\ekvVersion{1.2} +\def\ekvDate{2020-04-10} % \end{macrocode} % \end{macro} % @@ -948,7 +990,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 @@ -962,6 +1005,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 @@ -974,7 +1018,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 @@ -989,6 +1034,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} % @@ -1018,54 +1068,6 @@ and the derived files expkv.pdf \@firstoftwo } % \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} @@ -1076,6 +1078,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 @@ -1088,10 +1091,14 @@ 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. This will, however, break the +% package if an |\outer| has been gobbled this way. I consider that good, +% because keys shouldn't be defined |\outer| anyways. % \begin{macrocode} \protected\def\ekv@checkvalid#1#2% {% @@ -1112,6 +1119,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} @@ -1129,7 +1137,7 @@ and the derived files expkv.pdf \ekv@checkvalid{#1}{#2}% {% \expandafter\def\csname\ekv@name{#1}{#2}\endcsname##1{#3}% - \ekv@defset{#1}% + \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}% }% } \protected\long\def\ekvdefNoVal#1#2#3% @@ -1137,7 +1145,7 @@ and the derived files expkv.pdf \ekv@checkvalid{#1}{#2}% {% \expandafter\def\csname\ekv@name{#1}{#2}N\endcsname{#3}% - \ekv@defset{#1}% + \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}% }% } \protected\def\ekvlet#1#2#3% @@ -1145,7 +1153,7 @@ and the derived files expkv.pdf \ekv@checkvalid{#1}{#2}% {% \expandafter\let\csname\ekv@name{#1}{#2}\endcsname#3% - \ekv@defset{#1}% + \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}% }% } \protected\def\ekvletNoVal#1#2#3% @@ -1153,7 +1161,7 @@ and the derived files expkv.pdf \ekv@checkvalid{#1}{#2}% {% \expandafter\let\csname\ekv@name{#1}{#2}N\endcsname#3% - \ekv@defset{#1}% + \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}% }% } \protected\def\ekvletkv#1#2#3#4% @@ -1162,7 +1170,7 @@ and the derived files expkv.pdf {% \expandafter\let\csname\ekv@name{#1}{#2}\expandafter\endcsname \csname\ekv@name{#3}{#4}\endcsname - \ekv@defset{#1}% + \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}% }% } \protected\def\ekvletkvNoVal#1#2#3#4% @@ -1171,20 +1179,22 @@ and the derived files expkv.pdf {% \expandafter\let\csname\ekv@name{#1}{#2}N\expandafter\endcsname \csname\ekv@name{#3}{#4}N\endcsname - \ekv@defset{#1}% + \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}% }% } % \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 % sequence. % \begin{macrocode} -\protected\def\ekv@defset#1% +\protected\def\ekv@defset#1#2% {% - \expandafter\edef\csname\ekv@undefined@set{#1}\endcsname##1% - {\ekv@name@set{#1}\ekv@name@key{##1}}% + \ifx#1\relax + \edef#1##1{\ekv@name@set{#2}\ekv@name@key{##1}}% + \fi } % \end{macrocode} % \end{macro} @@ -1206,6 +1216,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. @@ -1215,7 +1226,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} @@ -1227,83 +1238,160 @@ 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 {% - \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 } % \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@set@eq@other@active@b{##1}##2=% + } +\long\def\ekv@set@eq@other@active@b##1% {% - \ekv@strip{##2}\ekv@set@pair##1% + \ekv@strip{##1}\ekv@set@pair } % \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\ekv@stop + {% + \ekv@strip{##1}\ekv@set@pair + } +% \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. @@ -1313,6 +1401,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. @@ -1341,90 +1430,123 @@ 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 + {% + \ekv@strip{##1}\ekv@parse@pair + } +% \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% + {% + \ekv@strip{##1}\ekv@parse@pair + } +% \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@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\ekv@stop {% - \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@strip{##1}\ekv@parse@pair } % \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@nil {% \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}}% } @@ -1455,6 +1577,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 @@ -1468,58 +1591,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\ekv@nil#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 @@ -1527,6 +1651,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} % @@ -1542,7 +1673,7 @@ and the derived files expkv.pdf \unexpanded{\long\def\ekv@err}##1% {% \unexpanded{\expandafter\ekv@err@\@firstofone}% - {\expandafter\noexpand\csname ! expkv Error:\endcsname ##1.}% + {\unexpanded\expandafter{\csname ! expkv Error:\endcsname}##1.}% \unexpanded{\ekv@stop}% }% } @@ -1550,6 +1681,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@, -- cgit v1.2.3