diff options
Diffstat (limited to 'macros/generic/expkv-bundle/pkg-main.tex')
-rw-r--r-- | macros/generic/expkv-bundle/pkg-main.tex | 1097 |
1 files changed, 1097 insertions, 0 deletions
diff --git a/macros/generic/expkv-bundle/pkg-main.tex b/macros/generic/expkv-bundle/pkg-main.tex new file mode 100644 index 0000000000..0109eb70da --- /dev/null +++ b/macros/generic/expkv-bundle/pkg-main.tex @@ -0,0 +1,1097 @@ +\genericekv\expkv{} + +This package supports two different front ends to parse a \kv\ list. The first +(|\ekvset|) is similar to \pkg{keyval}'s |\setkeys|, it parses the list and +executes defined actions based on the encountered \key{}s. The second +(|\ekvparse|) is more versatile, it only splits the list into \key{}s and +\val{}s and then runs user-provided code on the result. + +The first is described in subsections~\ref{sec:expkv:setup} +to~\ref{sec:expkv:set}, the latter is described in \autoref{sec:expkv:parse}. + +Unlike the other packages in the bundle, if you load \expkv\ as a \LaTeXe\ +package there is a single option available: +\begin{function}{all} + \begin{syntax} + \cs[no-index]{usepackage}[all]\{expkv\} + \end{syntax} + Loads all the packages of \expkvbundle. +\end{function} + +\subsection{General Parsing Rules} + +\expkv\ parses a \kv\ list by first splitting the elements on commas (active or +other), then looking for an equals sign (active or other). If there is one the +\kv\ pair will be split at the first. From both \key\ and \val\ (if there was a +\val) one set of outer spaces is stripped, and afterwards one set of outer +braces (meaning braces which are around the complete remainder after space +stripping if there are any). + +So the syntax looks something like the following pseudo-input: +\begin{syntaxexample} + \vissp{ \marg{key} = \marg{value} } +\end{syntaxexample} +with the displayed spaces and braces being optional and removed if found. Note +that if you want either \key\ or \val\ to include a comma the braces become +mandatory, the same is true if \key\ should contain an equals sign. + +\subsubsection{Expansion Control\label{sec:expkv:expansion}} + +\expkv\ provides a mechanism to specify expansions of a \key\ and/or \val. For +those familiar with \pkg{pgfkeys} this is similar to its |.expand once| or +|.expanded| handlers. This concept will be called \expnotation\ or \expansion\ +throughout this documentation. + +The syntax for this notation is a leading list of \expansion-rules followed by a +colon that is immediately followed by a space. Also the \expansion-rules must +not contain any spaces outside of braces, and the remainder on the right hand +side of the colon must not be blank, else it is not considered an \expnotation\ +but just a weirdly formed \key-name. + +The entire syntax of a \kv\ pair is +\begin{syntaxexample} + \vissp{ \{\expansion: \asciispace\marg{key} \} = \marg{value} } +\end{syntaxexample} +Note that the \expansion\ prefix is right delimited by \verb*|: | so the space +after the colon is only optional in the sense that the entire \expansion\ prefix +is optional. Else all displayed spaces and braces are optional, the inner set of +spaces and braces around \key\ only being optional if the optional \expansion\ +prefix (\expansion\verb*|: |) was present. If that part was present the list of +\expansion-rules will be executed, which might change the contents of both \key\ +and \val. For |\ekvparse| this is always true, however in |\ekvset| it is only +parsed for the \expnotation\ if there is no \key\ matching the given input (so +this notation doesn't impose a restriction on key names, though \key-names +actually containing what would otherwise be an \expansion\ prefix should be +pretty rare in practice). + +All packages in \expkvbundle\ support this notation (most of them internally use +|\ekvset| or |\ekvparse|). Please note however that while \expkvo\ fully +supports them, reinsertion via the |\r| \expansion-rule might affect the unused +global options list if used in the class options. + +An \expansion-rule consists of a single token. In a \Vkey\ they work on the +\val\ (but you can use the |\key| rule to also affect the \key\ there) while in +a \Nkey\ they work on the \key. The following rules are available (those +familiar with \pkg{expl3} will notice that the first six are identical to its +argument types): + +\begingroup +\let\bigskip\medskip +\begin{function}[module=Expansion]{o} + Expands the first token once. +\end{function} + +\begin{function}[module=Expansion]{e} + Expands the entire \val\ inside of |\expanded|. +\end{function} + +\begin{function}[module=Expansion]{c} + Builds a |\csname| from the contents. +\end{function} + +\begin{function}[module=Expansion]{f} + Expands the contents until a space or an unexpandable token is found (the + space would be removed). +\end{function} + +\begin{function}[module=Expansion]{V} + The \val\ should be a single token, either defined as a parameterless macro or + as a register (via |\newcount| etc.). This expands to the value of the + register or the macro's replacement text. If the token in \val\ has the + |\meaning| of |\relax| an error is thrown and the result is empty. +\end{function} + +\begin{function}[module=Expansion]{v} + This is a combination of |c| and |V|, meaning the \val\ is turned into a + single control sequence via |\csname|, and then expanded to its value. The + control sequence will only be built if it's defined. +\end{function} +\begin{example}{The difference between \texttt{co} and \texttt{v} expansion} + Say we want to hand the contents of a macro as the value to our key, but the + actual macro name depends on user input. For this we have two options which + behave slightly different. One is to use |v| the other is to combine the |co| + \expansion-rules. The following demonstrates both (I modified the way errors + are thrown to instead output them in red for this; you'll learn about + |\ekvparse| in a few pages, for now just stick with me): + \expkvdocPrintErrors + \makeatletter + \begin{enverb}[below] + \newcommand\mypair[2]{Arg: `\detokenize{#2}'. }% + \newcommand\myvalue{Value}% + \ekvparse\@firstofone\mypair + { + co: key = myvalue, v: key = myvalue, \par + ,co: key = myValue, v: key = myValue, \par + } + \end{enverb} + The difference is that in |co| the variable is implicitly initialised as + |\relax| by |c| if it doesn't exist and then doesn't expand in |o|. On the + other hand |v| will check whether the variable would exist and throw an error + if it doesn't (and will not set it to |\relax| by blindly using |\csname|). +\end{example} + +\begin{function}[module=Expansion]{s} + Strips one set of outer spaces and outer braces. +\end{function} + +\begin{function}[module=Expansion]{b} + Adds one set of outer braces. +\end{function} + +\begin{function}[module=Expansion]{p} + \begin{syntax} + p\marg{contents} + \end{syntax} + Places \meta{contents} before the \val. +\end{function} + +\begin{function}[module=Expansion]{P} + \begin{syntax} + P\marg{contents} + \end{syntax} + Places \meta{contents} after the \val. +\end{function} + +\begin{function}[module=Expansion]{g} + Gobbles the first token or balanced group on the left (leads to a low-level + \TeX-error if the \val\ is empty). +\end{function} + +\begin{function}[module=Expansion]{\r} + In a \Vkey\ reinserts the contents of \val\ after all the \expansion-rules + were executed (the \key-name needs to be empty). In a \Nkey\ the contents of + \key\ are reinserted after all the \expansion-rules were executed (the \val\ + needs to be empty, which is an easy to fulfil rule as there was no \val). + Normal \kv\ parsing is aborted afterwards for the current \kv\ list element. +\end{function} +\begin{example}{Parse the contents of a macro as additional \kv\ input} + Say we want to store a list of common settings in a macro, then we want to + parse a few keys, insert the contents of the macro, and parse a few more keys. + The following does exactly that (|\ekvset| is analogue to |\setkeys| of the + \pkg{keyval} package if you're familiar with it, else you'll learn about + |\ekvset| a few pages down the road so be patient): + \begin{enverb}[no-tcb] + \newcommand*\mykeylist{color=red,height=5cm} + \ekvset{mypkg}{key=value, o\r: \mykeylist, other key=other value} + \end{enverb} + You could also use the following with the same outcome, but this looks more + complicated so the other form should be preferred: + \begin{enverb}[no-tcb] + \ekvset{mypkg}{key=value, o\r: {}=\mykeylist, other key=other value} + \end{enverb} +\end{example} + +\begin{function}[module=Expansion]{\key} + \begin{syntax} + \cs[module=Expansion]{key}\marg{expansion} + \end{syntax} + This is the only supported way to change the contents of \key\ for a \Vkey\ in + the \expnotation. All the rules in \expansion\ are applied to \key\ instead of + \val. +\end{function} + +\begin{function}[module=Expansion]{R} + This is the same as if you used |V\r|. So it expects a single token, retrieves + its value, and reinserts this as additional \kv\ input. +\end{function} + +\begin{function}[module=Expansion]{r} + This is the same as if you used |v\r|. So it builds a |\csname| if that is + defined, retrieves its value, and reinserts this as additional \kv\ input. +\end{function} +\begin{example} + {Parse the contents of a macro as additional \kv\ input (revisited)} + Now that we also know the |R| and |r| rule, the example above can be input + even simpler: + \begin{enverb}[no-tcb] + \ekvset{mypkg}{key=value, R: \mykeylist, other key=other value} + \end{enverb} + or + \begin{enverb}[no-tcb] + \ekvset{mypkg}{key=value, r: mykeylist, other key=other value} + \end{enverb} +\end{example} +\endgroup + +\subsection{Setting up Keys\label{sec:expkv:setup}} + +\expkv\ provides a rather simple approach to setting up keys, similar to +\pkg{keyval}. If you're looking for a more sophisticated interface similar to +those of \pkg{l3keys} or \pkg{pgfkeys} take a look at \expkvd\ described in +\autoref{sec:expkv-def} or for a simple interface that defines expandable macros +at \expkvc\ described in \autoref{sec:expkv-cs}. + +Keys in \expkv\ (as in many other \kv\ implementations) belong to a \emph{set}, +so that different sets can contain keys of the same name. Unlike many other +implementations \expkv\ doesn't provide means to set a default value, instead we +have keys that take a value (we call those \Vkey) and keys that don't (which are +called \Nkey\ by \expkv), but both can share the same name on the user level, +the only difference for the user is whether =\val\ was used or not. + +The following macros are available to define new keys. Those macros containing +\enquote{\texttt{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). And prefixes allowed for |\let| can prefix those macros with +\enquote{\texttt{let}} in their name, 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 |\detokenize|d. Also \set\ should not +contain an explicit |\par| token. + +\begin{function}{\ekvdef} + \begin{syntax} + \cs{ekvdef}\marg{set}\marg{key}\marg{code} + \end{syntax} + Defines a \Vkey\ in a \set\ to expand to \meta{code}. In \meta{code} you can + use |#1| to refer to the given \val. +\end{function} +\begin{example}{Define a single \Vkey} + Define |text| in |foo| to store the \val\ inside |\foo@text|: + \begin{enverb}[no-tcb] + \protected\long\ekvdef{foo}{text}{\def\foo@text{#1}} + \end{enverb} +\end{example} + +\begin{function}{\ekvdefNoVal} + \begin{syntax} + \cs{ekvdefNoVal}\marg{set}\marg{key}\marg{code} + \end{syntax} + Defines a \Nkey\ in \set\ to expand to \meta{code}. +\end{function} +\begin{example}{Define a single \Nkey} + Define |bool| in |foo| to set |\iffoo@bool| to |true|: + \begin{enverb}[no-tcb] + \protected\ekvdefNoVal{foo}{bool}{\foo@booltrue} + \end{enverb} +\end{example} + +\begin{function}{\ekvlet} + \begin{syntax} + \cs{ekvlet}\marg{set}\marg{key}\meta{cs} + \end{syntax} + Let the \Vkey\ in \set\ to \meta{cs}. There are no checks on \meta{cs} + enforced, but the code should expect the \val\ as a single braced argument + directly following it. +\end{function} +\begin{example}{Copy a macro to define a \Vkey} + Let |cmd| in |foo| do the same as |\foo@cmd|: + \begin{enverb}[no-tcb] + \ekvlet{foo}{cmd}\foo@cmd + \end{enverb} +\end{example} + +\begin{function}{\ekvletNoVal} + \begin{syntax} + \cs{ekvletNoVal} \marg{set} \marg{key} \meta{cs} + \end{syntax} + Let the \Nkey\ in \set\ to \meta{cs}. Again no checks on \meta{cs} are done. + It shouldn't expect any provided argument. +\end{function} +\begin{example}{Copy a macro to define a \Nkey} + See above. +\end{example} + +\begin{function}{\ekvletkv} + \begin{syntax} + \cs{ekvletkv}\marg{set}\marg{key}\marg{set2}\marg{key2} + \end{syntax} + Copies the definition such that \Vkey\ in \set\ behaves like \meta{key2} of + \meta{set2}. It is not checked whether that second key exists! +\end{function} +\begin{example}{Copy an existing \Vkey} + Let |B| in |bar| do the same as |A| in |foo|: + \begin{enverb}[no-tcb] + \ekvletkv{bar}{B}{foo}{A} + \end{enverb} +\end{example} + +\begin{function}{\ekvletkvNoVal} + \begin{syntax} + \cs{ekvletkvNoVal}\marg{set}\marg{key}\marg{set2}\marg{key2} + \end{syntax} + And this lets the \Nkey\ in \set\ to the definition of the |NoVal|-\meta{key2} + in \meta{set2}. Again, it is not checked whether the second key exists. +\end{function} +\begin{example}{Copy an existing \Nkey} + See above. +\end{example} + +\subsection{Handle Unknown Keys} + +By default \expkv\ throws an error message if it encounters an undefined \key. +You can change this behaviour with the macros listed here. Just like in the +section above, prefixes for |\def| are allowed if the macro has |def| in its +name, and |\let| prefixes are allowed if the macro is named something with +|let|. + +\begin{function}{\ekvdefunknown} + \begin{syntax} + \cs{ekvdefunknown}\marg{set}\marg{code} + \end{syntax} + Execute \meta{code} if an undefined \Vkey\ is encountered while parsing in + \set. You can refer to the given \val\ with |#1|, the unknown \key's name + with |#2| (will be |\detokenize|d), and to the \key's name without + |\detokenize| applied with |#3| in \meta{code} (this order is chosen for + performance reasons). + + \cs{ekvdefunknown} and \cs{ekvredirectunknown} are mutually exclusive, you + can't use both. +\end{function} +\begin{example}{Search undefined \Vkey{}s in another \set} + Also search |bar| for undefined keys of set |foo| (and use the not yet + |\detokenize|d \key's name in case the undefined key handler of |bar| needs + that): + \begin{enverb}[no-tcb] + \long\ekvdefunknown{foo}{\ekvset{bar}{{#3}={#1}}} + \end{enverb} + This example differs from using |\ekvredirectunknown{foo}{bar}| (see below) in + that also the unknown-key handler of the |bar| set will be triggered, error + messages for undefined keys will look different, and this is slower than using + \cs{ekvredirectunknown}. +\end{example} + +\begin{function}{\ekvdefunknownNoVal} + \begin{syntax} + \cs{ekvdefunknownNoVal}\marg{set}\marg{code} + \end{syntax} + With this you can let \expkv\ execute \meta{code} if an unknown \Nkey\ was + encountered. You can refer to the given \key\ with |#1| (will be + |\detokenize|d), and to the not |\detokenize|d \key's name with |#2|. + + \cs{ekvdefunknownNoVal} and \cs{ekvredirectunknownNoVal} + are mutually exclusive, you can't use both. +\end{function} +\begin{example}{Search undefined \Nkey{}s in another \set} + Adding to the above also handling of \Nkey{}s in |foo|: + \begin{enverb}[no-tcb] + \ekvdefunknownNoVal{foo}{\ekvset{bar}{{#2}}} + \end{enverb} +\end{example} + +\begin{function}{\ekvredirectunknown} + \begin{syntax} + \cs{ekvredirectunknown}\marg{set}\marg{set-list} + \end{syntax} + This is a short cut to set up a special \cs{ekvdefunknown}-rule for \set\ that + will check each set in the comma separated \meta{set-list} for an unknown + \Vkey. The resulting unknown-key handler will always be |\long| and \emph{not} + |\protected|. The first set in \meta{set-list} has highest priority, once the + \Vkey\ is found in one of the sets the remainder of the list is discarded. If + \meta{key} isn't found in any of the sets an error will be thrown eventually. + Note that the error message looks different than a normal key-not-found error, + in particular no unwanted-value message can be thrown (it will not be checked + if a \Nkey\ of the same name does exist), and the error message will contain + all sets. + + \cs{ekvdefunknown} and \cs{ekvredirectunknown} + are mutually exclusive, you can't use both. +\end{function} +\begin{example}{Search an undefined \Vkey\ in a list of other \set{}s} + For every undefined \Vkey\ in |foo| also search the sets |bar| and |baz|: + \begin{enverb}[no-tcb] + \ekvredirectunknown{foo}{bar, baz} + \end{enverb} +\end{example} + +\begin{function}{\ekvredirectunknownNoVal} + \begin{syntax} + \cs{ekvredirectunknownNoVal}\marg{set}\marg{set-list} + \end{syntax} + This behaves just like \cs{ekvredirectunknown}, it does the same but for + \Nkey{}s. Again no prefixes are supported (the result will neither be |\long| + nor |\protected|). Note that the error messages will not check whether a + missing-value error should be thrown. + + \cs{ekvdefunknownNoVal} and \cs{ekvredirectunknownNoVal} + are mutually exclusive, you can't use both. +\end{function} +\begin{example}{Search an undefined \Nkey\ in a list of other \set{}s} + See above. +\end{example} + +\begin{function}{\ekvletunknown} + \begin{syntax} + \cs{ekvletunknown}\marg{set}\meta{cs} + \end{syntax} + This lets the handler for unknown \Vkey{}s to \meta{cs}. \meta{cs} should + expect three arguments, the first will be the \val\ the second the + |\detokenize|d \key-name, the third the unprocessed \key-name. No conditions + on \meta{cs} are enforced. +\end{function} +\begin{example} + {Do the same as an already defined macro if an unknown \Vkey\ is found} + Let the set |foo| do the same as the macro |\foo@unknown| whenever an unknown + \Vkey\ is encountered: + \begin{enverb}[no-tcb] + \ekvletunknown{foo}\foo@unknown + \end{enverb} +\end{example} + +\begin{function}{\ekvletunknownNoVal} + \begin{syntax} + \cs{ekvletunknownNoVal}\marg{set}\meta{cs} + \end{syntax} + This does the same as \cs{ekvletunknown} but for \Nkey{}s. The \meta{cs} + should expect two arguments, namely the |\detokenize|d \key\ and the + unprocessed \key. +\end{function} +\begin{example}{Silently ignore unknown \Nkey{}s} + Let the set |foo| ignore unknown \Nkey{}s by gobbling the \key-name: + \begin{enverb}[no-tcb] + \ekvletunknownNoVal{foo}\@gobbletwo + \end{enverb} +\end{example} + +\subsection{Helpers in Actions} + +\begin{function}{\ekvifdefined,\ekvifdefinedNoVal} + \begin{syntax} + \cs{ekvifdefined}\marg{set}\marg{key}\marg{true}\marg{false} + \cs{ekvifdefinedNoVal}\marg{set}\marg{key}\marg{true}\marg{false} + \end{syntax} + These two macros test whether there is a \key\ in \set. It is false if either + a hash table entry doesn't exist for that key or its meaning is |\relax|. +\end{function} +\begin{example}{Check if a \Vkey\ is already defined} + Check whether the key |special| is already defined in set |foo|, if it isn't + input a file that contains more key definitions: + \begin{enverb}[no-tcb] + \ekvifdefined{foo}{special}{}{\input{foo.morekeys.tex}} + \end{enverb} +\end{example} + +\begin{function}{\ekvifdefinedset} + \begin{syntax} + \cs{ekvifdefinedset}\marg{set}\marg{true}\marg{false} + \end{syntax} + This macro tests whether \set\ is defined (which it is if at least one key was + defined for it). If it is \meta{true} will be run, else \meta{false}. +\end{function} +\begin{example}{Check if a \set\ is already defined} + Check whether the set |VeRyUnLiKeLy| is already defined, if so throw an error, + else do nothing: + \begin{enverb}[no-tcb] + \ekvifdefinedset{VeRyUnLiKeLy} + {\errmessage{VeRyUnLiKeLy already defined}}{} + \end{enverb} +\end{example} + +\begin{function}{\ekvsneak,\ekvsneakPre} + \begin{syntax} + \cs{ekvsneak}\marg{after} + \end{syntax} + Puts \meta{after} after the effects of \cs{ekvset} (without cancelling the + current \cs{ekvset} call). The first variant will put \meta{after} after any + other tokens which might have been sneaked before, while \cs{ekvsneakPre} will + put \meta{after} before other smuggled stuff. After \cs{ekvset} has parsed the + entire \kv\ list everything that has been \cs{ekvsneak}ed will be left in the + input stream. +\end{function} +\begin{example}{Execute code after \cs[no-index]{ekvset} if a \Nkey\ was used} + Define a key |secret| in the set |foo| that will sneak out + |\foo@secretly@sneaked|: + \begin{enverb}[no-tcb] + \ekvdefNoVal{foo}{secret}{\ekvsneak{\foo@secretly@sneaked}} + \end{enverb} +\end{example} +A more elaborate usage example is shown in \autoref{sec:expkv:sneak}. + +\begin{function}{\ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak} + \begin{syntax} + \cs{ekvbreak}\marg{after} + \end{syntax} + Gobbles the remainder of the current \cs{ekvset} call and its argument list + and inserts \meta{after}. So this can be used to break out of \cs{ekvset}. The + first variant will also gobble anything that has been sneaked out using + \cs{ekvsneak} or \cs{ekvsneakPre}, while \cs{ekvbreakPreSneak} will put + \meta{after} before anything that has been smuggled and \cs{ekvbreakPostSneak} + will put \meta{after} after the stuff that has been sneaked out. +\end{function} +\begin{example}{Stop parsing a \kv\ list if a specific \Nkey\ was used} + Define a key |abort| that will stop key parsing inside the set |foo| and + execute |\foo@aborted|, or if it got a value |\foo@aborted@with|: + \begin{enverb}[no-tcb] + \ekvdefNoVal{foo}{abort}{\ekvbreak{\foo@aborted}} + \ekvdef{foo}{abort}{\ekvbreak{\foo@aborted@with{#1}}} + \end{enverb} +\end{example} + +\begin{function}{\ekvmorekv} + \begin{syntax} + \cs{ekvmorekv}\kvarg + \end{syntax} + Adds the contents of the \kv\ list to the list processed by the current call + of |\ekvset|. +\end{function} +\begin{example}{Use one \key\ to set multiple other keys} + Define a \Nkey\ |style| that sets the keys |border|, |width|, and |height| as + a shortcut: + \begin{enverb}[no-tcb] + \ekvdefNoVal{foo}{style}{\ekvmorekv{border, width=2cm, height=1.5ex}} + \end{enverb} +\end{example} + +\begin{function}{\ekvchangeset} + \begin{syntax} + \cs{ekvchangeset}\marg{new-set} + \end{syntax} + Replaces the current \set\ with \meta{new-set}, so for the rest of the current + \cs{ekvset} call that call behaves as if it was called with + \cs{ekvset}\marg{new-set}. It is comparable to using \key|/.cd| in + \pkg{pgfkeys}. +\end{function} +\begin{example}{Change the current \set} + Define a key |cd| in set |foo| that will change to another set as specified in + the \val. If the set is undefined it'll stop the parsing and throw an error as + defined in the macro |\foo@cd@error|: + \begin{enverb}[no-tcb] + \ekvdef{foo}{cd} + {\ekvifdefinedset{#1}{\ekvchangeset{#1}}{\ekvbreak{\foo@cd@error}}} + \end{enverb} +\end{example} + +\subsection{Parsing Keys in Sets}\label{sec:expkv:set} + +\begin{function}{\ekvset} + \begin{syntax} + \cs{ekvset}\marg{set}\kvarg + \end{syntax} + This macro parses the \kv\ list and checks for defined \key{}s that are in + \set. Unlike the generic \cs{ekvparse} this macro uses |\detokenize| on the + \key\ before checking whether it is a defined key. + + \cs{ekvset} is nestable, and fully expandable. But it is \emph{not} alignment + safe. As a result \key\ names and \val{}s that contain an |&| must be wrapped + in braces if \cs{ekvset} is used inside an alignment (like \LaTeXe's |tabular| + environment) or alternatively you have to create a wrapper that ensurs an + alignment safe context. +\end{function} +\begin{example}{Set defined keys using \cs[no-index]{ekvset}} + Parse |key=arg, key| in set |foo|: + \begin{enverb}[no-tcb] + \ekvset{foo}{key=arg, key} + \end{enverb} +\end{example} + +\begin{function}{\ekvsetSneaked} + \begin{syntax} + \cs{ekvsetSneaked}\marg{set}\marg{sneak}\kvarg + \end{syntax} + This behaves like \cs{ekvset} in which \cs{ekvsneak} was immediately called. +\end{function} +\begin{example} + {Set defined keys and execute code afterwards using + \cs[no-index]{ekvsetSneaked}} + Parse |key=arg, key| in the set |foo| with |\afterwards| sneaked out: + \begin{enverb}[no-tcb] + \ekvsetSneaked{foo}{\afterwards}{key=arg, key} + \end{enverb} +\end{example} + +\begin{function}{\ekvsetdef} + \begin{syntax} + \cs{ekvsetdef}\meta{cs}\marg{set} + \end{syntax} + Defines the macro \meta{cs} to be a shortcut for \cs{ekvset}\marg{set}. You + can use any \TeX-prefix allowed to prefix |\def| for \cs{ekvsetdef} (so + |\long|, |\protected|, or |\global| -- don't use |\outer|). + The resulting macro is faster than but else equivalent to the idiomatic + definition:\par + |\def|\meta{cs}|#1{|\cs{ekvset}\marg{set}|{#1}}| +\end{function} +\begin{example} + {Define a setup command for a defined \set\ using \cs[no-index]{ekvsetdef}} + Define the macro |\foosetup| to parse keys in the set |foo| and use it to + parse |key=arg, key|: + \begin{enverb}[no-tcb] + \ekvsetdef\foosetup{foo} + \foosetup{key=arg, key} + \end{enverb} +\end{example} + +\begin{function}{\ekvsetSneakeddef} + \begin{syntax} + \cs{ekvsetSneakeddef}\meta{cs}\marg{set} + \end{syntax} + Just like \cs{ekvsetdef} this defines a shorthand macro \meta{cs}, but this + will make it a shorthand for \cs{ekvsetSneaked}, meaning \meta{cs} will take + two arguments (first the \cs{ekvsneak} argument, then the \kv\ list). Hence + the result is a faster version of:\par + |\long\def|\meta{cs}|#1#2{|\cs{ekvsetSneaked}\marg{set}|{#1}{#2}}| +\end{function} +\begin{example} + {Define a setup command that will also require code to execute after all + keys were processed using \cs[no-index]{ekvsetSneakeddef}} + Define the macro |\foothings| to parse keys in the set |foo| and accept a + sneaked argument, then use it to parse |key=arg, key| and sneak |\afterwards|: + \begin{enverb}[no-tcb] + \ekvsetSneakeddef\foothings{foo} + \foothings{\afterwards}{key=arg, key} + \end{enverb} +\end{example} + +\begin{function}{\ekvsetdefSneaked} + \begin{syntax} + \cs{ekvsetdefSneaked}\meta{cs}\marg{set}\marg{sneaked} + \end{syntax} + This macro behaves like \cs{ekvsetSneakeddef}, but with a fixed \meta{sneaked} + argument. So the resulting macro is faster than but else equivalent to\par + |\long\def|\meta{cs}|#1{|\cs{ekvsetSneaked}\marg{set}\marg{sneaked}|{#1}}| +\end{function} +\begin{example} + {Define a setup command that will execute codes after all keys were + processed using \cs[no-index]{ekvsetdefSneaked}} + Define the macro |\barthing| to parse keys in the set |bar| and always execute + |\afterwards| afterwards, then use it to parse |key=arg, key|: + \begin{enverb}[no-tcb] + \ekvsetdefSneaked\barthing{bar}{\afterwards} + \barthing{key=arg, key} + \end{enverb} +\end{example} + +\begin{function}{\ekvcompile} + \begin{syntax} + \cs{ekvcompile}\meta{*}\meta{cs}\meta{parameters}\marg{set}\kvarg + \end{syntax} + This macro defines \meta{cs} to be a \emph{fast} way to set the given \kv\ + list in \meta{set}. The meaning of the keys is frozen if you don't give the + optional |*| (if the star is present the stored content will be the key-macros + and later redefinitions of keys will affect them, otherwise the key-macros are + expanded once, hence the key-code is stored). This does support the unknown + key handlers set up with |\ekvdefunknown| and |\ekvdefunknownNoVal| and also + the redirection of unknown keys (the latter will not be expanded exhaustively + though, so the key-search is done on every later call of \meta{cs}). Any + prefix allowed for |\def| might prefix \cs{ekvcompile}. The list is not + entirely fixed, as you might use \meta{parameters} in a \val\ (this is not a + single token but a parameter text as you'd use it with |\def|). They can not + be part of a \key-name (the names are indeed fixed). If you need a |#| in a + \val\ you'll need to double it just as you'd do in |\def|. Internally + \cs{ekvcompile} uses |\ekvparse| and no |\ekvset| variant, because of this the + \expnotation\ is handled slightly differently; in case you're using a + \key-name that starts with something that looks like \expnotation\ you'll have + to explicitly add an empty \expansion\ prefix. +\end{function} +\begin{example} + {Compile a \kv\ list into a macro that will quickly set that list} + Define the macro |\foo| to set some keys in the set |foo|. Since one key has a + strange name we need to add an empty \expansion\ prefix. Also we'd like |\foo| + to take one parameter which is part of the \val\ of |bar| (since the list is + parsed now and not when |\foo| is used we don't need to put braces around that + value, even if at use time |#1| contains commas): + \begin{enverb}[no-tcb] + \ekvcompile\foo#1{foo} + { + bar = #1baz + ,: part-of-key: name = strange + ,NoVal + } + \end{enverb} + After this using |\foo{VAL}| will be the same as but faster than + \begin{enverb}[no-tcb] + \ekvset{foo}{bar={VALbaz},part-of-key: name=strange,NoVal} + \end{enverb} +\end{example} + +\subsection{Generic Key Parsing}\label{sec:expkv:parse} + +\begin{function}{\ekvparse} + \begin{syntax} + \cs{ekvparse}\marg{code1}\marg{code2}\kvarg + \end{syntax} + This macro parses the \kv\ list and provides \Nkey{}s to \meta{code1} as a + single argument and \Vkey{}s with their corresponding \val\ as two arguments + to \meta{code2}. + + \cs{ekvparse} is fully expandable and alignment safe, meaning that you don't + have to take any extra precautions if it is used inside an alignment context + (like \LaTeXe's |tabular| environment) and any \key\ or \val\ can contain an + |&|. \cs{ekvparse} expands in exactly two steps, the result is provided inside + |\unexpanded| (so doesn't expand further in an |\edef| or |\expanded| + context). + + \cs{ekvbreak}, \cs{ekvsneak}, \cs{ekvmorekv}, \emph{etc.}\ don't work + in \cs{ekvparse}. \cs{ekvparse} does not throw an error if multiple + unprotected equals signs are found (it just splits at the first), and doesn't + throw an error if a \key\ is empty. If something looks like \expnotation\ (has + a colon followed but not preceded by a space and with non-blank material + following it) it'll be parsed as such (which might throw errors due to + undefined \expansion-rules if that wasn't the intended input). If you for some + reason need to input a \key-name that would match that pattern you'll need to + precede it by \verb*|: | (an empty \expansion\ prefix). +\end{function} +\begin{example} + {Parse a \kv\ list and execute arbitrary code for each element using + \cs[no-index]{ekvparse}} + \begin{enverb}[no-tcb] + \ekvparse{\handlekey{S}}{\handlekeyval{S}}{foo = bar, key, baz={zzz}} + \end{enverb} + would be equivalent to + \begin{enverb}[no-tcb] + \handlekeyval{S}{foo}{bar}\handlekey{S}{key}\handlekeyval{S}{baz}{zzz} + \end{enverb} + and afterwards |\handlekey| and |\handlekeyval| would have to further handle + the keys. No such macros are contained in \expkv, but I hope you get the idea. + Because it expands in two steps and doesn't expand any further both + \begin{enverb}[no-tcb] + \expandafter\parse\expanded{\ekvparse\k\kv{foo = bar, key, baz={zzz}}} + \end{enverb} + and + \begin{enverb}[no-tcb] + \expandafter\expandafter\expandafter + \parse\ekvparse\k\kv{foo={bar}, key, baz = zzz} + \end{enverb} + expand to + \begin{enverb}[no-tcb] + \parse\kv{foo}{bar}\k{key}\kv{baz}{zzz} + \end{enverb} +\end{example} + +\subsection{Other Useful Macros} + +\begin{function}{\ekvoptarg} + \begin{syntax} + \cs{ekvoptarg}\marg{next}\marg{default} + \end{syntax} + This macro will expandably check for a following optional argument in brackets + (|[]|). After the optional argument there has to be a mandatory one (or else + this might have unwanted side effects). The code in \meta{next} should expect + two arguments (or more), namely the processed optional argument and the + mandatory one that followed it. If there was an optional argument the result + will be \meta{next}\marg{optional}\meta{mandatory} (so the optional argument + will be wrapped in braces, the mandatory argument will be untouched). If there + was no optional argument the result will be + \meta{next}\marg{default}\marg{mandatory} (so the default will be used and the + mandatory argument will be wrapped in braces after it was read once -- if it + was already wrapped it is effectively unchanged). + + \cs{ekvoptarg} expands in exactly two steps, grabs all the arguments only at + the second expansion step, and is alignment safe. It has its limitations + however. It can't tell the difference between |[| and |{[}|, so it doesn't + work if the mandatory argument is a single bracket. Also if the optional + argument should contain a nested closing bracket it has to be nested in braces + like so: |[{arg[u]ment}]| (or else the result would be |arg[u| with a trailing + |ment]|). +\end{function} +\begin{example}{Expandably search for an optional argument with a default value} + Say we have a macro that should take an optional argument defaulting to |1|, + we could program it like this: + \makeatletter + \begin{enverb} + \newcommand\foo{\ekvoptarg\@foo{1}} + \newcommand\@foo[2]{Mandatory: #2\par Optional: #1} + \foo{5}\par + \foo[4]{5}\par + \end{enverb} +\end{example} + +\begin{function}{\ekvoptargTF} + \begin{syntax} + \cs{ekvoptargTF}\marg{true}\marg{false} + \end{syntax} + This macro is similar to \cs{ekvoptarg} but will result in + \meta{true}\marg{optional}\meta{mandatory} or \meta{false}\marg{mandatory} + instead of placing a default value. + + \cs{ekvoptargTF} expands in exactly two steps, grabs all the arguments only at + the second expansion step, and is alignment safe. It has the same limitations + as \cs{ekvoptarg}. +\end{function} +\begin{example} + {Expandably search for an optional argument and behave differently if it's + found or not} + Say we have a macro that should behave differently depending on whether there + was an optional argument or not. This could be done with: + \makeatletter + \begin{enverb} + \newcommand\foo{\ekvoptargTF\foo@a\foo@b} + \newcommand\foo@a[2]{Mandatory: #2\par Optional: #1} + \newcommand\foo@b[1]{Mandatory: #1\par No optional.} + \foo{5}\par + \foo[4]{5}\par + \end{enverb} +\end{example} + +\begin{function}{\ekvcsvloop} + \begin{syntax} + \cs{ekvcsvloop}\marg{code}\marg{csv-list} + \end{syntax} + This loops over the comma separated items in \meta{csv-list} and, after + stripping spaces from either end of \meta{item} and removing at most one set + of outer braces, leaves |\unexpanded{|\meta{code}\marg{item}|}| for each list + item in the input stream. Blank elements are ignored (if you need a blank + element it should be given as \verb*|{ }|). It supports both active commas and + commas of category other. \cs{ekvcsvloop} is not alignment safe, but you could + make it so by nesting it in |\expanded| (since the braces around the argument + of |\expanded| will hide alignment characters from \TeX's parsing). +\end{function} +\begin{example} + {Loop over a comma separated list and execute arbitrary code for each + element} + The following splits a comma separated list and prints it in a typewriter font + with parentheses around each element: + \begin{enverb} + \newcommand*\myprocessor[1]{\texttt{(#1)}} + \ekvcsvloop\myprocessor{abc,def,ghi}\par + \ekvcsvloop\myprocessor{1,,2,,3,,4}\par + \end{enverb} +\end{example} + +\begin{function}{\ekverr} + \begin{syntax} + \cs{ekverr}\marg{package}\marg{message} + \end{syntax} + This macro will throw an error fully expandably.\footnotemark\ The error + length is limited to a total length of 69~characters, and since ten characters + will be added for the formatting (\verb*|! | and \verb*| Error: |) that leaves + us with a total length of \meta{package} plus \meta{message} of 59~characters. + If the message gets longer \TeX\ will only display the first 69~characters and + append |\ETC.| to the end. + + Neither \meta{package} nor \meta{message} expand any further. Also + \meta{package} must not contain an explicit |\par| token or the token + |\thanks@jfbu|. No such restriction applies to \meta{message}. + + If |^^J| is set up as the |\newlinechar| (which is the case in \LaTeXe\ but + not in \hologo{plainTeX} by default) you can use that to introduce line breaks + in your error message. However that doesn't change the message length limit. +\end{function} +\footnotetext{The used mechanism was to the best of my knowledge first +implemented by Jean-François Burnol.} +After your own error message some further text will be placed. The formatting +of that text will look good if |^^J| is the |\newlinechar|, else not so much. +That text will read: +\begin{verbatim} + ! Paragraph ended before \<an-expandable-macro> + completed due to above exception. If the error + summary is not comprehensible see the package + documentation. + I will try to recover now. If you're in inter- + active mode hit <return> at the ? prompt and I + continue hoping recovery was complete. +\end{verbatim} +Any clean up has to be done by you, |\ekverr| will expand to nothing after +throwing the error message. + +In \ConTeXt\ this macro works differently. While still being fully expandable, +it doesn't have the character count limitation and doesn't impose restrictions +on \meta{package}. It will not display the additional text and adding line +breaks is not possible. +\begin{example}{Expandably throw error messages using \cs[no-index]{ekverr}} + Say we set up a macro that takes as mandatory argument a simple equation which + must not be empty and if it's not empty it displays it and calculates the + result: + \begin{enverb}[no-tcb] + \newcommand*\mycalc[1] + {% + \the\numexpr + \if\relax\detokenize{#1}\relax + \ekverr{my}{Empty equation not allowed, leaving -2147483647}% + -2147483647% + \else + #1% + \fi + \relax + } + Using \mycalc{} wrong. + \end{enverb} + If that code gets executed the following will be the terminal output + \begin{verbatim} + Runaway argument? + ! my Error: Empty equation not allowed, leaving -2147483647 + ! Paragraph ended before \<an-expandable-macro> + completed due to above exception. If the error + summary is not comprehensible see the package + documentation. + I will try to recover now. If you're in inter- + active mode hit <return> at the ? prompt and I + continue hoping recovery was complete. + <to be read again> + \par + l.17 Using \mycalc{} + wrong. + ? + \end{verbatim} + and the output would contain + \exres{Using -2147483647 wrong} + if we continued the \TeX\ run at the prompt. +\end{example} + + +\subsection{Other Macros} + +\begin{variable}{\ekvDate,\ekvVersion} + These two macros store \expkv's date and version. +\end{variable} + +\begin{function}{\ekv@name,\ekv@name@set,\ekv@name@key} + \begin{syntax} + \cs{ekv@name}\marg{set}\marg{key} + \cs{ekv@name@set}\marg{set} + \cs{ekv@name@key}\marg{key} + \end{syntax} + The names of the macros storing the code of \Vkey{}s are stored in are built + with these macros. The name is built from two blocks, one that is formatting + the \set\ name, and on for formatting the \key\ name. To get the actual name + the argument to \cs{ekv@name@key} must be |\detokenize|d. Both blocks are put + together (with the necessary |\detokenize|) by \cs{ekv@name}. For \Nkey{}s an + additional |N| gets appended, so their name is + \cs{ekv@name}\marg{set}\marg{key}|N|. + + You can use these macros to implement additional functionality or access + key-macros outside of \expkv, but \emph{don't} change them! \expkv\ relies on + their exact definitions internally. +\end{function} +\begin{example}{Directly call key code without parsing a \kv\ list} + Execute the key-macro of the \Nkey\ named |bar| in set |foo|: + \begin{enverb}[no-tcb] + \csname\ekv@name{foo}{bar}N\endcsname + \end{enverb} +\end{example} + + +\subsection{Examples} +\subsubsection{Standard Use-Case\label{sec:expkv:standard}} + +\begin{example}{A \kv\ based replacement for \LaTeXe's \cs[no-index]{rule}} + Because I keep forgetting the correct order of \LaTeXe's |\rule| command I + want to create a \kv\ interface to it. For this I define the keys |ht| to + specify the rule's height, |wd| to specify its width, and to give a + displacement I use two keys (because who can remember whether the rule is + moved upwards or downwards?). + + First the internals storing the values are initialised + \begin{enverb}[no-tcb,store] + \makeatletter + \newcommand*\myrule@ht{1ex} + \newcommand*\myrule@wd{0.1em} + \newcommand*\myrule@raise{\z@} + \end{enverb} + then the keys are defined. We could use |\dimen| registers instead of defining + macros, but macros have the advantage that the font dependent dimensions are + evaluated at use time. + \begin{enverb}[no-tcb,store] + \protected\ekvdef{myrule}{ht}{\def\myrule@ht{#1}} + \protected\ekvdef{myrule}{wd}{\def\myrule@wd{#1}} + \protected\ekvdef{myrule}{raise}{\def\myrule@raise{#1}} + \protected\ekvdef{myrule}{lower}{\def\myrule@raise{-#1}} + \end{enverb} + We also want a way to change the initial values without outputting a rule + (since there are unexpandable keys involved it's a good idea to define this + |\protected|) + \begin{enverb}[no-tcb,store] + \protected\ekvsetdef\myruleset{myrule} + \end{enverb} + and we need an actual frontend that does the job: + \begin{enverb}[no-tcb,store] + \newcommand*\myrule[1][] + {% + \begingroup + \myruleset{#1}% + \rule[\myrule@raise]{\myrule@wd}{\myrule@ht}% + \endgroup + } + \makeatother + \end{enverb} + Now we can use it: + \begin{enverb}[same-line=0.5,restore] + a\myrule\par + a\myrule[ht=2ex,lower=.5ex]\par + \myruleset{wd=5cm} + a\myrule + \end{enverb} +\end{example} + + +\subsubsection{An Expandable \kv\ Macro Using + \cs[no-index]{ekvsneak}\label{sec:expkv:sneak}} + +\begin{example}{An expandable \kv\ macro using \cs[no-index]{ekvsneak}} + Let's set up an expandable macro that uses a \kv\ interface. The problems + we'll face for this are: + \begin{enumerate} + \item ignoring duplicate keys + \item default values for keys which weren't used + \item providing the values as the correct argument to a macro (ordered) + \end{enumerate} + + First we need to decide which \kv\ parsing macro we want to do this with, + \cs{ekvset} or \cs{ekvparse}. For this example we also want to show the usage + of \cs{ekvsneak}, hence we'll choose \cs{ekvset}. And we'll have to use + \cs{ekvset} such that it builds a parsable list for our macro internals. To + gain back control after \cs{ekvset} is done we have to put an internal of our + macro at the start of that list, so we use an internal key that uses + \cs{ekvsneakPre} after any user input. + + To ignore duplicates will be easy if the value of the key used last will be + put first in the list, so we'll use \cs{ekvsneakPre} for the real values as + well. If for some reason we wanted a key for which the first usage was the + binding one we'd use \cs{ekvsneak} for that one. + + 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 + with default values before our end marker. + + Ordering the keys can be done simply by searching for a specific token for + each argument (that token acts as a flag), so our sneaked out values will + include these specific tokens acting as markers. + + Now we got an answer to each of our initial problems. Everything that's left + is deciding what our macro should actually do. For this example we'll define a + macro that calculates the sine of a number rounded to a specified precision. + The macro should also understand input in radian and degree, and we could also + decide to evaluate a different function. For the real hard part of this + (expandably calculating trigonometric functions) we'll use \pkg{xfp}. + + First we set up our keys according to our earlier considerations and set up + the user facing macro |\sine|. The end marker of the parsing list will be a + |\sine@stop| token (which we don't need to define) and we put our default + values right before it. The user macro |\sine| uses |\ekvoptargTF| to check + for the optional argument short cutting a bit if no optional argument was + found. If you'd so prefer you could use \pkg{ltcmd}'s + |\NewExpandableDocumentCommand| to expandably get an optional argument as + well. + \begin{enverb}[no-tcb,store] + \RequirePackage{xfp} + \makeatletter + \ekvdef{sine}{f}{\ekvsneakPre{\f{#1}}} + \ekvdef{sine}{round}{\ekvsneakPre{\rnd{#1}}} + \ekvdefNoVal{sine}{degree}{\ekvsneakPre{\deg{d}}} + \ekvdefNoVal{sine}{radian}{\ekvsneakPre{\deg{}}} + \ekvdefNoVal{sine}{internal}{\ekvsneakPre{\sine@rnd}} + \newcommand*\sine{\ekvoptargTF\sine@args{\sine@final{sin}{d}{3}}} + \newcommand*\sine@args[2] + {\ekvset{sine}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine@stop{#2}} + \end{enverb} + + Now we need to define some internal macros to extract the value of each key's + last usage (remember that this will be the argument after the first matching + flag). For that we use one delimited macro per key. + \begin{enverb}[no-tcb,store] + \def\sine@rnd#1\rnd#2#3\sine@stop{\sine@deg#1#3\sine@stop{#2}} + \def\sine@deg#1\deg#2#3\sine@stop{\sine@f#1#3\sine@stop{#2}} + \def\sine@f#1\f#2#3\sine@stop{\sine@final{#2}} + \end{enverb} + After the macros |\sine@rnd|, |\sine@deg|, and |\sine@f| the macro + |\sine@final| will see + |\sine@final|\marg{f}\marg{degree/radian}\marg{round}\marg{num}. Now + |\sine@final| has to expandably deal with those arguments such that the + |\fpeval| macro of \pkg{xfp} gets the correct input. Luckily this part is + pretty easy after the build up we've done until now. In |\fpeval| the + trigonometric functions have names such as |sin| or |cos|, and the degree + taking alternatives just have an appended |d| (so |sind| or |cosd|). So + putting \meta{f} and \meta{degree/radian} together will form the correct + names. + \begin{enverb}[no-tcb,store] + \newcommand*\sine@final[4]{\fpeval{round(#1#2(#4),#3)}} + \makeatother + \end{enverb} + Let's give our macro a test: + \begin{enverb}[restore,inter=\def\RequirePackage#1{}] + \sine{60}\par + \sine[round=10]{60}\par + \sine[f=cos,radian]{pi}\par + \edef\myval{\sine[f=tan]{1}}\texttt{\meaning\myval} + \end{enverb} +\end{example} + +Please note that setting this up a lot more user friendly is easily possible by +utilizing \expkvc\ (see \autoref{sec:expkv-cs}). |