summaryrefslogtreecommitdiff
path: root/macros/generic/expkv-cs/expkv-cs.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/generic/expkv-cs/expkv-cs.dtx')
-rw-r--r--macros/generic/expkv-cs/expkv-cs.dtx236
1 files changed, 213 insertions, 23 deletions
diff --git a/macros/generic/expkv-cs/expkv-cs.dtx b/macros/generic/expkv-cs/expkv-cs.dtx
index 88510651dc..c5325faee5 100644
--- a/macros/generic/expkv-cs/expkv-cs.dtx
+++ b/macros/generic/expkv-cs/expkv-cs.dtx
@@ -92,7 +92,7 @@ and the derived files expkv-cs.pdf
{\}}{{\CodeSymbol\}}}{1}
^^A,literate=*{<key>}{\key}{4}{<set>}{\set}{4}
}
-\newcommand*\CodeSymbol[1]{\textbf{#1}}
+\newcommand*\CodeSymbol[1]{\kern-1pt\textbf{#1}\kern1pt}
\RequirePackage{randtext}
\let\metaORIG\meta
\protected\def\meta #1{\texttt{\metaORIG{#1}}}
@@ -206,11 +206,11 @@ and the derived files expkv-cs.pdf
%
% \begin{abstract}
% \noindent\parfillskip=0pt
-% \expkvc\ provides two small interfaces to define expandable \kv\ macros
-% utilizing \expkv. It therefore lowers the entrance boundary to expandable \kv\
-% macros. The stylised name is \expkvc\ but the files use
-% \file{expkv-cs}, this is due to CTAN-rules which don't allow \string| in
-% package names since that is the pipe symbol in *nix shells.
+% \expkvc\ provides two small interfaces to define expandable \kv\ macros using
+% \expkv. It therefore lowers the entrance boundary to expandable \kv\ macros.
+% The stylised name is \expkvc\ but the files use \file{expkv-cs}, this is due
+% to CTAN-rules which don't allow \string| in package names since that is the
+% pipe symbol in *nix shells.
% \end{abstract}
%
% \tableofcontents
@@ -222,13 +222,19 @@ and the derived files expkv-cs.pdf
% The \expkv\ package enables the new possibility of creating \kv\ macros which
% are fully expandable. The creation of such macros is however cumbersome for
% the average user. \expkvc\ tries to step in here. It provides interfaces to
-% define \kv\ macros without worrying too much about the implementation. There
-% are two different approaches supported by this package. The first is splitting
-% the keys up into individual arguments, the second is providing all the keys as
-% a single argument to the underlying macro and getting an individual \val\ by
-% using a hash. Well, actually there is no real hash, just some markers which
-% are parsed, but this shouldn't be apparent to the user, the behaviour matches
-% that of a hash-table.
+% define \kv\ macros without worrying too much about the implementation. In case
+% you're wondering now, the |cs| in \expkvc\ stands for control sequence,
+% because |def| was already taken by \expkvd\ and ``control sequence'' is the
+% term D.\,E.\,Knuth used in his \TeX book for named commands hence macros
+% (though he also used the term ``macro''). So \expkvc\ defines control
+% sequences for and with \expkv.
+%
+% There are two different approaches supported by this package. The first is
+% splitting the keys up into individual arguments, the second is providing all
+% the keys as a single argument to the underlying macro and getting an
+% individual \val\ by using a hash. Well, actually there is no real hash, just
+% some markers which are parsed, but this shouldn't be apparent to the user, the
+% behaviour matches that of a hash-table.
%
% In addition to these two methods of defining a macro with primary keys a way
% to define secondary keys, which can reference the primary ones, is provided.
@@ -238,10 +244,11 @@ and the derived files expkv-cs.pdf
%
% A word of advice you should consider: If your macro doesn't have to be
% expandable (and often it doesn't) don't use \expkvc. The interface has some
-% overhead (though it still can be considered fast) and the approach has its
-% limits. If you don't need to be expandable, you should consider either
-% defining your keys manually using \expkv\ or using \expkvd\ for convenience.
-% Or you resort to another \kv\ interface.
+% overhead (though it still can be considered fast -- check \autoref{sec:speed})
+% and the approach has its limits in versatility. If you don't need to be
+% expandable, you should consider either defining your keys manually using
+% \expkv\ or using \expkvd\ for convenience. Or you resort to another \kv\
+% interface.
%
% \expkvc\ is usable as generic code and as a \LaTeX\ package. It'll
% automatically load \expkv\ in the same mode as well. To use it, just use one
@@ -373,6 +380,30 @@ and the derived files expkv-cs.pdf
% of expansion in the no-errors case.
% \end{function}
%
+% \begin{function}{\ekvcValueSplit}
+% \begin{syntax}
+% \cs{ekvcValueSplit}\{\key\}\marg{key list}\marg{next}
+% \end{syntax}
+% If you need a specific \key\ from a \meta{key list} more than once, it'll
+% be a good idea to only extract it once and from then on keep it as a
+% separate argument. Hence the macro \cs{ekvcValueSplit} will extract one
+% specific \key's value from the list and forward the remainder of the list
+% as the first and the \key's value as the second argument to \meta{next}, so
+% the result of this will be \meta{next}\marg{key list'}\marg{value} with
+% \meta{key list'} the remaining list. This is almost as fast as |\ekvcValue|
+% and runs the same tests. Keep in mind that you can't fetch for the same
+% \key\ again from \meta{key list'} as it got removed.
+% \end{function}
+%
+% \begin{function}{\ekvcValueSplitFast}
+% \begin{syntax}
+% \cs{ekvcValueSplitFast}\{\key\}\marg{key list}\marg{next}
+% \end{syntax}
+% This behaves just like |\ekvcValueSplit|, but it won't run the same tests,
+% hence it is faster but more error prone, just like the relation between
+% |\ekvcValue| and |\ekvcValueFast|.
+% \end{function}
+%
%
% \subsection{Secondary Keys}
%
@@ -528,9 +559,9 @@ and the derived files expkv-cs.pdf
% As one might see, the |lady| key could actually have been an |nmeta| key as
% well, as all that is done with the argument is using it as a \kv\ list.
%
-% Utilizing \pkg{xparse} and forwarding arguments one can easily define \kv\
-% macros with actual optional and mandatory arguments as well. A small nonsense
-% example (which should perhaps use |\ekvcSplitAndForward| instead of
+% Using \pkg{xparse} and forwarding arguments one can easily define \kv\ macros
+% with actual optional and mandatory arguments as well. A small nonsense example
+% (which should perhaps use |\ekvcSplitAndForward| instead of
% |\ekvcHashAndForward| since it only uses four keys and one other argument --
% and isn't expandable since it uses a \env{tabular} environment):
% \begin{lstlisting}
@@ -565,6 +596,80 @@ and the derived files expkv-cs.pdf
% \end{lstlisting}
%
%
+% \subsection{Speed Considerations}\label{sec:speed}
+%
+% As already mentioned in the introduction there are some speed considerations
+% implied if you choose to define macros via \expkvc. However the overhead isn't
+% the factor which should hinder you to use \expkvc\ if you found a reasonable
+% use case. The key-parsing is still faster than with most other \kv\ packages
+% (see the ``Comparisons'' subsection in the \expkv\ documentation).
+%
+% The speed considerations in this subsection use the first example in this
+% documentation as the benchmark. So we have seven keys and a short sentence
+% which should be typeset. For comparisons I use the following equivalent
+% \expkvd\ definitions. Each result is the average between changing no keys from
+% their initial values and altering four. Furthermore I'll compare three
+% variants of \expkvc\ with the \expkvd\ definitions, namely the split example
+% from above, a hash variant using |\ekvcValue| and a hash variant using
+% |\ekvcValueFast|.
+% \begin{lstlisting}
+% \usepackage{expkv-def}
+% \ekvdefinekeys{keys}
+% {%
+% ,store name = \KEYSname
+% ,initial name = John Doe
+% ,store age = \KEYSage
+% ,initial age = any
+% ,store nationality = \KEYSnationality
+% ,initial nationality = the Universe
+% ,store hobby = \KEYShobby
+% ,initial hobby = to exist
+% ,store type = \KEYStype
+% ,initial type = Mister
+% ,store pronoun = \KEYSpronoun
+% ,initial pronoun = He
+% ,store possessive = \KEYSpossessive
+% ,initial possessive = his
+% }
+% \newcommand*\KEYS[1]
+% {%
+% \begingroup
+% \ekvset{keys}{#1}%
+% \KEYSname\ is a \KEYStype\ from \KEYSnationality. \KEYSpronoun\ is
+% of \KEYSage\ age and \KEYSpossessive\ hobby is \KEYShobby.%
+% \endgroup
+% }
+% \end{lstlisting}
+%
+% The first comparison removes the typesetting part from all the definitions, so
+% that only the key parsing is compared. In this comparison the |\ekvcValue|
+% and |\ekvcValueFast| variants will not differ, as they are exactly the same
+% until the key usage. We find that the split approach is $1.4$ times slower
+% than the \expkvd\ setup and the hash variants end up in the middle at $1.17$
+% times slower.
+%
+% Next we put the typesetting part back in. Every call of the macros will
+% typeset the sentences into a box register in horizontal mode. With the
+% typesetting part (which includes the accessing of values) the fastest remains
+% the \expkvd\ definitions, but split is close at $1.16$ times slower, followed
+% by the hash variant with fast accesses at $1.36$ times slower, and the safe
+% hash access variant ranks in the slowest $1.8$ times slower than \expkvd.
+%
+% Just in case you're wondering now, a simple macro taking seven arguments is
+% $30$ to $40$ times faster than any of those in the argument grabbing and \kv\
+% parsing part and only $1.5$ to $2.8$ times faster if the typesetting part is
+% factored in. So the real choke isn't the parsing.
+%
+% So to summarize this, if you have a reasonable use case for expandable \kv\
+% parsing macros you should go on and define them using \expkvc. If you have a
+% reasonable use case for \kv\ parsing macros but defining them expandable isn't
+% necessary for your use you should take advantage of the greater flexibility of
+% non-expandable \kv\ setups (but if you're after maximum speed there aren't
+% that many \kv\ parsers beating \expkvc). And if you are after maximum
+% performance maybe ditching the \kv\ interface altogether is a good idea, but
+% depending on the number of arguments your interface might get convoluted.
+%
+%
% \subsection{Useless Macros}
%
% Perhaps these macros aren't completely useless, but I figured from a user's
@@ -661,8 +766,8 @@ and the derived files expkv-cs.pdf
% \begin{macro}{\ekvcVersion,\ekvcDate}
% We're on our first input, so lets store the version and date in a macro.
% \begin{macrocode}
-\def\ekvcVersion{0.1}
-\def\ekvcDate{2020-04-04}
+\def\ekvcVersion{0.2}
+\def\ekvcDate{2020-04-05}
% \end{macrocode}
% \end{macro}
%
@@ -1097,6 +1202,36 @@ and the derived files expkv-cs.pdf
{%
####4####2%
}%
+ \long\gdef\unexpanded\expandafter
+ {\csname ekvc@fastsplithash@#1\endcsname}%
+ ####1%
+ \unexpanded\expandafter{\csname ekvc@hashmark@#1\endcsname}%
+ ####2####3\unexpanded{\ekvc@stop}####4%
+ {%
+ ####4{####1####3}{####2}%
+ }%
+ \long\gdef\unexpanded\expandafter
+ {\csname ekvc@safesplithash@#1\endcsname}####1%
+ {%
+ \unexpanded\expandafter
+ {\csname ekvc@@safesplithash@#1\endcsname}%
+ ####1\unexpanded{\ekvc@mark\ekvc@safe@found@hash}%
+ \unexpanded\expandafter
+ {%
+ \csname ekvc@hashmark@#1\endcsname{}%
+ \ekvc@mark{\ekvc@err@missing@hash{#1}\ekvc@safe@no@hash}%
+ \ekvc@stop
+ }%
+ }%
+ \long\gdef\unexpanded\expandafter
+ {\csname ekvc@@safesplithash@#1\endcsname}%
+ ####1%
+ \unexpanded\expandafter{\csname ekvc@hashmark@#1\endcsname}%
+ ####2####3\unexpanded{\ekvc@mark}####4####5%
+ \unexpanded{\ekvc@stop}%
+ {%
+ ####4{####2}####1####3\unexpanded{\ekvc@stop}%
+ }%
}%
\ekvc@tmp
\endgroup
@@ -1128,7 +1263,60 @@ and the derived files expkv-cs.pdf
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[internal]{\ekvc@safehash@,\ekvc@fasthash@}
+% \begin{macro}{\ekvcValueSplit}
+% This splits off a single version
+% \begin{macrocode}
+\long\def\ekvcValueSplit#1#2#3%
+ {%
+ \ekv@ifdefined{ekvc@safesplithash@#1}%
+ {\csname ekvc@safesplithash@#1\endcsname{#2}{#3}}%
+ {\ekvc@err@unknown@hash{#1}#3{}{#2}}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekvc@safe@found@hash, \ekvc@safe@no@hash}
+% \begin{macrocode}
+\long\def\ekvc@safe@found@hash#1#2\ekvc@stop#3%
+ {%
+ #3{#2}{#1}%
+ }
+\long\def\ekvc@safe@no@hash#1#2\ekvc@mark\ekvc@safe@found@hash\ekvc@stop#3%
+ {%
+ #3{#2}{}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvcValueSplitFast}
+% Again a fast approach which doesn't provide too many safety measurements.
+% This needs to build the hash function and expand it before passing the
+% results to the next control sequence. The first step only builds the control
+% sequence.
+% \begin{macrocode}
+\long\def\ekvcValueSplitFast#1#2%
+ {%
+ \csname ekvc@fastsplithash@#1\endcsname#2\ekvc@stop
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekvcValueSplitFast@a}
+% This step then expands the hash function once and passes the result to |#3|
+% which should be a single control sequence.
+% \begin{macrocode}
+\long\def\ekvcValueSplitFast@#1#2#3%
+ {%
+ \expandafter#3\expandafter{#1#2\ekvc@stop}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]
+% {
+% \ekvc@safehash@,\ekvc@fasthash@,
+% \ekvc@safesplithash@,\ekvc@fastsplithash@
+% }
% At least in the empty hash case we can provide a meaningful error message
% without affecting performance by just defining the macro that would be build
% in that case. There is of course a downside to this, the error will not be
@@ -1137,6 +1325,8 @@ and the derived files expkv-cs.pdf
% \begin{macrocode}
\long\def\ekvc@safehash@#1{\ekvc@err@empty@hash\@gobble{} }% keep this space
\long\def\ekvc@fasthash@#1\ekvc@stop{\ekvc@err@empty@hash}
+\long\def\ekvc@safesplithash@#1#2{\ekvc@err@empty@hash#2{#1}{}}
+\long\def\ekvc@fastsplithash@#1\ekvc@stop#2{\ekvc@err@empty@hash#2{#1}{}}
% \end{macrocode}
% \end{macro}
%