diff options
Diffstat (limited to 'macros/generic/expkv-bundle/pkg-cs.tex')
-rw-r--r-- | macros/generic/expkv-bundle/pkg-cs.tex | 937 |
1 files changed, 937 insertions, 0 deletions
diff --git a/macros/generic/expkv-bundle/pkg-cs.tex b/macros/generic/expkv-bundle/pkg-cs.tex new file mode 100644 index 0000000000..850b9af620 --- /dev/null +++ b/macros/generic/expkv-bundle/pkg-cs.tex @@ -0,0 +1,937 @@ +\genericekv\expkvc{-cs} + +\expkvc\ aids in creating fully expandable macros that take a \kv\ argument. It +implements somewhat efficient solutions to expandable \kv\ parsing without the +user having to worry too much about the details. + +The package supports two main approaches for this. The first is splitting the +keys up into individual arguments, the second preparses the \kv\ list into a +single argument in which accessing the value of individual keys is fast. The +behaviour of the second type is similar to a hash table, so we call that variant +|Hash|, the first type is called |Split|. Both these variants support a number +of so called \emph{primary keys} (a primary key matches an argument, roughly +speaking). + +In addition to these methods there is a structured way to define additional keys +which might build upon the primary keys but not directly relate to an argument. +These keys are called \emph{secondary keys}. Primary and secondary keys belong +to a specific macro (the macro name serves as the \emph{set}). + +A word of advice you should consider: Macros defined with \expkvc\ are simple to +create, and there might be good use cases for them (for instance since they +don't work by assignments but only by argument forwarding logic they have no +issues with implicit or explicit groups whatsoever). But they don't scale as +well as established \kv\ interfaces (think of the idiomatic key definitions with +\pkg{keyval}, or \pkg{l3keys}, or \expkv\ with or without \expkvd), and they are +slower than idiomatic key definitions in packages with fast \kv\ parsing. + + +\subsection{Defining Macros and Primary Keys} + +All macros defined with \expkvc\ have to be previously undefined (or have the +|\meaning| of |\relax|). There is no way to automatically undefine keys once +they are set up -- so to make sure there are no conflicts only new definitions +are allowed. The \emph{set} name (as used by \cs[no-index]{ekvset}) will be +\cs[no-index]{string}\cs[no-index]{\meta{macro}}. + + +\subsubsection{Primary Keys\label{sec:c:primary}} + +The notion of primary keys needs a bit of explanation, or better, the input +syntax for the argument \meta{primary keys} in the following explanations. The +\meta{primary keys} argument should be a \kv\ list in which each \key\ will be +one primary key and \val\ the initial value of said \key\ (and that value is +mandatory, even if you leave it blank that's fine, but you have to explicitly +state it). By default all keys are defined short, but you can define |\long| +keys by prefixing \key\ with |long| (\emph{e.g.}, +|long name=Jonathan P. Spratte| to define a |\long| key called |name|). Due to +some internal implementations it's worth noting that |\long| keys are a +microscopic grain faster. The \meta{cs} will only be defined |\long| if at least +one of the keys was |\long|. For obvious reasons there is no interface in place +to define something as |\protected|. + +To allow keys to start with the word |long| even if you don't want them to be +|\long| you can also prefix them with |short|. The first found prefix of the two +will stop the parsing for prefixes and what remains becomes the \key. + +These rules culminate in the following: + +\begin{enverb}[no-tcb] + \ekvcSplit\foo + { + long short = abc\par + ,short long = def + } + {#1#2} +\end{enverb} +will define a macro |\foo| that knows two primary keys, |short| which is defined +|\long| (so will accept explicit |\par| tokens inside its value at use time), +and |long| which doesn't accept explicit |\par| tokens (leading to a low level +\TeX\ error). The description of |\ekvcSplit| follows shortly. + +There is one exception to the rule that each \key\ in \meta{primary keys} needs +to get a value: If you include a key named |...| without a value this will be a +primary key in which every unknown key will be collected -- and \meta{cs} will +be defined |\long|. The unknown keys will be stored in a way that \emph{most} +\kv\ parsers will parse them correctly (but this is no general guarantee, for +instance \pkg{pgfkeys} can accidentally strip multiple sets of braces at the +wrong moment). See some examples in \autoref{sec:c: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 +some features I deem useful can't be implemented without such internal keys. +Because of this, please don't use key names starting with "EKVC|" as that +should be the private name space. + + +\subsubsection{Split\label{sec:c:split}} + +The split variants will provide the key values as separate arguments. This +limits the number of keys for which this is truly useful. + +\begin{function}{\ekvcSplit} + \begin{syntax} + \cs{ekvcSplit}\meta{cs}\marg{primary keys}\marg{definition} + \end{syntax} + This defines \meta{cs} to be a macro taking one mandatory argument which + should contain a \kv\ list. The \meta{primary keys} will be defined for this + macro (see \autoref{sec:c:primary}). The \meta{definition} is the code that + will be executed. You can access the \val\ of a \key\ by using a macro + parameter from |#1| to |#9|. The order of the macro parameters will be the + order provided in the \meta{primary keys} list (so |#1| is the \val\ of the + \key\ defined first). With \cs{ekvcSplit} you can define macros using at most + nine primary keys. +\end{function} +\begin{example}{Simple macro with \cs[no-index]{ekvcSplit}} + The following defines a macro |\foo| that takes the keys |a| and |b| + and outputs their values in a textual form: + \begin{enverb} + \ekvcSplit\foo{a=a,b=b}{a is #1.\par b is #2.\par} + \foo{} + \foo{b=e} + \end{enverb} +\end{example} + +\begin{function}{\ekvcSplitAndForward} + \begin{syntax} + \cs{ekvcSplitAndForward}\meta{cs}\marg{after}\marg{primary keys} + \end{syntax} + This defines \meta{cs} to be a macro taking one mandatory argument which + should contain a \kv\ list. You can use as many primary keys as you want with + this. The primary keys will be forwarded to \meta{after} as braced arguments + (as many as necessary for your primary keys). The order of the braced + arguments will be the order of your primary key definitions. In \meta{after} + you can use just a single control sequence, or some arbitrary stuff which will + be left in the input stream before your braced values (with one set of braces + stripped from \meta{after}), so both of the following would be fine: + \begin{enverb}[no-tcb] + \ekvcSplitAndForward\foo\foo@aux{keyA = A, keyB = B} + \ekvcSplitAndForward\foo{\foo@aux{more args}}{keyA = A, keyB = B} + \end{enverb} +\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} + \cs{ekvcSplitAndUse}\meta{cs}\marg{primary keys} + \end{syntax} + This will roughly do the same as \cs{ekvcSplitAndForward}, but instead of + 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{quote} + \ttfamily + \meta{cs}\kvarg\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\label{sec:c: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 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 \cs{ekvcValue}, not the parsing, and for a key +access using \cs{ekvcValueFast} it was still about 1.2~times slower). Still to +be future proof, considering the hash variants is a good idea, and to get best +performance but less maintainable code you can resort to the split approach. + +\begin{function}{\ekvcHash} + \begin{syntax} + \cs{ekvcHash}\meta{cs}\marg{primary keys}\marg{definition} + \end{syntax} + This defines \meta{cs} to be a macro taking one mandatory argument which + should contain a \kv\ list. You can use as many primary keys as you want. The + primary keys will be forwarded as a single argument containing every key to + the underlying macro. The underlying macro is defined as \meta{definition}, in + which you can access the \val\ of a \key\ by using + \cs{ekvcValue}\marg{key}|{#1}| (or similar). +\end{function} +\begin{example}{Simple macro with \cs[no-index]{ekvcHash}} + This defines an equivalent macro to the |\foo| defined with \cs{ekvcSplit} + earlier: + \begin{enverb} + \ekvcHash\foo{a=a,b=b}{a is \ekvcValue{a}{#1}.\par + b is \ekvcValue{b}{#1}.\par} + \foo{} + \foo{b=e} + \end{enverb} +\end{example} + +\begin{function}{\ekvcHashAndForward} + \begin{syntax} + \cs{ekvcHashAndForward}\meta{cs}\marg{after}\marg{primary keys} + \end{syntax} + This defines \meta{cs} to be a macro taking one mandatory argument which + should contain a \kv\ list. You can use as many primary keys as you want. The + primary keys will be forwarded as a single argument containing every key to + \meta{after}. You can use a single macro for \meta{after} or use some + arbitrary stuff, which will be left in the input stream before the hashed \kv\ + list with one set of braces stripped. In the macro called in \meta{after} you + can access the \val\ of a \key\ by using \cs{ekvcValue}\marg{key}|{#1}| (or + whichever argument the hashed \kv\ list will be in). +\end{function} +\begin{example}{Using \cs[no-index]{ekvcHashAndForward}} + This defines a macro |\foo| processing two keys, and passing the result to + |\foobar|: + \begin{enverb} + \ekvcHashAndForward\foo\foobar{a=a,b=b} + \newcommand*\foobar[1]{a is \ekvcValue{a}{#1}.\par + b is \ekvcValue{b}{#1}.\par} + \foo{} + \foo{b=e} + \end{enverb} +\end{example} + +\begin{function}{\ekvcHashAndUse} + \begin{syntax} + \cs{ekvcHashAndUse}\meta{cs}\marg{primary keys} + \end{syntax} + This will roughly do the same as \cs{ekvcHashAndForward}, but instead of + specifying what will be used after hashing the keys during the definition, + \meta{cs} will use what follows the \kv\ list. So its syntax will be + \begin{quote} + \ttfamily + \meta{cs}\kvarg\marg{after} + \end{quote} +\end{function} + +\begin{function}{\ekvcValue} + \begin{syntax} + \cs{ekvcValue}\marg{key}\marg{key list} + \end{syntax} + This is a safe way to access your keys in a hash variant. \key\ is the key + which's \val\ you want to use out of the \meta{key list}. \meta{key list} + should be the key list argument forwarded to your underlying macro by + \cs{ekvcHash}, \cs{ekvcHashAndForward}, or \cs{ekvcHashAndUse}. It will be + tested whether the hash function to access that \key\ exists, the \key\ + argument is not empty, and that the \meta{key list} really contains a \val\ of + that \key. This macro needs exactly two steps of expansion and if used inside + of an |\edef| or |\expanded| context will protect the \val\ from further + expanding. +\end{function} + +\begin{function}{\ekvcValueFast} + \begin{syntax} + \cs{ekvcValueFast}\marg{key}\marg{key list} + \end{syntax} + This behaves similar to \cs{ekvcValue}, but \emph{without any} safefy tests. + As 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 \meta{key} name). + Note that this will not only throw low level errors but result in undefined + behaviour as well! This macro needs exactly three steps of expansion in the + no-error case. +\end{function} + +\begin{function}{\ekvcValueSplit} + \begin{syntax} + \cs{ekvcValueSplit}\marg{key}\marg{key list}\marg{next} + \end{syntax} + If you need a specific \key\ from a \meta{key list} more than once, it'll be a + good idea to only extract it once and from then on keep it as a separate + argument (or if you want to forward this value to another macro). Hence the + macro \cs{ekvcValueSplit} will extract one specific \key's \val\ 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 \cs{ekvcValue} and runs + the same tests. +\end{function} +\begin{example} + {Splitting of a key from a hash list using \cs[no-index]{ekvcValueSplit}} + 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{enverb} + \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{enverb} +\end{example} + +\begin{function}{\ekvcValueSplitFast} + \begin{syntax} + \cs{ekvcValueSplitFast}\marg{key}\marg{key list}\marg{next} + \end{syntax} + This behaves just like \cs{ekvcValueSplit}, but it won't run the safety tests, + hence it is faster but more error prone, just like the relation between + \cs{ekvcValue} and \cs{ekvcValueFast}. +\end{function} + + +\subsection{Secondary Keys\label{sec:c:secondary}} + +To lift some limitations of each primary key matching one argument or hash +entry, you can define secondary keys. Those have to be defined for each macro +individually but it doesn't matter whether that macro was set up as a split or +hash variant. + +Secondary keys can have a \prefix\ (|long|), and must have a \type\ +(like |meta|). Some \types\ might require some \prefix\ while other +\types\ might forbid the usage of a specific \prefix. + +Please keep in mind that key names shouldn't start with "EKVC|". + +\begin{function}{\ekvcSecondaryKeys} + \begin{syntax} + \cs{ekvcSecondaryKeys}\meta{cs}\kvarg + \end{syntax} + This is the front facing macro to define secondary keys. For the macro + \meta{cs} define \key\ to have definition \val. The general syntax for \key\ + should be + \begin{quote}\ttfamily + \meta{prefix} \meta{name} + \end{quote} + Where \meta{prefix} is a space separated list of optional \prefixes\ + followed by one \type. The syntax of \meta{value} is dependent on the + used \type. +\end{function} + + +\subsubsection{\textit{Prefixes}} + +Currently there is only one \prefix\ available, which is +\begin{function}[module=expkv-cs prefix]{long} + The following key will be defined |\long|. +\end{function} + + +\subsubsection{\textit{Types}\label{sec:c:secondarytypes}} + +Compared to \expkvd\ you might notice that the \types\ here are much fewer. +Unfortunately the expansion only concept doesn't allow for great variety in the +common key \types. + +The syntax examples of the \types\ will show which \prefix\ will be +automatically used by printing those black (\texttt{\enfprefix{long}}), which +will be available in grey (\texttt{\allprefix{long}}), and which will be +disallowed in red (\texttt{\notprefix{long}}). This will be put flush right next +to the syntax line. + +If a secondary key references another key it doesn't matter whether that other +key is a primary or secondary key (unless explicitly stated otherwise). + +\begin{function}[module=expkv-cs type]{meta} + \begin{syntax} + meta \key\ = \kvarg \prefixes2 + \end{syntax} + With a |meta| key you can set other keys. Whenever \meta{key} is used the keys + in the \kv\ list will be set to the values given there. You can use the \val\ + given to \key\ by using |#1| in the \kv\ list. +\end{function} + +\begin{function}[module=expkv-cs type]{nmeta} + \begin{syntax} + nmeta \key\ = \kvarg \prefixes3 + \end{syntax} + An |nmeta| key is like a |meta| key, but it doesn't take a value at use time, + so the \kv\ list is static. +\end{function} + +\begin{function}[module=expkv-cs type]{alias} + \begin{syntax} + alias \meta{key} = \marg{key_2} \prefixes3 + \end{syntax} + This assigns the definition of \meta{key_2} to \key. As a result \key\ is an + alias for \meta{key_2} behaving just the same. Both the \Vkey\ and the \Nkey\ + will be copied if they are defined when |alias| is used. Of course, + \meta{key_2} has to be defined as at least one of \Nkey\ or \Vkey. +\end{function} + +\begin{function}[module=expkv-cs type]{default} + \begin{syntax} + default \key\ = \marg{default} \prefixes3 + \end{syntax} + If \key\ is defined as a \Vkey\ you can define a \Nkey\ version with this. The + \Nkey\ 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 + (see \cs{ekvcChange} in \autoref{sec:c:change} for this). If \key\ isn't yet + defined this results in an error. +\end{function} + +\begin{function}[module=expkv-cs type]{enum} + \begin{syntax} + enum \key\ = \marg{key_2}\kvarg \prefixes2 + \end{syntax} + This defines \key\ to only accept the values given in the list of the second + argument of its definition. It forwards the position of \val\ in that list to + \meta{key_2} (zero-based). The \meta{key_2} has to already be defined by the + time an |enum| key is set up. Each \val\ in the list (and at use time) is + |\detokenize|d, so no expansion takes place here. + + If you use |enum| twice on the same \key\ the new values will again start at + zero (so it is possible to define multiple values with the same outcome), + however since you can't skip values you'll have to use the same as in the + first call for values with just a single variant. There is no interface to + delete existing values. +\end{function} +\begin{example}{Enumerating choices with the \texttt{enum} \type} + First a small example that might give you an idea of what the description + above could mean: + \begin{enverb} + \ekvcSplit\foo{k-internal=-1}{#1} + \ekvcSecondaryKeys\foo + {enum k = {k-internal}{a,b,c}} + \foo{}\foo{k=a}\foo{k=b}\foo{k=c} + \end{enverb} +\end{example} +\begin{example}{A slightly more complicated usage of the \texttt{enum} \type} + We can define a choice setup that might do different things based on the + choice encountered, and the numeric value is easy to parse using + \cs[no-index]{ifcase}: + \begin{enverb} + \ekvcSplit\foo{k-internal=-1} + {% + \ifcase#1 + is\or + This\or + easy% + \else + .% + \fi + } + \ekvcSecondaryKeys\foo + {enum k = {k-internal}{a,b,c}} + \foo{k=b} \foo{k=a} \foo{k=c}\foo{} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-cs type]{choice} + \begin{syntax} + choice \key\ = \marg{key_2}\kvarg \prefixes2 + \end{syntax} + This is pretty similar to an |enum|, but unlike with |enum| the forwarded + \val\ will not be numeric, instead the \val\ as given during the definition + time will be forwarded. This means that while the user input has to match in a + |\detokenize|d form, the \val\ might still expand further during your macro's + expansion (if what you provided as a choice is expandable). +\end{function} +\begin{example}{Filtering possible values with the \texttt{choice} \type} + We could use this to filter out the possible vertical placements of a \LaTeXe\ + |tabular|: + \begin{enverb} + \ekvcSplit\foo{v-internal=c,a=t,b=c,c=b} + {% + \begin{tabular}[#1]{@{} c @{:} c @{}} + a & #2\\ + b & #3\\ + c & #4\\ + \end{tabular}% + } + \ekvcSecondaryKeys\foo + {choice v = {v-internal}{t,c,b}} + \foo{} \foo{v=t} \foo{v=c} \foo{v=b} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-cs type]{aggregate,e-aggregate} + \begin{syntax} + aggregate \key\ = \marg{primary}\marg{definition} \prefixes3 + \end{syntax} + While other key \types\ replace the current value of the associated + primary key, with |aggregate| you can create keys that append or prepend (or + whatever you like) the new value to the current one. Your definition of an + |aggregate| key must be exactly two \TeX\ arguments, where \meta{primary} + should be the name of a primary key, and \meta{definition} the way you want to + store the current and the new value. Inside \meta{definition} you can use |#1| + for the current, and |#2| for the new value. The \meta{definition} will not + expand any further during the entire parsing for |aggregate|, whereas in + |e-aggregate| everything that ends up in \meta{definition} (so whatever you + provide including the values in |#1| and |#2|) will be fully expanded (using + the |\expanded| primitive), so use |\noexpand| and |\unexpanded| to protect + what shouldn't be expanded. + The resulting \meta{key} will inherit being either |short| or |long| from the + \meta{primary} key. +\end{function} +\begin{example}{Building a list with the \texttt{aggregate} \type} + The following defines an internal key (|k-internal|), which is used to build a + comma separated list from each call of the user facing key (|k|): + \begin{enverb} + \ekvcSplit\foo + {k-internal=0,color=red} + {\textcolor{#2}{#1}} + \ekvcSecondaryKeys\foo + {aggregate k = {k-internal}{#1,#2}} + \foo{}\par + \foo{k=1,k=2,k=3,k=4} + \end{enverb} +\end{example} +\begin{example}{Building a convoluted list with the \texttt{aggregate} \type} + But also more strange stuff could end there, like macros or using the same + value multiple times: + \begin{enverb}[no-tcb] + \ekvcSecondaryKeys\foo + {aggregate k = {k-internal}{\old{#1}\new{#2\old{#1}}}} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-cs type]{flag-bool} + \begin{syntax} + flag-bool \key\ = \meta{cs} \prefixes3 + \end{syntax} + This is a secondary \type\ that doesn't involve any of the primary or other + secondary keys. This defines \key\ to take a value, which should be either + |true| or |false|, and set the flag called \meta{cs} accordingly as a boolean. + If \meta{cs} isn't defined yet it will be initialised as a flag. Note that the + flag will not be set to a specific state automatically so a flag set in one + macro might affect every other macro in the current scope. Please also read + \autoref{sec:c:flags}. +\end{function} +\begin{example}{Using a Boolean flag with the \texttt{flag-bool} \type} + Provide a key |bold| to turn the output of our macro bold if the associated + flag is |true|. + \begin{enverb} + \ekvcSplit\foo{a=a,b=b} + {% + \ekvcFlagIf\fooFlag + {\textbf{a is #1 and b is #2}\par} + {a is #1 and b is #2\par}% + } + \ekvcSecondaryKeys\foo{flag-bool bold = \fooFlag} + \foo{}\foo{bold=true}\foo{}\foo{bold=false}\foo{} + \end{enverb} +\end{example} + +\begin{function}[module=expkv-cs type]{flag-true,flag-false} + \begin{syntax} + flag-true \key\ = \meta{cs} \prefixes3 + \end{syntax} + This is similar to |flag-bool|, but the \meta{key} will be a \Nkey\ and if + used will set the flag to either |true| or |false|. If \meta{cs} isn't defined + yet it will be initialised as a flag. Note that the flag will not be set to a + specific state automatically. Please also read \autoref{sec:c:flags}. +\end{function} + +\begin{function}[module=expkv-cs type]{flag-raise} + \begin{syntax} + flag-raise \key\ = \meta{cs} \prefixes3 + \end{syntax} + This defines \key\ to be a \Nkey\ that will raise the flag called \meta{cs} on + usage. If \meta{cs} isn't defined yet it will be initialised as a flag. Note + that the flag will not be set to a specific state automatically. Please also + read \autoref{sec:c:flags}. +\end{function} + + +\subsection{Changing the Initial Values\label{sec:c:change}} + +\begin{function}{\ekvcChange} + \begin{syntax} + \cs{ekvcChange}\meta{cs}\kvarg + \end{syntax} + This processes the \kv\ list for the macro \meta{cs} to set new defaults for + it (meaning the initial values used if you don't provide anything at use time, + not those specified with the |default| \type). \meta{cs} should be defined + with \expkvc\ (but it doesn't matter if it's a split or hash variant). 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 initial values were). If there was an + unknown key forwarding added to \meta{cs} (see \autoref{sec:c:unknown}) any + unknown key will be stored inside the list of unknown keys as well. + \cs{ekvcChange} is not expandable! +\end{function} +\begin{example} + {Changing the values for future calls using \cs[no-index]{ekvcChange}} + With \cs{ekvcChange} we can now do the following: + \begin{enverb} + \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{enverb} +\end{example} +\begin{example}{A typical setup macro for \expkvc\ macros} + As a result with this the typical setup macro could be implemented: + \begin{enverb}[no-tcb] + \ekvcHashAndUse\fooKV{keyA=a,keyB=b} + \def\fooA#1{\fooKV{#1}\fooAinternal} + \def\fooB#1{\fooKV{#1}\fooBinternal} + \protected\def\foosetup{\ekvcChange\fooKV} + \end{enverb} + Of course the usage is limited to a single macro |\fooKV|, 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 like |\fooA| and |\fooB| do in this example. +\end{example} + + +\subsection{Handling Unknown Keys\label{sec:c: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\ and the \val\ will be forwarded with one set of surrounding spaces and +braces, so a \kv\ pair will result in \texttt{\vissp{ \marg{key} = \marg{val} }} +and a \Nkey\ is forwarded as \texttt{\vissp{ \marg{key} }} (this way most other +\kv\ implementations should parse the correct input). + +The exact behaviour differs slightly between the two variants (as all primary +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 (since only the position in the list determines the argument, +not the name). +\begin{example}{Using unknown key handlers to wrap another \kv\ enabled macro} + The following will forward any unknown key to |\includegraphics| to control + the appearance while processing its own keys: + \begin{enverb}[same-line=0.6] + \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{enverb} +\end{example} + +Inside the hash variants the unknown keys list will be put inside the hash named +|...| (we have to use some name, and this one seems reasonable). 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. +\begin{example}{Wrapping an existing macro, but with a hash variant} + The following is more or less equivalent to the above example, 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{enverb}[same-line=0.76] + \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 + \ekvcValueSplit{...}{#1}{\includegraphics[}]% + {#2}\par + } + \foo[width=\linewidth, b=c] + {example-image-duck-portrait} + \end{enverb} +\end{example} + + +\subsection{Flags\label{sec:c:flags}} + +The idea of flags is taken from \pkg{expl3}. They provide a way to store +numerical information expandably, however only incrementing and accessing works +expandably, decrementing is unexpandable. A flag has a height, which is a +numerical value, and which can be raised by 1. Flags come at a high +computational cost (accessing them is slow and they require more memory than +normal \TeX\ data types like registers, both issues getting linearly worse with +the height), so don't use them if not necessary. + +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 \types\ involving flags +bear in mind that they can affect other macros using the same flags at the +current scope! + +\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}. + +\begin{function}{\ekvcFlagNew} + \begin{syntax} + \cs{ekvcFlagNew}\meta{flag} + \end{syntax} + This initialises the macro \meta{flag} as a new flag. It isn't checked 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 + \cs[no-index]{ifnum} for example and it will terminate the number scanning on + its own). + + All other macros dealing with flags take as a parameter a macro defined as a + \meta{flag} with \cs{ekvcFlagNew}. +\end{function} + +\begin{function}{\ekvcFlagHeight} + \begin{syntax} + \cs{ekvcFlagHeight}\meta{flag} + \end{syntax} + This expands to the current height of \meta{flag} in a single step of + expansion (without a trailing space). +\end{function} + +\begin{function}{\ekvcFlagRaise} + \begin{syntax} + \cs{ekvcFlagRaise}\meta{flag} + \end{syntax} + This expandably raises the height of \meta{flag} by 1. +\end{function} + +\begin{function}{\ekvcFlagSetTrue,\ekvcFlagSetFalse} + \begin{syntax} + \cs{ekvcFlagSetTrue}\meta{flag} + \end{syntax} + By interpreting an even value as false and an odd value as true we can use a + flag as a boolean. This expandably sets \meta{flag} to true or false, + respectively, by raising it if necessary. +\end{function} + +\begin{function}{\ekvcFlagIf} + \begin{syntax} + \cs{ekvcFlagIf}\meta{flag}\marg{true}\marg{false} + \end{syntax} + This interprets a \meta{flag} as a boolean and expands to either \meta{true} + or \meta{false}. +\end{function} + +\begin{function}{\ekvcFlagIfRaised} + \begin{syntax} + \cs{ekvcFlagIfRaised}\meta{flag}\marg{true}\marg{false} + \end{syntax} + This tests whether the \meta{flag} is raised, meaning it has a height greater + than zero, and if so expands to \meta{true} else to \meta{false}. +\end{function} + +\begin{function}{\ekvcFlagReset,\ekvcFlagResetGlobal} + \begin{syntax} + \cs{ekvcFlagReset}\meta{flag} + \end{syntax} + This resets a flag (so restores its height to 0). This operation is \emph{not} + expandable and done locally for \cs{ekvcFlagReset} and globally for + \cs{ekvcFlagResetGlobal}. If you really intend to use flags you can reset them + every now and then to keep the performance hit low. +\end{function} + +\begin{function}{\ekvcFlagGetHeight} + \begin{syntax} + \cs{ekvcFlagGetHeight}\meta{flag}\marg{next} + \end{syntax} + This retrieves the current height of the \meta{flag} and provides it as a + braced argument to \meta{next}, leaving \meta{next}\marg{height} in the input + stream. +\end{function} + +\begin{function}{\ekvcFlagGetHeights} + \begin{syntax} + \cs{ekvcFlagGetHeights}\marg{flag-list}\marg{next} + \end{syntax} + This retrieves the current height of each \meta{flag} in the \meta{flag-list} + and provides them as a single argument to \meta{next}. Inside that argument + each height is enclosed in a set of braces individually. The \meta{flag-list} + is just a single argument containing the \meta{flag}s. So a usage like + |\ekvcFlagGetHeights{\myflagA\myflagB}{\stuff}| will expand to + |\stuff{|\marg{height-A}\marg{height-B}|}|. +\end{function} + + +\subsection{Further Examples} + +\begin{example}{Defining an expandable \kv\ macro with an optional argument} + Using \cs[no-index]{NewExpandableDocumentCommand} or \expkv's \cs{ekvoptarg} + or \cs{ekvoptargTF} and forwarding arguments one can easily define \kv\ macros + with actual optional and mandatory arguments as well. A small nonsense + example: + \begin{enverb}[below,inter=resulting in] + \makeatletter + \newcommand*\nonsense{\ekvoptarg\nonsense@a{}} + \ekvcHashAndForward\nonsense@a\nonsense@b + { + keyA = A, + keyB = B, + keyC = c, + keyD = d, + } + \newcommand*\nonsense@b[2] + {% + \begin{tabular}{lll|} + key & A & \ekvcValue{keyA}{#1} \\ + & B & \ekvcValue{keyB}{#1} \\ + & C & \ekvcValue{keyC}{#1} \\ + & D & \ekvcValue{keyD}{#1} \\ + \multicolumn{2}{l}{mandatory} & #2 \\ + \end{tabular}% + } + \makeatother + \nonsense{} % do nonsense + \nonsense[keyA=hihi]{haha} + \nonsense[keyA=hihi, keyB=A]{hehe} + \nonsense[keyC=huhu, keyA=hihi, keyB=A]{haha} + \end{enverb} +\end{example} + +\begin{example}{The \cs[no-index]{sine} example revisited} + In \autoref{sec:expkv:sneak} I presented an expandable macro to calculate the + sine of some user input with a few keys, and there I hinted to \expkvc, so + here's the same function implemented with |\ekvcSplitAndForward|. There is a + small difference here, we need to use an internal key to store whether degrees + or radians will be used, but we don't need to use an internal key to collect + the values of our individual keys in the correct order. + \begin{enverb} + \makeatletter + \newcommand\sine{\ekvoptarg\sine@kv{}} + \ekvcSplitAndForward\sine@kv\sine@do + { + f = sin + ,internal = d + ,round = 3 + } + \ekvcSecondaryKeys\sine@kv + { + nmeta degree = internal=d + ,nmeta radian = internal={} + } + \newcommand*\sine@do[4]{\fpeval{round(#1#2(#4),#3)}} + \makeatother + \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} + + +\subsection{Freedom for Keys!} + +If this had been the \TeX{}book this subsection would have had 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 key \types\ listed in \autoref{sec:c:secondary} 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 the result of +|\string|\meta{cs} is used. This can be exploited to define additional keys with +arbitrary code. Consider the following \emph{bad} example: +\begin{enverb}[no-tcb] + \ekvcSplit\foo{a=A,b=B}{a is #1.\par b is #2.\par} + \protected\ekvdef{\string\foo}{c}{\def\fooC{#1}} +\end{enverb} +This would define a key named |c| that will store its \val\ 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 +creating a key that pre-parses the input and after that passes the parsed value +on. This parsing would have to be completely expandable though (and we could +perhaps also implement this using the |e-aggregate| \type). 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 \val\ on to \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} +\begin{example} + {Forwarding pre-parsed keys to an \expkvc\ key with \cs[no-index]{ekvcPass}} + With this we could for example split the value of a key at a hyphen and pass + the parts to different keys: + \begin{enverb} + \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{enverb} +\end{example} + +Additionally, there is a more general version of the |aggregate| secondary key +type, namely the |process| key type: + +\begin{function}[module=expkv-cs type]{process} + \begin{syntax} + process \key\ = \marg{primary}\marg{definition} \prefixes2 + \end{syntax} + This will grab the current value of a \meta{primary} key as |#1| (without + changing the current value) and the new value as |#2| and leave all the + processing to \meta{definition}. You should use \cs{ekvcPass} to forward the + values afterwards. Unlike |aggregate| you can specify whether the \meta{key} + should be long or not, this isn't inherited from the \meta{primary} key. Keep + in mind that you could easily break things here if your code does not work by + expansion. +\end{function} +\begin{example}{Filtering out values with the \texttt{process} \type} + We could define a key that only accepts values greater than the current value + with this: + \begin{enverb} + \ekvcSplit\foo{internal=5}{a is #1.\par} + \ekvcSecondaryKeys\foo + { + process a={internal} + {\ifnum#1<#2 \ekvcPass\foo{internal}{#2}\fi} + } + \foo{a=1} + \foo{a=5} + \foo{a=9} + \end{enverb} +\end{example} +\begin{example}{Filtering out values with the \texttt{e-aggregate} \type} + The same is possible with an |e-aggregate| key as well though: + \begin{enverb}[no-tcb] + \ekvcSplit\foo{internal=5}{a is #1.\par} + \ekvcSecondaryKeys\foo + { + e-aggregate a={internal} + {\ifnum#1<#2 \unexpanded{#2}\else\unexpanded{#1}\fi} + } + \end{enverb} +\end{example} + +\subsection{Useless Macros} + +These macros are most likely of little to no interest to users. + +\begin{function}{\ekvcDate,\ekvcVersion} + These two macros store the version and date of the package/generic code. +\end{function} |