diff options
author | Karl Berry <karl@freefriends.org> | 2021-06-20 19:56:27 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-06-20 19:56:27 +0000 |
commit | 2347cb3e8bc2c9276574d14461f4429c6b3c1bd4 (patch) | |
tree | eb091127516eb112f601cab0c919848e8b6beb2e /Master | |
parent | 0e008fd431955e2802ab0abb5220e3bfb78256e6 (diff) |
expkv-cs (20jun21)
git-svn-id: svn://tug.org/texlive/trunk@59655 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master')
-rw-r--r-- | Master/texmf-dist/doc/latex/expkv-cs/README.md | 2 | ||||
-rw-r--r-- | Master/texmf-dist/doc/latex/expkv-cs/expkv-cs.pdf | bin | 361934 -> 432515 bytes | |||
-rw-r--r-- | Master/texmf-dist/source/latex/expkv-cs/expkv-cs.dtx | 753 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/expkv-cs/expkv-cs.tex | 151 |
4 files changed, 775 insertions, 131 deletions
diff --git a/Master/texmf-dist/doc/latex/expkv-cs/README.md b/Master/texmf-dist/doc/latex/expkv-cs/README.md index 89ab6b6de69..a3f1a6067e9 100644 --- a/Master/texmf-dist/doc/latex/expkv-cs/README.md +++ b/Master/texmf-dist/doc/latex/expkv-cs/README.md @@ -1,7 +1,7 @@ ------------------------------------------------------------------------------- # expkv-cs -- define expandable key=val macros using expkv -Version 2021-06-03 v0.10 +Version 2021-06-20 v1.0 Released under the LaTeX Project Public License v1.3c or later See http://www.latex-project.org/lppl.txt diff --git a/Master/texmf-dist/doc/latex/expkv-cs/expkv-cs.pdf b/Master/texmf-dist/doc/latex/expkv-cs/expkv-cs.pdf Binary files differindex 1e19349f76e..b5d42c197ba 100644 --- a/Master/texmf-dist/doc/latex/expkv-cs/expkv-cs.pdf +++ b/Master/texmf-dist/doc/latex/expkv-cs/expkv-cs.pdf diff --git a/Master/texmf-dist/source/latex/expkv-cs/expkv-cs.dtx b/Master/texmf-dist/source/latex/expkv-cs/expkv-cs.dtx index 55a6a134aa4..599750404a6 100644 --- a/Master/texmf-dist/source/latex/expkv-cs/expkv-cs.dtx +++ b/Master/texmf-dist/source/latex/expkv-cs/expkv-cs.dtx @@ -86,7 +86,7 @@ and the derived files expkv-cs.pdf \RequirePackage{caption} \RequirePackage{microtype} \RequirePackage{accsupp} -\lstdefinelanguage{expkvd}[primitive]{TeX} +\lstdefinelanguage{expkvc}[primitive]{TeX} { ,moretexcs=[4]^^A e-TeX { @@ -124,13 +124,15 @@ and the derived files expkv-cs.pdf ekvcHash,ekvcHashAndForward,ekvcHashAndUse, ekvcValue,ekvcValueFast,ekvcValueSplit,ekvcValueSplitFast, ekvcSecondaryKeys, + ekvcChange, ekvcFlagNew, ekvcFlagHeight, ekvcFlagRaise, ekvcFlagSetTrue,ekvcFlagSetFalse, ekvcFlagIf,ekvcFlagIfRaised, ekvcFlagReset, - ekvcFlagGetHeight,ekvcFlagGetHeights + ekvcFlagGetHeight,ekvcFlagGetHeights, + ekvcPass } ,moretexcs=[3]^^A expkv-pkg macros { @@ -157,7 +159,7 @@ and the derived files expkv-cs.pdf \def\mylstwd{.55em} \lstset { - ,language=expkvd + ,language=expkvc ,flexiblecolumns=false ,basewidth=\mylstwd ,gobble=2 @@ -214,6 +216,7 @@ and the derived files expkv-cs.pdf \definecolor{expkvred}{HTML}{9F393D} \colorlet{expkvgrey}{black!75} \makeatletter +\newcommand*\example{\par\smallskip\noindent\textit{Example:} \ignorespaces} \newcommand*\expkv {^^A \texorpdfstring @@ -282,9 +285,11 @@ and the derived files expkv-cs.pdf \DoNotIndex{\protected,\ProvidesFile,\ProvidesPackage,\relax,\space,\advance} \DoNotIndex{\@,\unexpanded,\string,\expanded,\dimexpr,\global,\glueexpr,\hbox} \DoNotIndex{\numexpr,\RequirePackage,\setbox,\the,\unless,\xdef,\gdef,\newcount} -\DoNotIndex{\^,\@firstofone,\@firstoftwo,\@gobble} +\DoNotIndex{\number,\detokenize,\meaning} +\DoNotIndex{\^,\@firstofone,\@firstoftwo,\@gobble,\@secondoftwo} \DoNotIndex{\ifcsname} \DoNotIndex{\ifx} +\DoNotIndex{\ifodd} \DoNotIndex{\ifnum} \DoNotIndex{\ifdefined} \DoNotIndex{\iffalse} @@ -296,6 +301,7 @@ and the derived files expkv-cs.pdf \@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi \@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi \@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi +\@gobble\fi ^^A ignoring \ifx, \ifcsname, etc., but only one \fi \@ifdefinable\gobbledocstriptag{\def\gobbledocstriptag#1>{}} \makeatother \begin{document} @@ -357,12 +363,14 @@ and the derived files expkv-cs.pdf % has its own set of primary and secondary keys. % % 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 -- check \autoref{sec:speed}) -% and the approach has its limits in versatility. If you don't need to be +% expandable (and often it doesn't) consider not using \expkvc. The interface +% has some overhead (though it still is 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. +% interface. Nevertheless setting up macros with \expkvc, especially with +% |\ekvcSplit|, is very convenient in my opinion, so if you just want to define +% a single macro with just a few keys this might be the way to go. % % \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,13 +381,14 @@ and the derived files expkv-cs.pdf % \end{lstlisting} % % -% \subsection{Define Macros and Primary Keys} +% \subsection{Define Macros and Primary Keys}\label{sec:define} % % All macros defined with \expkvc\ have to be previously undefined or have the -% |\meaning| of |\relax|. This is necessary as there is no way to undefine keys -% once they are set up (neither \expkv\ nor \expkvc\ keep track of defined -% keys) -- so to make sure there are no conflicts only new definitions are -% allowed (that's not the case for individual keys, only for frontend macros). +% |\meaning| of |\relax|. This is necessary as there is no way to automatically +% undefine keys once they are set up (neither \expkv\ nor \expkvc\ keep track of +% defined keys) -- so to make sure there are no conflicts only new definitions +% are allowed (that's not the case for individual keys, only for frontend +% macros). % % % \subsubsection{Primary Keys}\label{sec:primaries} @@ -389,11 +398,36 @@ and the derived files expkv-cs.pdf % primary key and \val\ the associated initial value. By default all keys are % defined short, but you can define long keys by prefixing \key\ with |long| % (\emph{e.g.}, \texttt{long name=Jonathan P. Spratte}). You only need |long| if -% the key should be able to take a |\par| token. Note however that |long| keys -% are a microscopic grain faster (due to some internals of \expkvc). Only if at -% least one of the keys was |long| the \meta{cs} in the following defining -% macros will be |\long|. For obvious reasons there is no possibility to define -% a macro or key as |\protected|. +% the key should be able to take an explicit |\par| token. Note however that +% |long| keys are a microscopic grain faster (due to some internals of \expkvc). +% Only if at least one of the keys was |long| the \meta{cs} in the following +% defining macros will be |\long|. For obvious reasons there is no possibility +% to define a macro or key as |\protected|. +% +% To allow keys not defined |long| which key names should start with |long|, you +% can also use the prefix |short| (|short| and |long| are mutually exclusive, +% which ever comes first defines the behaviour, the latter is considered part of +% the key name). Note that this is the only reason for |short|'s existance, +% essentially it does nothing. +% +% The consequence culminates in the following: +% \begin{lstlisting} +% \ekvcSplit\foo +% { +% long short = abc\par +% ,short long = def +% } +% {#1#2} +% \end{lstlisting} +% will define a macro |\foo| that knows two primary keys, the first one is +% called |short|, and accepts explicit |\par| tokens inside its values, the +% second one is called |long| and will not accept |\par| tokens (leading to a +% low level \TeX\ error). A description of |\ekvcSplit| follows shortly. +% +% There is one exception to these syntax rules of \meta{primary keys}: One can +% include a key named |...| without a value. If this is found the \meta{cs} will +% be defined |\long|. Any unknown keys found at use time will end up in a list +% at this spot. See some examples in \autoref{sec:unknown}. % % At the moment \expkvc\ doesn't require any internal keys, but I can't foresee % whether this will be the case in the future as well, as it might turn out that @@ -420,6 +454,20 @@ and the derived files expkv-cs.pdf % key defined first). With |\ekvcSplit| you can define macros using at most % nine primary keys. % \end{function} +% \example The following defines a macro |\foo| that takes the keys |a| and |b| +% and outputs their values in a textual form:\\ +% \begin{minipage}{.75\linewidth} +% \begin{lstlisting} +% \ekvcSplit\foo{a=a,b=b}{a is #1.\par b is #2.\par} +% \foo{} +% \foo{b=e} +% \end{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} +% \ekvcSplit\foo{a=a,b=b}{a is #1.\par b is #2.\par} +% \foo{} +% \foo{b=e} +% \end{exresult} % % \begin{function}{\ekvcSplitAndForward} % \begin{syntax} @@ -439,6 +487,9 @@ and the derived files expkv-cs.pdf % \ekvcSplitAndForward\foo{\foo@aux{more args}}{keyA = A, keyB = B} % \end{lstlisting} % \end{function} +% \noindent +% In the first case |\foo@aux| should take at least two arguments (|keyA| and +% |keyB|), in the second case at least three (|more args|, |keyA|, and |keyB|). % % \begin{function}{\ekvcSplitAndUse} % \begin{syntax} @@ -448,20 +499,23 @@ and the derived files expkv-cs.pdf % specifying what will be used after splitting the keys, \meta{cs} will use % what follows the \kv\ list. So its syntax will be % \end{function} -% \begin{syntax} -% \meta{cs}\{\key=\val, \ldots\}\marg{after} -% \end{syntax} +% \begin{quote} +% \ttfamily +% \meta{cs}\{\key=\val, \ldots\}\marg{after} +% \end{quote} +% and the code in |after| should expect at least as many arguments as the number +% of keys defined for \meta{cs}. % % \subsubsection{Hash} % % The hash variants will provide the key values as a single argument in which % you can access specific values using a special macro. The implementation might -% be more convenient and scale better, \emph{but} it is much slower (for a -% primitive macro with a single key benchmarking was almost $1.7$ times slower, -% the root of which being the key access with |\ekvcValue|, not the parsing, and -% for a key access using |\ekvcValueFast| it was still about $1.2$ times -% slower). So if your macro uses less than ten primary keys, you should most -% likely use the split approach. +% be more convenient and scale better, \emph{but} it is slower (for a +% rudimentary macro with a single key benchmarking was almost $1.7$ times +% slower, the root of which being the key access with |\ekvcValue|, not the +% parsing, and for a key access using |\ekvcValueFast| it was still about $1.2$ +% times slower). So if your macro uses less than ten primary keys, you should +% consider using the split approach. % % \begin{function}{\ekvcHash} % \begin{syntax} @@ -474,6 +528,22 @@ and the derived files expkv-cs.pdf % \meta{definition}, in which you can access the \val\ of a \key\ by using % \texttt{\cs[no-index]{ekvcValue}\{\key\}\{\#1\}}. % \end{function} +% \example This defines an equivalent macro to the |\foo| defined with +% |\ekvcSplit| earlier:\\ +% \begin{minipage}{.75\linewidth} +% \begin{lstlisting} +% \ekvcHash\foo{a=a,b=b}{a is \ekvcValue{a}{#1}.\par +% b is \ekvcValue{b}{#1}.\par} +% \foo{} +% \foo{b=e} +% \end{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} +% \ekvcHash\foo{a=a,b=b}{a is \ekvcValue{a}{#1}.\par +% b is \ekvcValue{b}{#1}.\par} +% \foo{} +% \foo{b=e} +% \end{exresult} % % \begin{function}{\ekvcHashAndForward} % \begin{syntax} @@ -489,18 +559,26 @@ and the derived files expkv-cs.pdf % \texttt{\cs[no-index]{ekvcValue}\{\key\}\{\#1\}} (or whichever argument the % hashed \kv\ list will be). % \end{function} +% \example This defines a macro |\foo| processing two keys, and passing the +% result to |\foobar|: +% \begin{lstlisting} +% \ekvcHashAndForward\foo\foobar{a=a,b=b} +% \newcommand*\foobar[1]{a is \ekvcValue{a}{#1}.\par +% b is \ekvcValue{b}{#1}.\par} +% \end{lstlisting} % % \begin{function}{\ekvcHashAndUse} % \begin{syntax} % \cs{ekvcHashAndUse}\meta{cs}\marg{primary keys} % \end{syntax} % This will roughly do the same as |\ekvcHashAndForward|, but instead of -% specifying what will be used after hashing the keys, \meta{cs} will use what -% follows the \kv\ list. So its syntax will be +% specifying what will be used after hashing the keys at install time, +% \meta{cs} will use what follows the \kv\ list. So its syntax will be % \end{function} -% \begin{syntax} -% \meta{cs}\{\key=\val, \ldots\}\marg{after} -% \end{syntax} +% \begin{quote} +% \ttfamily +% \meta{cs}\{\key=\val, \ldots\}\marg{after} +% \end{quote} % % \begin{function}{\ekvcValue} % \begin{syntax} @@ -525,9 +603,9 @@ and the derived files expkv-cs.pdf % a result this is about $1.4$ times faster \emph{but} will throw low level % \TeX\ errors eventually if the hash function isn't defined or the \key\ % isn't part of the \meta{key list} (\emph{e.g.}, because it was defined as a -% key for another macro -- all macros share the same hash function per \key). -% Use it if you know what you're doing. This macro needs exactly three steps -% of expansion in the no-errors case. +% key for another macro -- all macros share the same hash function per \key\ +% name). Use it if you know what you're doing. This macro needs exactly three +% steps of expansion in the no-errors case. % \end{function} % % \begin{function}{\ekvcValueSplit} @@ -537,13 +615,34 @@ and the derived files expkv-cs.pdf % 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. +% specific \key's value from the list and forward it as an argument to +% \meta{next}, so the result +% of this will be \meta{next}\marg{value}. This is roughly as fast as +% |\ekvcValue| and runs the same tests. % \end{function} +% \example The following defines a macro |\foo| which will take three keys. +% Since the next parsing step will need the value of one of the keys multiple +% times we split that key off the list (in this example the next step doesn't +% use the key multiple times for simplicity though), and the entire list is +% forwarded as the second argument:\\ +% \begin{minipage}{.75\linewidth} +% \begin{lstlisting} +% \ekvcHash\foo{a=a,b=b,c=c} +% {\ekvcValueSplit{a}{#1}\foobar{#1}} +% \newcommand*\foobar[2]{a is #1.\par +% b is \ekvcValue{b}{#2}.\par +% c is \ekvcValue{c}{#2}.\par} +% \foo{} +% \end{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} +% \ekvcHash\foo{a=a,b=b,c=c} +% {\ekvcValueSplit{a}{#1}\foobar{#1}} +% \newcommand*\foobar[2]{a is #1.\par +% b is \ekvcValue{b}{#2}.\par +% c is \ekvcValue{c}{#2}.\par} +% \foo{} +% \end{exresult} % % \begin{function}{\ekvcValueSplitFast} % \begin{syntax} @@ -555,7 +654,7 @@ and the derived files expkv-cs.pdf % \end{function} % % -% \subsection{Secondary Keys} +% \subsection{Secondary Keys}\label{sec:secondaries} % % To remove some of the limitations with the approach that each primary key % matches an argument or hash entry, you can define secondary keys. Those have @@ -641,8 +740,8 @@ and the derived files expkv-cs.pdf % If \key\ is a defined value taking key, you can define a |NoVal| version % with this that will behave as if \key\ was given \meta{default} as its % \val. Note that this doesn't change the initial values of primary keys set -% at definition time in |\ekvcSplit| and friends. \key\ can be a primary or -% secondary key. +% at definition time in |\ekvcSplit| and friends (see |\ekvcChange| in +% \autoref{sec:change} for this). \key\ can be a primary or secondary key. % \end{function} % % \begin{function}{flag-bool} @@ -681,6 +780,167 @@ and the derived files expkv-cs.pdf % \end{function} % % +% \subsection{Changing the Initial Values}\label{sec:change} +% +% \begin{function}{\ekvcChange} +% \begin{syntax} +% \cs{ekvcChange}\meta{cs}\{\kv,\ldots\} +% \end{syntax} +% This processes the \kv\ list for the macro \meta{cs} to set new defaults for +% it (meaning the values used if you don't provide anything at use time, not +% those specified with the |default| secondary key). \meta{cs} should be +% defined with \expkvc\ (so with any of the methods in \autoref{sec:define}). +% Inside the \kv\ list both primary and secondary keys can be used. If +% \meta{cs} was defined |\long| earlier it will still be |\long|, every other +% \TeX\ prefix will be stripped (but \expkvc\ doesn't support them anywhere +% else so that should be fine). The resulting new defaults will be stored +% inside the \meta{cs} locally (just as the original defaults were). If there +% was an unknown key forwarding added to \meta{cs} (see \autoref{sec:unknown}) +% any unknown key will be stored inside the list of unknown keys as well. +% |\ekvcChange| is not expandable! +% \end{function} +% +% Consider the following example:\\ +% \begin{minipage}[c]{.75\linewidth} +% \begin{lstlisting} +% \ekvcSplit\foo{a=a,b=b}{a is #1.\par b is #2.\par} +% \begingroup +% \ekvcChange\foo{b=B} +% \foo{} % new defaults +% \ekvcSecondaryKeys\foo{meta c={a={#1},b={#1}}} +% \ekvcChange\foo{c=c} +% \foo{} % newer defaults +% \endgroup +% \foo{} % initial defaults +% \end{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} +% \ekvcSplit\foo{a=a,b=b}{a is #1.\par b is #2.\par} +% \begingroup +% \ekvcChange\foo{b=B} +% \foo{} +% \ekvcSecondaryKeys\foo{meta c={a={#1},b={#1}}} +% \ekvcChange\foo{c=c} +% \foo{} +% \endgroup +% \foo{} +% \end{exresult} +% +% As a result with this the typical setup macro could be implemented: +% \begin{lstlisting} +% \ekvcHashAndUse\foo{key=a,key=b} +% \newcommand*\foosetup{\ekvcChange\foo} +% \end{lstlisting} +% Of course the usage is limited to a single macro |\foo|, hence this might not +% be as powerful as similar macros used with other \kv\ interfaces. But at least +% a few similar macros could be grouped using the same key parsing macro +% internally. +% +% +% \subsection{Handling Unknown Keys}\label{sec:unknown} +% +% If your macro should handle unknown keys without directly throwing an error +% you can use the special |...| marker in the \meta{primary keys} list. Since +% those keys will be processed once by \expkv\ they will be forwarded +% normalised: The key name will be the result of one |\detokenize|, the value +% will be forwarded as \verb*| {|\meta{value}\verb*|} |, so with spaces around +% one set of braces (this way, most other \kv\ implementations should parse the +% correct input). +% +% The exact behaviour differs slightly between the two variants (as all keys +% do). The behaviour inside the split variants will be similar to normal primary +% keys, the $n$-th argument (corresponding to the position of |...| inside the +% primary keys list) will contain any unknown key encountered while parsing the +% argument. And inside the split variant you can use a primary key named |...| +% at the same time. +% +% \example The following will forward any unknown key to |\includegraphics| to +% control the appearance while processing its own keys:\\ +% \begin{minipage}{.6\linewidth} +% \begin{lstlisting} +% \newcommand*\foo{\ekvoptarg\fooKV{}} +% \ekvcSplitAndForward\fooKV\fooOUT +% { +% a=a +% ,... +% ,b=b +% ,...={} +% } +% \newcommand\fooOUT[5] +% {% +% a is #1 and b is #3.\par +% \includegraphics[{#2}]{#5}\par +% \texttt{...} is #4.\par +% } +% \foo[width=.5\linewidth, b=c, +% ...={a stupid key name, but works}] +% {example-image-duck} +% \end{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.4\linewidth} +% \newcommand*\foo{\ekvoptarg\fooKV{}} +% \ekvcSplitAndForward\fooKV\fooOUT +% { +% a=a +% ,... +% ,b=b +% ,...={} +% } +% \newcommand\fooOUT[5] +% {% +% a is #1 and b is #3.\par +% \includegraphics[{#2}]{#5}\par +% \texttt{...} is #4.\par +% } +% \foo[width=.5\linewidth, b=c, +% ...={a stupid key name, but works}] +% {example-image-duck} +% \end{exresult} +% +% Inside the hash variants the unknown keys list will be put inside the hash +% named |...| (we have to use some name, so why not this). As a consequence a +% primary key named |...| would clash with the unknown key handler. If you still +% used such a key it would remove any unknown key stored there until that point +% and replace the list with its value. +% +% \example The following is more or less equivalent to the example above, but +% with the hash variant, and it will not contain the primary |...| key. We have +% to make sure that |\includegraphics| sees the \kv\ list, so need to expand +% |\ekvcValue{...}{#1}| before |\includegraphics| parses it.\\ +% \begin{minipage}{.76\linewidth} +% \begin{lstlisting} +% \newcommand*\foo{\ekvoptarg\fooKV{}} +% \ekvcHashAndForward\fooKV\fooOUT +% {a=a, b=b, ...} +% \newcommand\fooOUT[2] +% {% +% a is \ekvcValue{a}{#1} and +% b is \ekvcValue{b}{#1}.\par +% \expanded{\noexpand\includegraphics +% [{\ekvcValue{...}{#1}}]} +% {#2}\par +% } +% \foo[width=\linewidth, b=c] +% {example-image-duck-portrait} +% \end{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.24\linewidth} +% \newcommand*\foo{\ekvoptarg\fooKV{}} +% \ekvcHashAndForward\fooKV\fooOUT +% {a=a, b=b, ...} +% \newcommand\fooOUT[2] +% {% +% a is \ekvcValue{a}{#1} and +% b is \ekvcValue{b}{#1}.\par +% \expanded{\noexpand\includegraphics +% [{\ekvcValue{...}{#1}}]} +% {#2}\par +% } +% \foo[width=\linewidth, b=c] +% {example-image-duck-portrait} +% \end{exresult} +% +% % \subsection{Flags}\label{sec:flags} % % The idea of flags is taken from \pkg{expl3}. They provide a way to store @@ -693,7 +953,8 @@ and the derived files expkv-cs.pdf % % The state of flags is always changed locally to the current group, but not to % the current macro, so if you're using one of the |t|-types involving flags -% bear in mind that they can affect other macros at the current group level! +% bear in mind that they can affect other macros using the same flags at the +% current group level! % % \expkvc\ provides some macros to access, alter, and use flags. Flags of % \expkvc\ don't share a name space with the flags of \pkg{expl3}. @@ -706,7 +967,7 @@ and the derived files expkv-cs.pdf % whether the macro \meta{flag} is currently undefined. A \meta{flag} will % expand to the flag's current height with a trailing space (so you can use it % directly with |\ifnum| for example and it will terminate the number scanning -% itself). +% on its own). % \end{function} % % All other macros dealing with flags take as a parameter a macro defined as a @@ -791,26 +1052,26 @@ and the derived files expkv-cs.pdf % \end{function} % % -% \subsection{Example} +% \subsection{Further Examples}\label{sec:examples} % -% How could a documentation be a good documentation without some basic examples? -% Say we want to define a small macro expanding to some character description -% (who knows why this has to be expandable?). A character description will not -% have too many items to it, so we use |\ekvcSplit|. +% How could a documentation be a good documentation without enough basic +% examples? Say we want to define a small macro expanding to some character +% description (who knows why this has to be expandable?). A character +% description will not have too many items to it, so we use |\ekvcSplit| (the +% comments with the parameter numbers are of course not necessary and just ease +% reading the example). % \begin{lstlisting} % \ekvcSplit\character % { -% name=John Doe, -% age=any, -% nationality=the Universe, -% hobby=to exist, -% type=Mister, -% pronoun=He, -% possessive=his, -% } -% {% -% #1 is a #5 from #3. #6 is of #2 age and #7 hobby is #4.\par +% name=John Doe, % #1 +% age=any, % #2 +% nationality=the Universe, % #3 +% hobby=to exist, % #4 +% type=Mister, % #5 +% pronoun=He, % #6 +% possessive=his, % #7 % } +% {#1 is a #5 from #3. #6 is of #2 age and #7 hobby is #4.\par} % \end{lstlisting} % Also we want to give some short cuts so that it's easier to describe several % persons. @@ -854,7 +1115,7 @@ 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. % -% The result of all the first two usages would be: +% The result of only the first two usages would be: % \begin{exresult}{\linewidth} % \ekvcSplit\character % { @@ -898,7 +1159,9 @@ and the derived files expkv-cs.pdf % 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): +% uses a \env{tabular} environment, so it would've been better to use a more +% feature rich \kv\ interface most likely, \emph{e.g.}, the one provided by +% \expkvd): % \begin{lstlisting} % \makeatletter % \newcommand*\nonsense{\ekvoptarg\nonsense@a{}} @@ -957,6 +1220,70 @@ and the derived files expkv-cs.pdf % \end{exresult} % % +% \subsection{Freedom for Keys!} +% +% If this was the \TeX book this subsection would have a double bend sign. Not +% because it is overly complicated, but because it shows things which could +% break \expkvc's expandability and its alignment safety. This is for +% experienced users wanting to get the most flexibility and knowing what they +% are doing. +% +% In case you're wondering, it is possible to define other keys than the +% primaries and the secondary types listed in \autoref{sec:secondaries} for a +% macro defined with \expkvc\ by using the low-level interface of \expkv\ or +% even the interface provided by \expkvd. The set name used for \expkvc's keys +% is the macro name, including the leading backslash, or more precisely +% |\string|\meta{cs} is used. This can be exploited to define additional keys +% with arbitrary code. Consider the following \emph{bad} example: +% \begin{lstlisting} +% \ekvcSplit\foo{a=A,b=B}{a is #1.\par b is #2\par} +% \protected\ekvdef{\string\foo}{c}{\def\fooC{#1}} +% \end{lstlisting} +% This would define a key named |c| that will store its value inside a macro. +% The issue with this is that this can't be done expandably. As a result, the +% macro |\foo| isn't always expandable any more (not that bad if this was never +% required; killjoy if it was) and as soon as the key |c| is used, it is also no +% longer alignment safe\footnote{This means that the \kv-list can't contain +% alignment markers that are not inside an additional set of braces if used +% inside a \TeX\ alignment.} +% (might be bad depending on the usage). +% +% So why do I show you this? Because we could as well do something useful like +% create a key that pre-parses the input and after that passes the parsed value +% on. This parsing would have to be completely expandable though. For the +% pass-on part we can use the following function: +% +% \begin{function}{\ekvcPass} +% \begin{syntax} +% \cs{ekvcPass}\meta{cs}\marg{key}\marg{value} +% \end{syntax} +% This passes \meta{value} on to \meta{key} for the \expkvc-macro \meta{cs}. +% It should be used inside the key parsing of a macro defined with \expkvc, +% else this most likely results in a low level \TeX\ error. You can't forward +% anything to the special unknown key handler |...| as that is no defined key. +% \end{function} +% +% With this we could for example split the value of a key at a hyphen and pass +% the parts to different keys:\\ +% \begin{minipage}[c]{.75\linewidth} +% \begin{lstlisting} +% \ekvcSplit\foo{a=A,b=B}{a is #1.\par b is #2.\par} +% \ekvdef{\string\foo}{c}{\fooSplit#1\par} +% \def\fooSplit#1-#2\par +% {\ekvcPass\foo{a}{#1}\ekvcPass\foo{b}{#2}} +% \foo{} +% \foo{c=1-2} +% \end{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} +% \ekvcSplit\foo{a=A,b=B}{a is #1.\par b is #2.\par} +% \ekvdef{\string\foo}{c}{\fooSplit#1\par} +% \def\fooSplit#1-#2\par{\ekvcPass\foo{a}{#1}\ekvcPass\foo{b}{#2}} +% \foo{} +% \foo{c=1-2} +% \end{exresult} +% +% % \subsection{Speed Considerations}\label{sec:speed} % % As already mentioned in the introduction there are some speed considerations @@ -965,13 +1292,13 @@ and the derived files expkv-cs.pdf % 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 +% The speed considerations in this subsection use the first example of +% \autoref{sec:examples} 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} @@ -996,8 +1323,9 @@ and the derived files expkv-cs.pdf % {% % \begingroup % \ekvset{keys}{#1}% -% \KEYSname\ is a \KEYStype\ from \KEYSnationality. \KEYSpronoun\ is -% of \KEYSage\ age and \KEYSpossessive\ hobby is \KEYShobby.% +% \KEYSname\ is a \KEYStype\ from \KEYSnationality. +% \KEYSpronoun\ is of \KEYSage\ age and +% \KEYSpossessive\ hobby is \KEYShobby.% % \endgroup % } % \end{lstlisting} @@ -1021,14 +1349,16 @@ and the derived files expkv-cs.pdf % 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. +% So to summarise this, if you have a use case for expandable \kv\ +% parsing macros you should go on and define them using \expkvc. If you just +% want to define a simple macro with a few keys |\ekvcSplit| might be the +% easiest interface there is. 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} @@ -1127,8 +1457,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.10} -\def\ekvcDate{2021-06-03} +\def\ekvcVersion{1.0} +\def\ekvcDate{2021-06-20} % \end{macrocode} % \end{macro} % @@ -1148,6 +1478,14 @@ and the derived files expkv-cs.pdf % |\ekvc@tmp| will be reused later, but we don't need it to ever store % information long-term after \expkvc\ was initialized. % +% \begin{macro}[internal]{\ekvc@tripledots} +% This macro just serves as a marker for a comparison to allow the syntax +% for the unknown key handlers. +% \begin{macrocode} +\def\ekvc@tripledots{...} +% \end{macrocode} +% \end{macro} +% % \begin{macro}[internal]{\ekvc@keycount} % We'll need to keep count how many keys must be defined for each macro in the % |split| variants. @@ -1334,7 +1672,8 @@ and the derived files expkv-cs.pdf % \begin{macro}[internal] % { % \ekvc@SetupSplitKeys, \ekvc@SetupSplitKeys@a, \ekvc@SetupSplitKeys@b, -% \ekvc@SetupSplitKeys@c +% \ekvc@SetupSplitKeys@c, +% \ekvc@SetupSplitKeys@check@unknown,\ekvc@SetupSplitKeys@unknown % } % These macros parse the list of keys and set up the key macros. First we need % to initialise some macros and start |\ekvparse|. @@ -1344,7 +1683,7 @@ and the derived files expkv-cs.pdf \ekvc@keycount=0 \let\ekvc@any@long\ekv@empty \let\ekvc@initials\ekv@empty - \ekvparse\ekvc@err@value@required\ekvc@SetupSplitKeys@a + \ekvparse\ekvc@SetupSplitKeys@check@unknown\ekvc@SetupSplitKeys@a } % \end{macrocode} % Then we need to step the key counter for each key. Also we have to check @@ -1421,6 +1760,47 @@ and the derived files expkv-cs.pdf {}% } % \end{macrocode} +% If no value was provided this could either be an error, or the unknown key +% forwarding. We have to check this (comparing against |...| inside +% |\ekvc@tripledots|) and if this is the unknown key list type, set it up +% accordingly (advancing the key count and setting up the unknown handlers of +% \expkv). Else we simply throw an error and ignore the incident. +% \begin{macrocode} +\protected\long\def\ekvc@SetupSplitKeys@check@unknown#1% + {% + \begingroup + \edef\ekvc@tmp{\detokenize{#1}}% + \expandafter + \endgroup + \ifx\ekvc@tripledots\ekvc@tmp + \advance\ekvc@keycount1 +% \end{macrocode} +% The |\begingroup\expandafter\endgroup| ensures that the split mark isn't +% actually defined (even if it just were with meaning |\relax|). +% \begin{macrocode} + \begingroup\expandafter\endgroup + \expandafter\ekvc@SetupSplitKeys@unknown + \csname ekvc@splitmark@\the\ekvc@keycount\endcsname + \let\ekvc@any@long\long + \else + \ekvc@err@value@required{#1}% + \fi + } +% \end{macrocode} +% +% \begin{macrocode} +\protected\long\def\ekvc@SetupSplitKeys@unknown#1% + {% + \long\expandafter\def\csname\ekv@name\ekvc@set{}u\endcsname##1##2##3#1##4% + {##3#1{##4,##2= {##1} }}% + \long\expandafter\def\csname\ekv@name\ekvc@set{}uN\endcsname##1##2#1##3% + {##2#1{##3,##1}}% + \edef\ekvc@initials{\unexpanded\expandafter{\ekvc@initials#1{}}}% + \ekvc@helpers@needed + {\expandafter\ekvc@setup@splitmacro\expandafter{\the\ekvc@keycount}}% + {}% + } +% \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\ekvc@split@p@long} @@ -1436,6 +1816,14 @@ and the derived files expkv-cs.pdf % \end{macrocode} % \end{macro} % +% \begin{macro}[internal]{\ekvc@split@p@short} +% The |short| prefix does essentially nothing, it is only provided to allow +% key names starting with |long| that aren't |\long|. +% \begin{macrocode} +\def\ekvc@split@p@short{\ekvc@SetupSplitKeys@c} +% \end{macrocode} +% \end{macro} +% % \begin{macro}[internal]{\ekvc@defarggobbler} % This is needed to define a macro with 1-9 parameters programmatically. % \LaTeX's \cs[no-index]{newcommand} does something similar for example. @@ -1623,7 +2011,11 @@ and the derived files expkv-cs.pdf % \end{macro} % % \begin{macro}[internal] -% {\ekvc@SetupHashKeys,\ekvc@SetupHashKeys@a,\ekvc@SetupHashKeys@b} +% {% +% \ekvc@SetupHashKeys,\ekvc@SetupHashKeys@a,\ekvc@SetupHashKeys@b, +% \ekvc@SetupHashKeys@c, +% \ekvc@SetupHashKeys@check@unknown,\ekvc@SetupHashKeys@unknown +% } % This should look awfully familiar as well, since it's just the same as for % the split keys with a few other names here and there. % \begin{macrocode} @@ -1631,7 +2023,7 @@ and the derived files expkv-cs.pdf {% \let\ekvc@any@long\ekv@empty \let\ekvc@initials\ekv@empty - \ekvparse\ekvc@err@value@required\ekvc@SetupHashKeys@a{#1}% + \ekvparse\ekvc@SetupHashKeys@check@unknown\ekvc@SetupHashKeys@a{#1}% } \protected\def\ekvc@SetupHashKeys@a#1% {% @@ -1685,6 +2077,39 @@ and the derived files expkv-cs.pdf \ekvc@setup@hashmacro{#1}% } % \end{macrocode} +% \begin{macrocode} +\protected\long\def\ekvc@SetupHashKeys@check@unknown#1% + {% + \begingroup + \edef\ekvc@tmp{\detokenize{#1}}% + \expandafter + \endgroup + \ifx\ekvc@tripledots\ekvc@tmp + \ekvc@SetupHashKeys@unknown + \let\ekvc@any@long\long + \else + \ekvc@err@value@required{#1}% + \fi + } +% \end{macrocode} +% \begin{macrocode} +\def\ekvc@SetupHashKeys@unknown#1% + {% + \protected\def\ekvc@SetupHashKeys@unknown + {% + \expandafter + \let\csname\ekv@name\ekvc@set{}u\endcsname\ekvc@hash@unknown@kv + \expandafter + \let\csname\ekv@name\ekvc@set{}uN\endcsname\ekvc@hash@unknown@k + \edef\ekvc@initials{\unexpanded\expandafter{\ekvc@initials#1{}}}% + \ekvc@setup@hashmacro{...}% + }% + \long\def\ekvc@hash@unknown@kv##1##2##3#1##4{##3#1{##4,##2= {##1} }}% + \long\def\ekvc@hash@unknown@k##1##2#1##3{##2#1{##3,##1}}% + } +\expandafter\ekvc@SetupHashKeys@unknown + \csname ekvc@hashmark@\ekvc@tripledots\endcsname +% \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\ekvc@hash@p@long} @@ -1699,6 +2124,14 @@ and the derived files expkv-cs.pdf % \end{macrocode} % \end{macro} % +% \begin{macro}[internal]{\ekvc@hash@p@short} +% The |short| prefix does essentially nothing, it is only provided to allow +% key names starting with |long| that aren't |\long|. +% \begin{macrocode} +\def\ekvc@hash@p@short{\ekvc@SetupHashKeys@c} +% \end{macrocode} +% \end{macro} +% % \begin{macro}[internal]{\ekvc@setup@hashmacro} % The safe hash macros will be executed inside of an |\unexpanded| expansion % context, so they have to insert braces for that once they are done. @@ -1748,18 +2181,19 @@ and the derived files expkv-cs.pdf \unexpanded\expandafter{\csname ekvc@hashmark@#1\endcsname}% ####2####3\unexpanded{\ekvc@stop}####4% {% - ####4{####1####3}{####2}% + ####4{####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}% + ####1\unexpanded{\ekvc@mark\ekvc@safe@after@hash}% \unexpanded\expandafter {% \csname ekvc@hashmark@#1\endcsname{}% - \ekvc@mark{\ekvc@err@missing@hash{#1}\ekvc@safe@no@hash}% + \ekvc@mark + {\ekvc@err@missing@hash{#1}\ekvc@safe@after@hash}% \ekvc@stop }% }% @@ -1770,7 +2204,7 @@ and the derived files expkv-cs.pdf ####2####3\unexpanded{\ekvc@mark}####4####5% \unexpanded{\ekvc@stop}% {% - ####4{####2}####1####3\unexpanded{\ekvc@stop}% + ####4{####2}% }% }% \ekvc@tmp @@ -1812,22 +2246,15 @@ and the derived files expkv-cs.pdf {\csname ekvc@safesplithash@#1\endcsname}% {\ekvc@err@unknown@hash{#1}\ekvcValueSplit@recover}% } -\long\def\ekvcValueSplit@recover#1#2% - {% - #2{#1}{}% - } +\long\def\ekvcValueSplit@recover#1#2{#2{}} % \end{macrocode} % \end{macro} % -% \begin{macro}[internal]{\ekvc@safe@found@hash, \ekvc@safe@no@hash} +% \begin{macro}[internal]{\ekvc@safe@after@hash} % \begin{macrocode} -\long\def\ekvc@safe@found@hash#1#2\ekvc@stop#3% +\long\def\ekvc@safe@after@hash#1#2% {% - #3{#2}{#1}% - } -\long\def\ekvc@safe@no@hash#1#2\ekvc@mark\ekvc@safe@found@hash\ekvc@stop#3% - {% - #3{#2}{}% + #2{#1}% } % \end{macrocode} % \end{macro} @@ -1839,9 +2266,7 @@ and the derived files expkv-cs.pdf % sequence. % \begin{macrocode} \long\def\ekvcValueSplitFast#1#2% - {% - \csname ekvc@fastsplithash@#1\endcsname#2\ekvc@stop - } + {\csname ekvc@fastsplithash@#1\endcsname#2\ekvc@stop} % \end{macrocode} % \end{macro} % @@ -1858,8 +2283,8 @@ and the derived files expkv-cs.pdf % \begin{macrocode} \long\def\ekvc@safehash@#1{\ekvc@err@empty@hash{}} \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}{}} +\long\def\ekvc@safesplithash@#1#2{\ekvc@err@empty@hash#2{}} +\long\def\ekvc@fastsplithash@#1\ekvc@stop#2{\ekvc@err@empty@hash#2{}} % \end{macrocode} % \end{macro} % @@ -1894,6 +2319,116 @@ and the derived files expkv-cs.pdf % \end{macrocode} % \end{macro} % +% \begin{macro}{\ekvcChange} +% This can be used to change the defaults of an \expkvc\ defined macro. It +% checks whether there is a set with the correct name and that the macro is +% defined. If both is true the real work is done by |\ekvc@change|. +% \begin{macrocode} +\protected\long\def\ekvcChange#1% + {% + \ekvifdefinedset{\string#1}% + {% + \ekvc@ifdefined#1% + {\ekvc@change#1}% + {\ekvc@err@no@key@macro#1\@gobble}% + }% + {\ekvc@err@no@key@macro#1\@gobble}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekvc@change,\ekvc@change@a,\ekvc@change@b,\ekvc@change@c} +% First we need to see whether the macro is currently |\long|. For this we get +% the meaning and will parse it. |#1| is the macro name in which we want to +% change the defaults. +% \begin{macrocode} +\protected\def\ekvc@change#1% + {\expandafter\ekvc@change@a\meaning#1\ekv@stop#1} +% \end{macrocode} +% A temporary definition to get the stringified |macro:|. |##1| will be the +% list of prefixes, we don't care for the exact contents of |##2| and |##3|. +% \begin{macrocode} +\def\ekvc@change@a#1% + {% + \protected\def\ekvc@change@a##1#1##2->##3\ekv@stop + {% + \ekvc@change@iflong{##1}% + {\ekvc@change@b{}}% + {\ekvc@change@b{\long}}% + }% + } +\expandafter\ekvc@change@a\expandafter{\detokenize{macro:}} +% \end{macrocode} +% Next we expand the macro once to get its contents (including the current +% default values with their markers). |#1| is either |\long| or empty, |#2| is +% the macro. +% \begin{macrocode} +\protected\def\ekvc@change@b#1#2% + {\expandafter\ekvc@change@c\expandafter{#2{##1}}{#1}#2} +% \end{macrocode} +% Here we place an unbalanced closing brace after the expansion of the macro. +% Then we just parse the \kv-list with |\ekvset|, that will exchange the +% values behind the markers. Once those are changed we give control to +% |\ekvc@change@d|. The |\ekvset| step might horribly fail if the user defined +% some keys that don't behave nice. |#1| is the expansion of the macro, |#2| +% is either |\long| or empty, |#3| is the macro, and |#4| is the \kv-list +% containing the new defaults. +% \begin{macrocode} +\ekv@exparg{\protected\long\def\ekvc@change@c#1#2#3#4}% + {% + \expandafter\iffalse\expandafter{\expandafter\fi + \ekvset{\string#3}{#4}% + \ekvc@change@d{#2}{#3}% + #1% + }% + } +% \end{macrocode} +% The final step needs to put an unbalanced opening brace after |\def|. We do +% that with the help of a temporary macro which stores everything necessary +% for |\def| and expand an |\iffalse}\fi| construct to leave a single +% opening brace. |#1| will be either empty or |\long| and |#2| is the macro. +% Each of the macros defined with \expkvc\ takes exactly one parameter, so we +% put that here as |####1|. +% \begin{macrocode} +\protected\def\ekvc@change@d#1#2% + {% + \def\ekvc@tmp{#1\def#2####1}% + \expandafter\ekvc@tmp\expandafter{\iffalse}\fi + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@change@iflong,\ekvc@change@iflong@} +% Checking whether a string contains the string representation of |\long| can +% be done by gobbling everything up to the first |\long| and checking whether +% the result is completely empty. We need a temporary macro to get the result +% of |\string\long| inside the definitions. +% \begin{macrocode} +\def\ekvc@change@iflong#1% + {% + \protected\def\ekvc@change@iflong##1% + {\expandafter\ekv@ifempty\expandafter{\ekvc@change@iflong@##1#1}}% + \def\ekvc@change@iflong@##1#1{} + } +\expandafter\ekvc@change@iflong\expandafter{\string\long} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcPass} +% This macro can be used to pass a value to a key of some macro (this way +% more complicated key codes are possible that in the end pass processed +% values on to some macro). The implemantation is pretty straight forward. +% \begin{macrocode} +\long\def\ekvcPass#1#2% + {% + \ekvifdefined{\string#1}{#2}% + {\csname\ekv@name{\string#1}{#2}\endcsname}% + {\ekvc@err@unknown@key@or@macro{#1}{#2}\@gobble}% + } +% \end{macrocode} +% \end{macro} +% % % \subsubsection{Secondary Key Types} % @@ -2287,7 +2822,7 @@ and the derived files expkv-cs.pdf % \begin{macro}[internal] % { % \ekvc@err@toomany,\ekvc@err@value@required,\ekvc@err@missing@type, -% \ekvc@err@already@defined +% \ekvc@err@already@defined,\ekvc@err@no@key@macro % } % Boring unexpandable error messages. % \begin{macrocode} @@ -2322,6 +2857,8 @@ and the derived files expkv-cs.pdf \errmessage {expkv-cs Error: Unknown key `\unexpanded{#1}' for macro `\ekvc@set'}% } +\long\def\ekvc@err@no@key@macro#1% + {\errmessage{expkv-cs Error: \string#1 is no key=val macro}} % \end{macrocode} % \end{macro} % @@ -2343,6 +2880,8 @@ and the derived files expkv-cs.pdf \long\def\ekvc@err@missing@hash#1{\ekvc@err{hash `#1' not found}} \long\def\ekvc@err@empty@hash{\ekvc@err{empty hash}} \def\ekvc@err@invalid@bool#1{\ekvc@err{invalid boolean value `#1'}} +\long\def\ekvc@err@unknown@key@or@macro#1#2% + {\ekvc@err{unknown key `#2' for macro #1}} % \end{macrocode} % \end{macro} % diff --git a/Master/texmf-dist/tex/generic/expkv-cs/expkv-cs.tex b/Master/texmf-dist/tex/generic/expkv-cs/expkv-cs.tex index 500356b7069..55ed7f01041 100644 --- a/Master/texmf-dist/tex/generic/expkv-cs/expkv-cs.tex +++ b/Master/texmf-dist/tex/generic/expkv-cs/expkv-cs.tex @@ -35,11 +35,12 @@ \else \expandafter\endinput \fi -\def\ekvcVersion{0.10} -\def\ekvcDate{2021-06-03} +\def\ekvcVersion{1.0} +\def\ekvcDate{2021-06-20} \csname ekvc@tmp\endcsname \expandafter\chardef\csname ekvc@tmp\endcsname=\catcode`\@ \catcode`\@=11 +\def\ekvc@tripledots{...} \newcount\ekvc@keycount \let\ekvc@long\ekv@empty \let\ekvc@any@long\ekv@empty @@ -150,7 +151,7 @@ \ekvc@keycount=0 \let\ekvc@any@long\ekv@empty \let\ekvc@initials\ekv@empty - \ekvparse\ekvc@err@value@required\ekvc@SetupSplitKeys@a + \ekvparse\ekvc@SetupSplitKeys@check@unknown\ekvc@SetupSplitKeys@a } \protected\def\ekvc@SetupSplitKeys@a#1% {% @@ -202,12 +203,40 @@ {\expandafter\ekvc@setup@splitmacro\expandafter{\the\ekvc@keycount}}% {}% } +\protected\long\def\ekvc@SetupSplitKeys@check@unknown#1% + {% + \begingroup + \edef\ekvc@tmp{\detokenize{#1}}% + \expandafter + \endgroup + \ifx\ekvc@tripledots\ekvc@tmp + \advance\ekvc@keycount1 + \begingroup\expandafter\endgroup + \expandafter\ekvc@SetupSplitKeys@unknown + \csname ekvc@splitmark@\the\ekvc@keycount\endcsname + \let\ekvc@any@long\long + \else + \ekvc@err@value@required{#1}% + \fi + } +\protected\long\def\ekvc@SetupSplitKeys@unknown#1% + {% + \long\expandafter\def\csname\ekv@name\ekvc@set{}u\endcsname##1##2##3#1##4% + {##3#1{##4,##2= {##1} }}% + \long\expandafter\def\csname\ekv@name\ekvc@set{}uN\endcsname##1##2#1##3% + {##2#1{##3,##1}}% + \edef\ekvc@initials{\unexpanded\expandafter{\ekvc@initials#1{}}}% + \ekvc@helpers@needed + {\expandafter\ekvc@setup@splitmacro\expandafter{\the\ekvc@keycount}}% + {}% + } \protected\def\ekvc@split@p@long {% \let\ekvc@long\long \let\ekvc@any@long\long \ekvc@SetupSplitKeys@c } +\def\ekvc@split@p@short{\ekvc@SetupSplitKeys@c} \protected\def\ekvc@defarggobbler#1{\def\ekvc@tmp##1#1##2##{##1#1}} \begingroup \edef\ekvc@tmp @@ -340,7 +369,7 @@ {% \let\ekvc@any@long\ekv@empty \let\ekvc@initials\ekv@empty - \ekvparse\ekvc@err@value@required\ekvc@SetupHashKeys@a{#1}% + \ekvparse\ekvc@SetupHashKeys@check@unknown\ekvc@SetupHashKeys@a{#1}% } \protected\def\ekvc@SetupHashKeys@a#1% {% @@ -386,12 +415,42 @@ \ekvlet\ekvc@set{#1}\ekvc@tmp \ekvc@setup@hashmacro{#1}% } +\protected\long\def\ekvc@SetupHashKeys@check@unknown#1% + {% + \begingroup + \edef\ekvc@tmp{\detokenize{#1}}% + \expandafter + \endgroup + \ifx\ekvc@tripledots\ekvc@tmp + \ekvc@SetupHashKeys@unknown + \let\ekvc@any@long\long + \else + \ekvc@err@value@required{#1}% + \fi + } +\def\ekvc@SetupHashKeys@unknown#1% + {% + \protected\def\ekvc@SetupHashKeys@unknown + {% + \expandafter + \let\csname\ekv@name\ekvc@set{}u\endcsname\ekvc@hash@unknown@kv + \expandafter + \let\csname\ekv@name\ekvc@set{}uN\endcsname\ekvc@hash@unknown@k + \edef\ekvc@initials{\unexpanded\expandafter{\ekvc@initials#1{}}}% + \ekvc@setup@hashmacro{...}% + }% + \long\def\ekvc@hash@unknown@kv##1##2##3#1##4{##3#1{##4,##2= {##1} }}% + \long\def\ekvc@hash@unknown@k##1##2#1##3{##2#1{##3,##1}}% + } +\expandafter\ekvc@SetupHashKeys@unknown + \csname ekvc@hashmark@\ekvc@tripledots\endcsname \protected\def\ekvc@hash@p@long {% \let\ekvc@long\long \let\ekvc@any@long\long \ekvc@SetupHashKeys@c } +\def\ekvc@hash@p@short{\ekvc@SetupHashKeys@c} \protected\def\ekvc@setup@hashmacro#1% {% \ekv@ifdefined{ekvc@fasthash@#1}{}% @@ -432,18 +491,19 @@ \unexpanded\expandafter{\csname ekvc@hashmark@#1\endcsname}% ####2####3\unexpanded{\ekvc@stop}####4% {% - ####4{####1####3}{####2}% + ####4{####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}% + ####1\unexpanded{\ekvc@mark\ekvc@safe@after@hash}% \unexpanded\expandafter {% \csname ekvc@hashmark@#1\endcsname{}% - \ekvc@mark{\ekvc@err@missing@hash{#1}\ekvc@safe@no@hash}% + \ekvc@mark + {\ekvc@err@missing@hash{#1}\ekvc@safe@after@hash}% \ekvc@stop }% }% @@ -454,7 +514,7 @@ ####2####3\unexpanded{\ekvc@mark}####4####5% \unexpanded{\ekvc@stop}% {% - ####4{####2}####1####3\unexpanded{\ekvc@stop}% + ####4{####2}% }% }% \ekvc@tmp @@ -475,26 +535,17 @@ {\csname ekvc@safesplithash@#1\endcsname}% {\ekvc@err@unknown@hash{#1}\ekvcValueSplit@recover}% } -\long\def\ekvcValueSplit@recover#1#2% +\long\def\ekvcValueSplit@recover#1#2{#2{}} +\long\def\ekvc@safe@after@hash#1#2% {% - #2{#1}{}% - } -\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}{}% + #2{#1}% } \long\def\ekvcValueSplitFast#1#2% - {% - \csname ekvc@fastsplithash@#1\endcsname#2\ekvc@stop - } + {\csname ekvc@fastsplithash@#1\endcsname#2\ekvc@stop} \long\def\ekvc@safehash@#1{\ekvc@err@empty@hash{}} \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}{}} +\long\def\ekvc@safesplithash@#1#2{\ekvc@err@empty@hash#2{}} +\long\def\ekvc@fastsplithash@#1\ekvc@stop#2{\ekvc@err@empty@hash#2{}} \protected\long\def\ekvcSecondaryKeys#1#2% {% \edef\ekvc@set{\string#1}% @@ -518,6 +569,56 @@ }% {#2}% } +\protected\long\def\ekvcChange#1% + {% + \ekvifdefinedset{\string#1}% + {% + \ekvc@ifdefined#1% + {\ekvc@change#1}% + {\ekvc@err@no@key@macro#1\@gobble}% + }% + {\ekvc@err@no@key@macro#1\@gobble}% + } +\protected\def\ekvc@change#1% + {\expandafter\ekvc@change@a\meaning#1\ekv@stop#1} +\def\ekvc@change@a#1% + {% + \protected\def\ekvc@change@a##1#1##2->##3\ekv@stop + {% + \ekvc@change@iflong{##1}% + {\ekvc@change@b{}}% + {\ekvc@change@b{\long}}% + }% + } +\expandafter\ekvc@change@a\expandafter{\detokenize{macro:}} +\protected\def\ekvc@change@b#1#2% + {\expandafter\ekvc@change@c\expandafter{#2{##1}}{#1}#2} +\ekv@exparg{\protected\long\def\ekvc@change@c#1#2#3#4}% + {% + \expandafter\iffalse\expandafter{\expandafter\fi + \ekvset{\string#3}{#4}% + \ekvc@change@d{#2}{#3}% + #1% + }% + } +\protected\def\ekvc@change@d#1#2% + {% + \def\ekvc@tmp{#1\def#2####1}% + \expandafter\ekvc@tmp\expandafter{\iffalse}\fi + } +\def\ekvc@change@iflong#1% + {% + \protected\def\ekvc@change@iflong##1% + {\expandafter\ekv@ifempty\expandafter{\ekvc@change@iflong@##1#1}}% + \def\ekvc@change@iflong@##1#1{} + } +\expandafter\ekvc@change@iflong\expandafter{\string\long} +\long\def\ekvcPass#1#2% + {% + \ekvifdefined{\string#1}{#2}% + {\csname\ekv@name{\string#1}{#2}\endcsname}% + {\ekvc@err@unknown@key@or@macro{#1}{#2}\@gobble}% + } \protected\def\ekvc@p@long#1% {% \ekvc@ifspace{#1}% @@ -762,11 +863,15 @@ \errmessage {expkv-cs Error: Unknown key `\unexpanded{#1}' for macro `\ekvc@set'}% } +\long\def\ekvc@err@no@key@macro#1% + {\errmessage{expkv-cs Error: \string#1 is no key=val macro}} \ekv@exparg{\long\def\ekvc@err#1}{\ekverr{expkv-cs}{#1}} \long\def\ekvc@err@unknown@hash#1{\ekvc@err{unknown hash `#1'}} \long\def\ekvc@err@missing@hash#1{\ekvc@err{hash `#1' not found}} \long\def\ekvc@err@empty@hash{\ekvc@err{empty hash}} \def\ekvc@err@invalid@bool#1{\ekvc@err{invalid boolean value `#1'}} +\long\def\ekvc@err@unknown@key@or@macro#1#2% + {\ekvc@err{unknown key `#2' for macro #1}} \catcode`\@=\ekvc@tmp %% %% |