summaryrefslogtreecommitdiff
path: root/macros/generic/expkv/expkv.dtx
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2020-07-03 03:03:30 +0000
committerNorbert Preining <norbert@preining.info>2020-07-03 03:03:30 +0000
commit925eebea568c77dab877e87724fe7b001b3b5e7f (patch)
tree49652ebe25a442b29b3641a515e5c79e910bc07e /macros/generic/expkv/expkv.dtx
parentc2b7c45f7c8c093b8bc730da9abaa919d8327491 (diff)
CTAN sync 202007030303
Diffstat (limited to 'macros/generic/expkv/expkv.dtx')
-rw-r--r--macros/generic/expkv/expkv.dtx237
1 files changed, 175 insertions, 62 deletions
diff --git a/macros/generic/expkv/expkv.dtx b/macros/generic/expkv/expkv.dtx
index 3fae5459b0..12cf70fd87 100644
--- a/macros/generic/expkv/expkv.dtx
+++ b/macros/generic/expkv/expkv.dtx
@@ -149,6 +149,7 @@ and the derived files expkv.pdf
}
\newcommand*\expkvd{\expkvpkg{def}}
\newcommand*\expkvc{\expkvpkg{cs}}
+\newcommand*\expkvo{\expkvpkg{opt}}
\newcommand\kv{\meta{key}=\meta{value}}
\newcommand\key{\meta{key}}
\newcommand\val{\meta{value}}
@@ -157,7 +158,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}
+\DoNotIndex{\@,\unexpanded,\string,\expanded,\detokenize}
\DoNotIndex{\ifcsname}
\DoNotIndex{\ifx}
\DoNotIndex{\else}
@@ -233,6 +234,8 @@ and the derived files expkv.pdf
% a key-defining frontend for \expkv\ using a \kv\ syntax
% \item[cs]
% define expandable \kv\ macros using \expkv
+% \item[opt]
+% parse package and class options with \expkv
% \end{description}
% Note that while the package names are stylised with a vertical rule, their
% names are all lower case with a hyphen (\emph{e.g.}, \file{expkv-def}).
@@ -254,9 +257,9 @@ and the derived files expkv.pdf
% ``|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
-% inside of |\csname ...\endcsname|.
+% 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.
%
% \begin{function}{\ekvdef}
% \begin{syntax}
@@ -305,6 +308,28 @@ and the derived files expkv.pdf
% whether that second key exists.
% \end{function}
%
+% \begin{function}{\ekvdefunknown}
+% \begin{syntax}
+% \cs{ekvdefunknown}\marg{set}\marg{code}
+% \end{syntax}
+% By default an error will be thrown if an unknown \key\ is encountered. With
+% this macro you can define \meta{code} that will be executed for a given
+% \set\ when an unknown \key\ with a \val\ was encountered instead of throwing
+% an error. You can refer to the given \val\ with |#1| and to the unknown
+% \key's name with |#2| in \meta{code}.\footnotemark
+% \end{function}
+% \footnotetext{That order is correct, this way the code is faster.}
+%
+% \begin{function}{\ekvdefunknownNoVal}
+% \begin{syntax}
+% \cs{ekvdefunknownNoVal}\marg{set}\marg{code}
+% \end{syntax}
+% As already explained for \cs[no-index]{ekvdefunknown}, \expkv\ would throw
+% an error when encountering an unknown \key. With this you can instead let it
+% execute \meta{code} if an unknown |NoVal| \key\ was encountered. You can
+% refer to the given \key\ with |#1| in \meta{code}.
+% \end{function}
+%
% \subsection{Parsing Keys}
%
% \begin{function}{\ekvset}
@@ -324,6 +349,18 @@ and the derived files expkv.pdf
% |\ekvset| should be nestable.
% \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
+% \texttt
+% {\string\long\string\def\meta{cs}\#1\{\string\ekvset\marg{set}\{\#1\}\}}
+% \end{function}
+%
% \begin{function}{\ekvparse}
% \begin{syntax}
% \cs{ekvparse}\meta{cs1}\meta{cs2}\{\kv,\ldots\}
@@ -440,7 +477,11 @@ and the derived files expkv.pdf
% ``\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{\cs[no-index]{ekv@name@set}\marg{set}\cs{ekv@name@key}\marg{key}}.
+% \texttt
+% {^^A
+% \cs[no-index]{ekv@name@set}\marg{set}\cs{ekv@name@key}^^A
+% \{\cs[no-index]{detokenize}\marg{key}\}^^A
+% }.
% 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
@@ -542,17 +583,16 @@ and the derived files expkv.pdf
% 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
-% 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}
-% \setKVdefault[simplekv]{height={ abc}} % key setup
-% \setKV[simplekv]{ height = 6 } % benchmarked
-% \end{lstlisting}
+% I don't understand the documentation). There was an update released on
+% 2020-04-27 which greatly improved the package's performance and adds
+% functionality so that it can be used more like most of the other \kv\
+% packages. It has problems with stripping braces and spaces in a hard to
+% predict manner just like \pkg{keyval}. Also, while it tries to be robust
+% against category code changes of commas and equal signs, the used mechanism
+% fails if the \kv\ list already got tokenized. 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.
%
% \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
@@ -592,16 +632,16 @@ and the derived files expkv.pdf
% \toprule
% \rmfamily Package & {$p_1$} & {$p_0$} & {$T_0$}& BB & CF & Date \\
% \midrule
-% 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 \\
+% 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 \\
% \bottomrule
% \end{tabular}
% \par
@@ -610,13 +650,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{7.473}$, $p_1=\num{51.276}$, and $p_0=\num{49.568}$. Of course the
+% $p_2=\num{8.811}$, $p_1=\num{36.015}$, and $p_0=\num{81.792}$. 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{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{11.396}$, $p_1=\num{3276.713}$, and $p_0=\num{6132.315}$.
+% 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}$.
% \end{table}
%
% \subsection{Examples}
@@ -797,7 +836,7 @@ and the derived files expkv.pdf
% \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.
+% way easier to code for the user.
% \begin{lstlisting}
% \makeatletter
% \ekvcSplitAndForward\sine\sine@
@@ -963,8 +1002,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.2}
-\def\ekvDate{2020-04-10}
+\def\ekvVersion{1.3}
+\def\ekvDate{2020-07-02}
% \end{macrocode}
% \end{macro}
%
@@ -1042,12 +1081,18 @@ and the derived files expkv.pdf
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[internal]{\ekv@ifblank@}
+% \begin{macro}[internal]{\ekv@ifblank,\ekv@ifblank@}
% The obvious test that can be based on an if-empty is if-blank, meaning a test
% checking whether the argument is empty or consists only of spaces. Our version
% here will be tweaked a bit, as we want to check this, but with one leading
-% |\ekv@mark| token that is to be ignored.
+% |\ekv@mark| token that is to be ignored. The wrapper |\ekv@ifblank| will not
+% be used by \expkv\ for speed reasons but \expkvo\ uses it.
% \begin{macrocode}
+\long\def\ekv@ifblank#1%
+ {%
+ \ekv@ifblank@#1\ekv@nil\ekv@ifempty@B\ekv@ifempty@true
+ \ekv@ifempty@A\ekv@ifempty@B\@secondoftwo
+ }
\long\def\ekv@ifblank@\ekv@mark#1{\ekv@ifempty@\ekv@ifempty@A}
% \end{macrocode}
% \end{macro}
@@ -1073,7 +1118,7 @@ and the derived files expkv.pdf
% \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{#2}}
+\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)}
% \end{macrocode}
@@ -1129,7 +1174,10 @@ and the derived files expkv.pdf
% \end{macro}
%
% \begin{macro}
-% {\ekvdef,\ekvdefNoVal,\ekvlet,\ekvletNoVal,\ekvletkv,\ekvletkvNoVal}
+% {
+% \ekvdef,\ekvdefNoVal,\ekvlet,\ekvletNoVal,\ekvletkv,\ekvletkvNoVal,
+% \ekvdefunknown,\ekvdefunknownNoVal
+% }
% Set up the key defining macros |\ekvdef| etc.
% \begin{macrocode}
\protected\long\def\ekvdef#1#2#3%
@@ -1137,7 +1185,7 @@ and the derived files expkv.pdf
\ekv@checkvalid{#1}{#2}%
{%
\expandafter\def\csname\ekv@name{#1}{#2}\endcsname##1{#3}%
- \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}%
+ \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}%
}%
}
\protected\long\def\ekvdefNoVal#1#2#3%
@@ -1145,7 +1193,7 @@ and the derived files expkv.pdf
\ekv@checkvalid{#1}{#2}%
{%
\expandafter\def\csname\ekv@name{#1}{#2}N\endcsname{#3}%
- \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}%
+ \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}%
}%
}
\protected\def\ekvlet#1#2#3%
@@ -1153,7 +1201,7 @@ and the derived files expkv.pdf
\ekv@checkvalid{#1}{#2}%
{%
\expandafter\let\csname\ekv@name{#1}{#2}\endcsname#3%
- \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}%
+ \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}%
}%
}
\protected\def\ekvletNoVal#1#2#3%
@@ -1161,7 +1209,7 @@ and the derived files expkv.pdf
\ekv@checkvalid{#1}{#2}%
{%
\expandafter\let\csname\ekv@name{#1}{#2}N\endcsname#3%
- \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}%
+ \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}%
}%
}
\protected\def\ekvletkv#1#2#3#4%
@@ -1170,7 +1218,7 @@ and the derived files expkv.pdf
{%
\expandafter\let\csname\ekv@name{#1}{#2}\expandafter\endcsname
\csname\ekv@name{#3}{#4}\endcsname
- \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}%
+ \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}%
}%
}
\protected\def\ekvletkvNoVal#1#2#3#4%
@@ -1179,21 +1227,37 @@ and the derived files expkv.pdf
{%
\expandafter\let\csname\ekv@name{#1}{#2}N\expandafter\endcsname
\csname\ekv@name{#3}{#4}N\endcsname
- \expandafter\ekv@defset\csname\ekv@undefined@set{#1}\endcsname{#1}%
+ \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}%
+ }%
+ }
+\protected\def\ekvdefunknown#1#2%
+ {%
+ \ekv@checkvalid{#1}{.}%
+ {%
+ \expandafter\def\csname\ekv@name{#1}{}u\endcsname##1##2{#2}%
+ \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}%
+ }%
+ }
+\protected\def\ekvdefunknownNoVal#1#2%
+ {%
+ \ekv@checkvalid{#1}{.}%
+ {%
+ \expandafter\def\csname\ekv@name{#1}{}uN\endcsname##1{#2}%
+ \expandafter\ekv@defsetmacro\csname\ekv@undefined@set{#1}\endcsname{#1}%
}%
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[internal]{\ekv@defset}
+% \begin{macro}[internal]{\ekv@defsetmacro}
% In order to enhance the speed the set name given to |\ekvset| will be turned
% into a control sequence pretty early, so we have to define that control
% sequence.
% \begin{macrocode}
-\protected\def\ekv@defset#1#2%
+\protected\def\ekv@defsetmacro#1#2%
{%
\ifx#1\relax
- \edef#1##1{\ekv@name@set{#2}\ekv@name@key{##1}}%
+ \edef#1##1{\ekv@name@set{#2}\ekv@name@key{\noexpand\detokenize{##1}}}%
\fi
}
% \end{macrocode}
@@ -1202,10 +1266,11 @@ and the derived files expkv.pdf
% \begin{macro}{\ekvset}
% Set up |\ekvset|, which should not be affected by active commas and equal
% signs. The equal signs are a bit harder to cope with and we'll do that later,
-% but replacing the active commas with commas of category other can be done
-% beforehand. That's why we define |\ekvset| here with a temporary meaning just
-% to set up the things with two different category codes. |#1| will be a
-% \texttt{,\textsubscript{13}} and |#2| will be a \texttt{=\textsubscript{13}}.
+% but the active commas can be handled by just doing two comma-splitting loops
+% one at actives one at others. That's why we define |\ekvset| here with a
+% temporary meaning just to set up the things with two different category codes.
+% |#1| will be a \texttt{,\textsubscript{13}} and |#2| will be a
+% \texttt{=\textsubscript{13}}.
% \begin{macrocode}
\def\ekvset#1#2{%
\endgroup
@@ -1597,8 +1662,9 @@ and the derived files expkv.pdf
% 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|.
+% 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{macrocode}
\long\def\ekv@set@pair#1#2\ekv@nil#3%
{%
@@ -1612,10 +1678,22 @@ and the derived files expkv.pdf
\endcsname
{#2}%
{%
- \ekv@ifdefined{#3{#1}N}%
- \ekv@err@noarg
- \ekv@err@unknown
- #3{#1}%
+ \expandafter\ekv@set@pair@
+ \csname
+ \ifcsname #3{}u\endcsname
+ #3{}u%
+ \else
+ relax%
+ \fi
+ \endcsname
+ {#2}%
+ {%
+ \ekv@ifdefined{#3{#1}N}%
+ \ekv@err@noarg
+ \ekv@err@unknown
+ #3%
+ }%
+ {#1}%
}%
}
\long\def\ekv@set@pair@#1#2%
@@ -1645,10 +1723,21 @@ and the derived files expkv.pdf
\fi
\endcsname
{%
- \ekv@ifdefined{#2{#1}}%
- \ekv@err@reqval
- \ekv@err@unknown
- #2{#1}%
+ \expandafter\ekv@set@key@
+ \csname
+ \ifcsname #2{}uN\endcsname
+ #2{}uN%
+ \else
+ relax%
+ \fi
+ \endcsname
+ {%
+ \ekv@ifdefined{#2{#1}}%
+ \ekv@err@reqval
+ \ekv@err@unknown
+ #2%
+ }%
+ {#1}%
}%
}
\def\ekv@set@key@#1%
@@ -1661,6 +1750,30 @@ and the derived files expkv.pdf
% \end{macrocode}
% \end{macro}
%
+% \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.
+% \begin{macrocode}
+\protected\def\ekvsetdef#1#2%
+ {%
+ \expandafter\expandafter\expandafter
+ \ekv@setdef\expandafter\expandafter\expandafter{\ekvset{#2}{##1}}#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{macrocode}
+\protected\def\ekv@setdef#1#2%
+ {%
+ \long\def#2##1{#1}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}[internal]{\ekv@err,\ekv@err@}
% Since |\ekvset| is fully expandable as long as the code of the keys is (which
% is unlikely) we want to somehow throw expandable errors, in our case via