diff options
Diffstat (limited to 'obsolete/macros/generic/expkv-cs/expkv-cs.dtx')
-rw-r--r-- | obsolete/macros/generic/expkv-cs/expkv-cs.dtx | 3431 |
1 files changed, 3431 insertions, 0 deletions
diff --git a/obsolete/macros/generic/expkv-cs/expkv-cs.dtx b/obsolete/macros/generic/expkv-cs/expkv-cs.dtx new file mode 100644 index 0000000000..561dec7af9 --- /dev/null +++ b/obsolete/macros/generic/expkv-cs/expkv-cs.dtx @@ -0,0 +1,3431 @@ +% \iffalse meta-comment +% +% File: expkv-cs.dtx Copyright (C) 2020-2022 Jonathan P. Spratte +% +% This work may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this license or +% (at your option) any later version. The latest version of this license is in +% the file: +% +% http://www.latex-project.org/lppl.txt +% +% ------------------------------------------------------------------------------ +% +%<*driver>^^A>>= +\def\nameofplainTeX{plain} +\ifx\fmtname\nameofplainTeX\else + \expandafter\begingroup +\fi +\input l3docstrip.tex +\askforoverwritefalse +\preamble + +-------------------------------------------------------------- +expkv-cs -- define expandable key=val macros using expkv +E-mail: jspratte@yahoo.de +Released under the LaTeX Project Public License v1.3c or later +See http://www.latex-project.org/lppl.txt +-------------------------------------------------------------- + +Copyright (C) 2020-2022 Jonathan P. Spratte + +This work may be distributed and/or modified under the conditions of the +LaTeX Project Public License (LPPL), either version 1.3c of this license or +(at your option) any later version. The latest version of this license is in +the file: + + http://www.latex-project.org/lppl.txt + +This work is "maintained" (as per LPPL maintenance status) by + Jonathan P. Spratte. + +This work consists of the file expkv-cs.dtx +and the derived files expkv-cs.pdf + expkv-cs.sty + expkv-cs.tex + t-expkv-cs.tex + +\endpreamble +% stop docstrip adding \endinput +\postamble +\endpostamble +\generate{\file{expkv-cs.sty}{\from{expkv-cs.dtx}{pkg}}} +\generate{\file{expkv-cs.tex}{\from{expkv-cs.dtx}{tex}}} +\generate{\file{t-expkv-cs.tex}{\from{expkv-cs.dtx}{ctx}}} +\ifx\fmtname\nameofplainTeX + \expandafter\endbatchfile +\else + \expandafter\endgroup +\fi +% +\IfFileExists{expkv-cs.tex}{\input{expkv-cs.tex}}{} +\ProvidesFile{expkv-cs.dtx} + [\csname ekvcDate\endcsname\ define expandable key=val macros using expkv] +\PassOptionsToPackage{full}{textcomp} +\documentclass{l3doc} +\RequirePackage[oldstylenums,nott]{kpfonts} +\input{glyphtounicode} +\pdfgentounicode=1 +\RequirePackage{tcolorbox} +\newtcolorbox{exresult}[2][] + {colback=expkvgrey!10!white,colframe=expkvgrey,fontupper=\small,width={#2},#1} +\newtcbox\exres[1][] + { + colback=expkvgrey!10!white + ,colframe=expkvgrey + ,size=small + ,nobeforeafter + ,tcbox raise base + ,fontupper=\small + ,#1 + } +\RequirePackage{listings} +\RequirePackage{booktabs} +\RequirePackage{array} +\RequirePackage{collcell} +\RequirePackage{siunitx} +\RequirePackage{xcolor} +\RequirePackage{caption} +\RequirePackage{microtype} +\RequirePackage{accsupp} +\lstdefinelanguage{expkvc}[primitive]{TeX} + { + ,moretexcs=[4]^^A e-TeX + { + expanded, + numexpr, + protected, + } + ,moretexcs=[5]^^A plain/LaTeX + { + approx, + begin, + empty, + item, + LaTeX, + makeatletter,makeatother,multicolumn, + newcommand,newdimen, + RequirePackage, + rule, + TeX, + textit,texttt, + usepackage, + usemodule, + } + ,moretexcs=[6]^^A used packages + { + ^^A xfp + fpeval, + ^^A keyval + setkeys, + ^^A yax + defactiveparameter,storevalue,setparameterlist + } + ,moretexcs=[2]^^A expkv-cs macros + { + ekvcSplit,ekvcSplitAndForward,ekvcSplitAndUse, + ekvcHash,ekvcHashAndForward,ekvcHashAndUse, + ekvcValue,ekvcValueFast,ekvcValueSplit,ekvcValueSplitFast, + ekvcSecondaryKeys, + ekvcChange, + ekvcFlagNew, + ekvcFlagHeight, + ekvcFlagRaise, + ekvcFlagSetTrue,ekvcFlagSetFalse, + ekvcFlagIf,ekvcFlagIfRaised, + ekvcFlagReset, + ekvcFlagGetHeight,ekvcFlagGetHeights, + ekvcPass + } + ,moretexcs=[3]^^A expkv-pkg macros + { + ^^A expkv + ekvdef,ekvdefNoVal,^^A + ekvlet,ekvletNoVal,ekvletkv,ekvletkvNoVal,^^A + ekvdefunknown,ekvdefunknownNoVal,^^A + ekvredirectunknown,ekvredirectunknownNoVal,^^A + ekvset,ekvsetSneaked,^^A + ekvsetdef,ekvsetSneakeddef,ekvsetdefSneaked,^^A + ekvparse,^^A + ekvVersion,ekvDate,^^A + ekvifdefined,ekvifdefinedNoVal,ekvifdefinedset,^^A + ekvbreak,ekvbreakPreSneak,ekvbreakPostSneak,^^A + ekvsneak,ekvsneakPre,^^A + ekvchangeset,^^A + ekvoptarg,ekvoptargTF,^^A + ekverr, + ^^A expkv-def + ekvdefinekeys + } + } +\colorlet{codeparam}{cyan!65!black} +\def\mylstwd{.55em} +\lstset + { + ,language=expkvc + ,flexiblecolumns=false + ,basewidth=\mylstwd + ,gobble=2 + ,basicstyle=\fontfamily{jkp}\itshape + ,texcsstyle=*[1]{\bfseries\color{expkvgrey}} ^^A primitives + ,texcsstyle=*[2]{\bfseries\color{expkvred}} ^^A expkv + ,texcsstyle=*[3]{\color{expkvred}} ^^A expkv-pkg + ,texcsstyle=*[4]{\bfseries\color{expkvgrey}} ^^A e-TeX + ,texcsstyle=*[5]{\bfseries\color{expkvgrey}} ^^A plain/LaTeX macros + ,texcsstyle=*[6]{} ^^A macros of other packages + ,commentstyle=\color[gray]{0.4} + ,literate= + {\{} {{\CodeUpBf\{}}{1} + {\}} {{\CodeUpBf\}}}{1} + {$} {{\CodeUpBf\$}}{1} + {[} {{\CodeUp[}}{1} + {]} {{\CodeUp]}}{1} + {(} {{\CodeUp(}}{1} + {)} {{\CodeUp)}}{1} + {*} {{$*$}}{1} + {1} {{\CodeUp{1}}}{1} + {2} {{\CodeUp{2}}}{1} + {3} {{\CodeUp{3}}}{1} + {4} {{\CodeUp{4}}}{1} + {5} {{\CodeUp{5}}}{1} + {6} {{\CodeUp{6}}}{1} + {7} {{\CodeUp{7}}}{1} + {8} {{\CodeUp{8}}}{1} + {9} {{\CodeUp{9}}}{1} + {0} {{\CodeUp{0}}}{1} + {##} {{\CodeColored{codeparam}{1}{\#}}}{1} + {##1}{{\CodeColored{codeparam}{2}{\#1}}}{2} + {##2}{{\CodeColored{codeparam}{2}{\#2}}}{2} + {##3}{{\CodeColored{codeparam}{2}{\#3}}}{2} + {##4}{{\CodeColored{codeparam}{2}{\#4}}}{2} + {##5}{{\CodeColored{codeparam}{2}{\#5}}}{2} + {##6}{{\CodeColored{codeparam}{2}{\#6}}}{2} + {##7}{{\CodeColored{codeparam}{2}{\#7}}}{2} + {##8}{{\CodeColored{codeparam}{2}{\#8}}}{2} + {##9}{{\CodeColored{codeparam}{2}{\#9}}}{2} + {<key>}{{$\langle$}key{$\rangle$}}{5} + {<set>}{{$\langle$}set{$\rangle$}}{5} + {<set1>}{{$\langle$}set1{$\rangle$}}{6} + {<set2>}{{$\langle$}set2{$\rangle$}}{6} + ^^A,literate=*{<key>}{\key}{4}{<set>}{\set}{4} + } +\newcommand*\CodeColored[3]{\textcolor{#1}{\makebox[\dimexpr\mylstwd*#2]{#3}}} +\newcommand*\CodeUpBf[1]{\makebox[\mylstwd]{\textup{\textbf{#1}}}} +\newcommand*\CodeUp[1]{\makebox[\mylstwd]{\textup{#1}}} +\RequirePackage{randtext} +\let\metaORIG\meta +\protected\def\meta #1{\texttt{\metaORIG{#1}}} +\renewcommand*\thefootnote{\fnsymbol{footnote}} +\definecolor{expkvred}{HTML}{9F393D} +\colorlet{expkvgrey}{black!75} +\makeatletter +\newcommand*\example{\par\smallskip\noindent\textit{Example:} \ignorespaces} +\newcommand*\expkv + {^^A + \texorpdfstring + {^^A + \mbox + {^^A + \BeginAccSupp{ActualText=expkv}^^A + \href{https://github.com/Skillmon/tex_expkv} + {^^A + \rmfamily + \bfseries + {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A + \lower.493ex + \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A + \kern-.18em{\color{expkvred}v}^^A + }^^A + \EndAccSupp{}^^A + }^^A + } + {expkv}^^A + } +\newcommand*\expkvpkg[1] + {^^A + \texorpdfstring + {^^A + \mbox + {^^A + \BeginAccSupp{ActualText=expkv-#1}^^A + \href{https://github.com/Skillmon/tex_expkv-#1} + {^^A + \rmfamily + \bfseries + {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A + \lower.493ex + \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A + \kern-.18em{\color{expkvred}v}^^A + {\color{expkvgrey}^^A + \kern.05em\rule[-.1ex]{.08em}{1.2ex}\kern.05em\textsc{#1}^^A + }^^A + }^^A + \EndAccSupp{}^^A + }^^A + } + {expkv-#1}^^A + } +\newcommand*\expkvd{\expkvpkg{def}} +\newcommand*\expkvc{\expkvpkg{cs}} +\newcommand\kv{\meta{key}=\meta{value}} +\newcommand\key{\meta{key}} +\newcommand\val{\meta{value}} +\newcommand\set{\meta{set}} +\newcommand\enflong{\textcolor{black}{long}} +\newcommand\alllong{\textcolor{gray}{long}} +\newcommand\notlong{\textcolor{red!80!black}{long}} +\newcommand\prefixes[1] + {^^A + \hfill + \ifcase\numexpr#1\relax\or + \enflong\or + \alllong\or + \notlong\fi + } +\hypersetup{linkcolor=red!80!black,urlcolor=purple!80!black} +\DoNotIndex{\def,\edef,\,,\=,\begingroup,\catcode,\chardef,\csname,\endcsname} +\DoNotIndex{\endgroup,\endinput,\errmessage,\expandafter,\input,\let,\long,\z@} +\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{\number,\detokenize,\meaning,\lccode,\lowercase} +\DoNotIndex{\^,\\,\@firstofone,\@firstoftwo,\@gobble,\@secondoftwo} +\DoNotIndex{\ifcsname} +\DoNotIndex{\ifx} +\DoNotIndex{\ifodd} +\DoNotIndex{\ifnum} +\DoNotIndex{\ifdefined} +\DoNotIndex{\iffalse} +\DoNotIndex{\iftrue} +\DoNotIndex{\else} +\DoNotIndex{\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 +\@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} + \title + {^^A + \texorpdfstring + {^^A + \huge\expkvc + \\[\medskipamount] + \Large define expandable \kv\ macros using \expkv + }{expkv-cs - define expandable key=val macros using expkv}^^A + } + \date{\ekvcDate\space v\ekvcVersion} + \author{Jonathan P. Spratte\thanks{\protect\randomize{jspratte@yahoo.de}}} + \DocInput{expkv-cs.dtx} +\end{document} +%</driver>^^A=<< +% \fi +% +% \maketitle +% \renewcommand*\thefootnote{\arabic{footnote}} +% +% \begin{abstract} +% \noindent\parfillskip=0pt +% \expkvc\ provides two small interfaces to define expandable \kv\ macros using +% \expkv. It therefore lowers the entrance boundary to expandable \kv\ macros. +% The stylised name is \expkvc\ but the files use \file{expkv-cs}, this is due +% to CTAN-rules which don't allow \string| in package names since that is the +% pipe symbol in *nix shells. +% \end{abstract} +% +% \tableofcontents +% +% \begin{documentation}^^A>>= +% +% \section{Documentation} +% +% The \expkv\ package enables the new possibility of creating \kv\ macros which +% are fully expandable. The creation of such macros is however cumbersome for +% the average user. \expkvc\ tries to step in here. It provides interfaces to +% define \kv\ macros without worrying too much about the implementation. In case +% you're wondering now, the |cs| in \expkvc\ stands for control sequence, +% because |def| was already taken by \expkvd\ and ``control sequence'' is the +% term D.\,E.\,Knuth used in his \TeX book for named commands hence macros +% (though he also used the term ``macro''). So \expkvc\ defines control +% sequences for and with \expkv. +% +% There are two different approaches supported by this package. The first is +% splitting the keys up into individual arguments, the second is providing all +% the keys as a single argument to the underlying macro and getting an +% individual \val\ by using a hash. Well, actually there is no real hash, just +% some markers which are parsed, but this shouldn't be apparent to the user, the +% behaviour matches that of a hash-table. +% +% In addition to these two methods of defining a macro with primary keys a way +% to define secondary keys, which can reference the primary ones, is provided. +% These secondary keys don't correspond to an argument or an entry in the hash +% table directly but might come in handy for the average use case. Each macro +% 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) 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. 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, as a \LaTeX\ package, and as a \ConTeXt\ +% module. It'll automatically load \expkv\ in the same mode as well. To use it, +% just use one of +% \begin{lstlisting} +% \input expkv-cs % plainTeX +% \usepackage{expkv-cs} % LaTeX +% \usemodule[expkv-cs] % ConTeXt +% \end{lstlisting} +% +% +% \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 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} +% +% In the following descriptions there will be one argument named \meta{primary +% keys}. This argument should be a \kv\ list where each \key\ will be one +% 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 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 +% some features I deem useful can't be implemented without such internal keys. +% Because of this, please don't use key names starting with +% \texttt{EKVC\string|} as that should be the private name space. +% +% +% \subsubsection{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:primaries}). 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 |\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} +% \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{lstlisting} +% \ekvcSplitAndForward\foo\foo@aux{keyA = A, keyB = B} +% \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} +% \cs{ekvcSplitAndUse}\meta{cs}\marg{primary keys} +% \end{syntax} +% This will roughly do the same as |\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}\{\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 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} +% \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 +% \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} +% \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 +% \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 at install time, +% \meta{cs} will use what follows the \kv\ list. So its syntax will be +% \end{function} +% \begin{quote} +% \ttfamily +% \meta{cs}\{\key=\val, \ldots\}\marg{after} +% \end{quote} +% +% \begin{function}{\ekvcValue} +% \begin{syntax} +% \cs{ekvcValue}\{\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 |\ekvcHash|, |\ekvcHashAndForward|, or |\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}\{\key\}\marg{key list} +% \end{syntax} +% This behaves just like |\ekvcValue|, but \emph{without any} safety 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 \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} +% \begin{syntax} +% \cs{ekvcValueSplit}\{\key\}\marg{key list}\marg{next} +% \end{syntax} +% If you need a specific \key\ from a \meta{key list} more than once, it'll +% be a good idea to only extract it once and from then on keep it as a +% separate argument. Hence the macro \cs{ekvcValueSplit} will extract one +% specific \key's value from the list and forward 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} +% \cs{ekvcValueSplitFast}\{\key\}\marg{key list}\marg{next} +% \end{syntax} +% This behaves just like |\ekvcValueSplit|, but it won't run the same tests, +% hence it is faster but more error prone, just like the relation between +% |\ekvcValue| and |\ekvcValueFast|. +% \end{function} +% +% +% \subsection{Secondary Keys}\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 +% to be defined for each macro individually but it doesn't matter whether that +% macro was a split or a hash variant. If a secondary key references another key +% it doesn't matter whether that other key is primary or secondary unless +% otherwise specified. +% +% Secondary keys can have a prefix (like |long|) which are called |p|-type +% prefix and must have a type (like |meta|) which are called |t|-type prefix. +% Some types might require some |p|-prefixes, while others might forbid those. +% +% Please keep in mind that key names shouldn't start with \texttt{EKVC\string|}. +% +% \begin{function}{\ekvcSecondaryKeys} +% \begin{syntax} +% \cs{ekvcSecondaryKeys}\meta{cs}\{\kv, \ldots\} +% \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\small +% \meta{prefix} \meta{name} +% \end{quote} +% Where \meta{prefix} is a space separated list of optional |p|-type prefixes +% followed by one |t|-type prefix. The syntax of \val\ is dependent on the +% used |t|-prefix. +% \end{function} +% +% +% \subsubsection{\texttt{p}-type Prefixes} +% +% There is only one |p|-prefix available, which is |long|. +% +% \begin{function}{long} +% The following key will be defined |\long|. +% \end{function} +% +% +% \subsubsection{\texttt{t}-type Prefixes} +% +% If you're familiar with \expkvd\ you'll notice that the |t|-type prefixes +% provided here are much fewer. The expansion only concept doesn't allow for +% great variety in the auto-defined keys. +% +% The syntax examples of the |t|-prefixes will show which |p|-prefix will be +% automatically used by printing those black (\texttt{\enflong}), which +% will be available in grey (\texttt{\alllong}), and which will be disallowed in +% red (\texttt{\notlong}). This will be put flush right next to the syntax line. +% +% \begin{function}{meta} +% \begin{syntax} +% meta \key\ = \{\kv, \ldots\} \prefixes2 +% \end{syntax} +% With a |meta| key you can set other keys. Whenever \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. The keys in the \kv\ list can +% be primary and secondary ones. +% \end{function} +% +% \begin{function}{nmeta} +% \begin{syntax} +% nmeta \key\ = \{\kv, \ldots\} \prefixes3 +% \end{syntax} +% An |nmeta| key is like a |meta| key, but it doesn't take a value, so the +% \kv\ list is static. +% \end{function} +% +% \begin{function}{alias} +% \begin{syntax} +% alias \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 value taking and the +% |NoVal| version (that's \expkv\ slang for a key not accepting a value) will +% be copied if they are defined when |alias| is used. Of course, \meta{key_2} +% has to be defined, be it as a primary or secondary one. +% \end{function} +% +% \begin{function}{default} +% \begin{syntax} +% default \key\ = \marg{default} \prefixes3 +% \end{syntax} +% 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 (see |\ekvcChange| in +% \autoref{sec:change} for this). \key\ can be a primary or secondary key. +% \end{function} +% +% \begin{function}{enum} +% \begin{syntax} +% enum \key\ = \marg{key_2}\{\val, \ldots\} \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 treated as a string, 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} +% \example First a small example that might give you an idea of what the +% description above could mean:\\ +% \begin{minipage}[c]{.75\linewidth} +% \begin{lstlisting} +% \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{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} +% \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{exresult} +% \example 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 +% |\ifcase|:\\ +% \begin{minipage}[c]{.75\linewidth} +% \begin{lstlisting} +% \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{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} +% \ekvcSplit\foo{k-internal=-1} +% {^^A +% \ifcase#1 +% is\or +% This\or +% easy^^A +% \else +% .^^A +% \fi +% } +% \ekvcSecondaryKeys\foo +% {enum k = {k-internal}{a,b,c}} +% \foo{k=b} \foo{k=a} \foo{k=c}\foo{} +% \end{exresult} +% +% \begin{function}{aggregate} +% \begin{syntax} +% aggregate \key\ = \marg{primary}\marg{definition} \prefixes3 +% \end{syntax} +% While all other key types replace the current value of the associated +% \meta{primary} key, with |aggregate| you can create keys that append or +% prepend (or whatever you like) the new value to the current one. The +% value must be exactly two \TeX\ arguments, where \meta{primary} should +% be the name of a \meta{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 (so +% this doesn't allow general processing of current and new values). The +% resulting \key\ will inherit being either |short| or |long| from the +% \meta{primary} key. +% \end{function} +% \example 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{minipage}[c]{.75\linewidth} +% \begin{lstlisting} +% \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{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} +% \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{exresult} +% +% But also more strange stuff could end there, like macros or using the same +% value multiple times: +% \begin{lstlisting} +% \ekvcSecondaryKeys\foo +% {aggregate k = {k-internal}{\old{#1}\new{#2\old{#1}}}} +% \end{lstlisting} +% +% +% \begin{function}{flag-bool} +% \begin{syntax} +% flag-bool \key\ = \meta{cs} \prefixes3 +% \end{syntax} +% This is a secondary key that doesn't directly involve any of the primary or +% 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. +% Please also read \autoref{sec:flags}. +% \end{function} +% +% \begin{function}{flag-true,flag-false} +% \begin{syntax} +% flag-true \key\ = \meta{cs} \prefixes3 +% \end{syntax} +% This is a secondary key that doesn't directly involve any of the primary or +% secondary keys. +% This defines \key\ to take no value and set the flag called \meta{cs} to +% |true| or |false|, respectively. +% If \meta{cs} isn't defined yet it will be initialised as a flag. +% Please also read \autoref{sec:flags}. +% \end{function} +% +% \begin{function}{flag-raise} +% \begin{syntax} +% flag-raise \key\ = \meta{cs} \prefixes3 +% \end{syntax} +% This is a secondary key that doesn't directly involve any of the primary or +% secondary keys. +% This defines \key\ to take no value and raise the flag called \meta{cs}. +% If \meta{cs} isn't defined yet it will be initialised as a flag. +% Please also read \autoref{sec:flags}. +% \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 +% 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 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 |t|-types involving flags +% 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}. +% +% \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 |\ifnum| for example and it will terminate the number scanning +% on its own). +% \end{function} +% +% All other macros dealing with flags take as a parameter a macro defined as a +% \meta{flag} with |\ekvcFlagNew|. +% +% \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} +% \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. 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 +% \texttt +% {^^A +% \cs[no-index]{ekvcFlagGetHeights}\hskip0pt^^A +% \{\cs[no-index]{myflagA}\cs[no-index]{myflagB}\}\hskip0pt^^A +% \{\cs[no-index]{stuff}\}^^A +% } +% will expand to +% \texttt{\cs[no-index]{stuff}\{\marg{height-A}\marg{height-B}\}}. +% \end{function} +% +% +% \subsection{Further Examples}\label{sec:examples} +% +% 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, % #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. +% \begin{lstlisting} +% \ekvcSecondaryKeys\character +% { +% alias pro = pronoun, +% alias pos = possessive, +% nmeta me = +% { +% name=Jonathan, +% age=a young, +% nationality=Germany, +% hobby=\TeX\ coding, +% }, +% meta lady = +% {type=Lady, pronoun=She, possessive=her, name=Jane Doe, #1}, +% nmeta paulo = +% { +% name=Paulo, +% type=duck, +% age=a young, +% nationality=Brazil, +% hobby=to quack, +% } +% } +% \end{lstlisting} +% Now we can describe people using +% \begin{lstlisting} +% \character{} +% \character{me} +% \character{paulo} +% \character +% {lady={name=Evelyn,nationality=Ireland,age=the best,hobby=reading}} +% \character +% { +% name=Our sun, type=star, nationality=our solar system, pro=It, +% age=an old, pos=its, hobby=shining +% } +% \end{lstlisting} +% 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 only the first two usages would be: +% \begin{exresult}{\linewidth} +% \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} +% \ekvcSecondaryKeys\character +% { +% alias pro = pronoun, +% alias pos = possessive, +% nmeta me = +% { +% name=Jonathan, +% age=a young, +% nationality=Germany, +% hobby=\TeX\ coding, +% }, +% meta lady = +% {type=Lady, pronoun=She, possessive=her, name=Jane Doe, #1}, +% nmeta paulo = +% { +% name=Paulo, +% type=duck, +% age=a young, +% nationality=Brazil, +% hobby=to quack, +% } +% } +% \character{} +% \character{me} +% \end{exresult} +% +% Using \pkg{xparse} 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 (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, 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{}} +% \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 +% \end{lstlisting} +% And then we would be able to do some nonsense +% \begin{lstlisting} +% \nonsense{} +% \nonsense[keyA=hihi]{haha} +% \nonsense[keyA=hihi, keyB=A]{hehe} +% \nonsense[keyC=huhu, keyA=hihi, keyB=A]{haha} +% \end{lstlisting} +% resulting in +% \begin{exresult}{\linewidth} +% \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{} +% \nonsense[keyA=hihi]{haha} +% \nonsense[keyA=hihi, keyB=A]{hehe} +% \nonsense[keyC=huhu, keyA=hihi, keyB=A]{haha} +% \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} +% +% Additionally, there is a more general version of the |aggregate| secondary key +% type (described in \autoref{sec:secondaries}), namely the |process| key type: +% +% \begin{function}{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 |\ekvcPass| to forward the +% values afterwards. Unlike |aggregate| you can specify whether the \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} +% \example We could define a key that only accepts values greater than the +% current value with this:\\ +% \begin{minipage}[c]{.75\linewidth} +% \begin{lstlisting} +% \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{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} +% \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{exresult} +% +% +% \subsection{Speed Considerations}\label{sec:speed} +% +% As already mentioned in the introduction there are some speed considerations +% implied if you choose to define macros via \expkvc. However the overhead isn't +% the factor which should hinder you to use \expkvc\ if you found a reasonable +% use case. The key-parsing is still faster than with most other \kv\ packages +% (see the ``Comparisons'' subsection in the \expkv\ documentation). +% +% The speed considerations in this subsection use the first example 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} +% \ekvdefinekeys{keys} +% {% +% ,store name = \KEYSname +% ,initial name = John Doe +% ,store age = \KEYSage +% ,initial age = any +% ,store nationality = \KEYSnationality +% ,initial nationality = the Universe +% ,store hobby = \KEYShobby +% ,initial hobby = to exist +% ,store type = \KEYStype +% ,initial type = Mister +% ,store pronoun = \KEYSpronoun +% ,initial pronoun = He +% ,store possessive = \KEYSpossessive +% ,initial possessive = his +% } +% \newcommand*\KEYS[1] +% {% +% \begingroup +% \ekvset{keys}{#1}% +% \KEYSname\ is a \KEYStype\ from \KEYSnationality. +% \KEYSpronoun\ is of \KEYSage\ age and +% \KEYSpossessive\ hobby is \KEYShobby.% +% \endgroup +% } +% \end{lstlisting} +% +% The first comparison removes the typesetting part from all the definitions, so +% that only the key parsing is compared. In this comparison the |\ekvcValue| +% and |\ekvcValueFast| variants will not differ, as they are exactly the same +% until the key usage. We find that the split approach is $1.4$ times slower +% than the \expkvd\ setup and the hash variants end up in the middle at $1.17$ +% times slower. +% +% Next we put the typesetting part back in. Every call of the macros will +% typeset the sentences into a box register in horizontal mode. With the +% typesetting part (which includes the accessing of values) the fastest remains +% the \expkvd\ definitions, but split is close at $1.16$ times slower, followed +% by the hash variant with fast accesses at $1.36$ times slower, and the safe +% hash access variant ranks in the slowest $1.8$ times slower than \expkvd. +% +% Just in case you're wondering now, a simple macro taking seven arguments is +% $30$ to $40$ times faster than any of those in the argument grabbing and \kv\ +% parsing part and only $1.5$ to $2.8$ times faster if the typesetting part is +% factored in. So the real choke isn't the parsing. +% +% So to 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} +% +% Perhaps these macros aren't completely useless, but I figured from a user's +% point of view I wouldn't know what I should do with these. +% +% \begin{function}{\ekvcDate,\ekvcVersion} +% These two macros store the version and the date of the package/generic code. +% \end{function} +% +% +% \subsection{Bugs} +% +% Of course I don't think there are any bugs (who would knowingly distribute +% buggy software as long as he isn't a multi-million dollar corporation?). But +% if you find some please let me know. For this one might find my email address +% on the first page or file an issue on Github: +% \url{https://github.com/Skillmon/tex_expkv-cs} +% +% +% \subsection{License} +% +% Copyright \textcopyright\ 2020\unless\ifnum\year=2020--\the\year\fi\ +% Jonathan P. Spratte +% +% \medskip\noindent +% This work may be distributed and/or modified under the conditions of the +% \LaTeX\ Project Public License (LPPL), either version 1.3c of this license or +% (at your option) any later version. The latest version of this license is in +% the file: +% +% \url{http://www.latex-project.org/lppl.txt} +% +% \noindent +% This work is ``maintained'' (as per LPPL maintenance status) by +% +% \mbox{Jonathan P. Spratte}. +% +% \end{documentation}^^A=<< +% +% \begin{implementation}^^A>>= +% +% \clearpage +% +% \section{Implementation}^^A>>= +%^^A the LaTeX package >>= +% \subsection{The \LaTeX\ Package} +% Just like for \expkv\ we provide a small \LaTeX\ package that sets up things +% such that we behave nicely on \LaTeX\ packages and files system. It'll +% |\input| the generic code which implements the functionality. +% \gobbledocstriptag +%<*pkg> +% \begin{macrocode} +\RequirePackage{expkv} +\def\ekvc@tmp + {% + \ProvidesFile{expkv-cs.tex}% + [% + \ekvcDate\space v\ekvcVersion\space + define expandable key=val macros using expkv% + ]% + } +\input{expkv-cs.tex} +\ProvidesPackage{expkv-cs}% + [% + \ekvcDate\space v\ekvcVersion\space + define expandable key=val macros using expkv% + ] +% \end{macrocode} +% \gobbledocstriptag +%</pkg> +%^^A=<< +%^^A the ConTeXt module >>= +% \subsection{The \ConTeXt\ module} +% \gobbledocstriptag +%<*ctx> +% \begin{macrocode} +\writestatus{loading}{ConTeXt User Module / expkv-cs} +\usemodule[expkv] +\unprotect +\input expkv-cs.tex +\writestatus{loading} + {ConTeXt User Module / expkv-cs / Version \ekvcVersion\space loaded} +\protect\endinput +% \end{macrocode} +% \gobbledocstriptag +%</ctx> +%^^A=<< +%^^A main file >>= +% \subsection{The Generic Code} +% The rest of this implementation will be the generic code. +% \gobbledocstriptag +%<*tex> +% +% Load \expkv\ if the package didn't already do so -- since \expkv\ has +% safeguards against being loaded twice this does no harm and the overhead +% isn't that big. Also we reuse some of the internals of \expkv\ to save us from +% retyping them. +% \begin{macrocode} +\input expkv +% \end{macrocode} +% +% We make sure that \file{expkv-cs.tex} is only input once: +% \begin{macrocode} +\expandafter\ifx\csname ekvcVersion\endcsname\relax +\else + \expandafter\endinput +\fi +% \end{macrocode} +% +% \begin{macro}{\ekvcVersion,\ekvcDate} +% We're on our first input, so lets store the version and date in a macro. +% \begin{macrocode} +\def\ekvcVersion{1.2} +\def\ekvcDate{2022-02-13} +% \end{macrocode} +% \end{macro} +% +% If the \LaTeX\ format is loaded we want to be a good file and report back who +% we are, for this the package will have defined |\ekvc@tmp| to use +% |\ProvidesFile|, else this will expand to a |\relax| and do no harm. +% \begin{macrocode} +\csname ekvc@tmp\endcsname +% \end{macrocode} +% +% Store the category code of |@| to later be able to reset it and change it to +% 11 for now. +% \begin{macrocode} +\expandafter\chardef\csname ekvc@tmp\endcsname=\catcode`\@ +\catcode`\@=11 +% \end{macrocode} +% |\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. +% \begin{macrocode} +\newcount\ekvc@keycount +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@long,\ekvc@any@long} +% Some macros will have to be defined long. These two will be let to |\long| +% when this should be the case. +% \begin{macrocode} +\let\ekvc@long\ekv@empty +\let\ekvc@any@long\ekv@empty +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@ifdefined} +% We want to test whether a macro is already defined. This test checks for a +% defined macro that isn't |\relax|. +% \begin{macrocode} +\long\def\ekvc@ifdefined#1% + {% + \ifdefined#1% + \ifx\relax#1% + \ekv@fi@gobble + \fi + \@firstofone + \ekv@fi@firstoftwo + \fi + \@secondoftwo + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@ekvset@pre@expander, +% \ekvc@ekvset@pre@expander@a, +% \ekvc@ekvset@pre@expander@b +% } +% This macro expands |\ekvset| twice so that the first two steps of expansion +% don't have to be made every time the \expkvc\ macros are used. We have to do +% a little magic trick to get the macro parameter |#1| for the macro +% definition this is used in, even though we're calling |\unexpanded|. We do +% that by splitting the expanded |\ekvset| at some marks and place |##1| in +% between. At this spot we also add |\ekv@alignsafe| and |\ekv@endalignsafe| +% to ensure that macros created with \expkvc\ are alignment safe. +% \begin{macrocode} +\def\ekvc@ekvset@pre@expander#1% + {% + \expandafter\ekvc@ekvset@pre@expander@a\ekvset{#1}\ekvc@stop\ekvc@stop + } +\def\ekvc@ekvset@pre@expander@a + {% + \expandafter\ekvc@ekvset@pre@expander@b + } +\def\ekvc@ekvset@pre@expander@b#1\ekvc@stop#2\ekvc@stop + {% + \ekv@unexpanded\expandafter{\ekv@alignsafe}% + \ekv@unexpanded{#1}##1\ekv@unexpanded{#2}% + \ekv@unexpanded\expandafter{\ekv@endalignsafe}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcSplitAndUse} +% The first user macro we want to set up can be reused for +% \cs[no-index]{ekvcSplitAndForward} and \cs[no-index]{ekvcSplit}. We'll split +% this one up so that the test whether the macro is already defined doesn't +% run twice. +% \begin{macrocode} +\protected\long\def\ekvcSplitAndUse#1#2% + {% + \let\ekvc@helpers@needed\@firstoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {\ekvcSplitAndUse@#1{}{#2}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvcSplitAndUse@} +% The actual macro setting up things. We need to set some variables, forward +% the key list to |\ekvc@SetupSplitKeys|, and afterwards define the front +% facing macro to call |\ekvset| and put the initials and the argument sorting +% macro behind it. The internals |\ekvc@any@long|, |\ekvc@initials| and +% |\ekvc@keycount| will be set correctly by |\ekvc@SetupSplitKeys|. +% \begin{macrocode} +\protected\long\def\ekvcSplitAndUse@#1#2#3% + {% + \edef\ekvc@set{\string#1}% + \ekvc@SetupSplitKeys{#3}% + \ekvc@helpers@needed + {% + \ekvc@any@long\edef#1##1% + {% + \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% + \ekv@unexpanded\expandafter + {\csname ekvc@split@\the\ekvc@keycount\endcsname}% + \ekv@unexpanded\expandafter{\ekvc@initials{}#2}% + }% + }% + {% + \ekvc@any@long\edef#1##1% + {% + \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% + \ekv@unexpanded{#2}% + \ekv@unexpanded\expandafter{\ekvc@initials}% + }% + }% + } +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\ekvcSplitAndForward} +% This just reuses |\ekvcSplitAndUse@| with a non-empty second argument, +% resulting in that argument to be called after the splitting. +% \begin{macrocode} +\protected\long\def\ekvcSplitAndForward#1#2#3% + {% + \let\ekvc@helpers@needed\@firstoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {\ekvcSplitAndUse@#1{{#2}}{#3}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcSplit} +% The first half is just |\ekvcSplitAndForward| then we define the macro to +% which the parsed key list is forwarded. There we need to allow for up to +% nine arguments. +% \begin{macrocode} +\protected\long\def\ekvcSplit#1#2#3% + {% + \let\ekvc@helpers@needed\@secondoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {% + \expandafter + \ekvcSplitAndUse@\expandafter#1\csname ekvc@\string#1\endcsname{#2}% + \ifnum\ekvc@keycount<1 + \ekvc@any@long\expandafter\def\csname ekvc@\string#1\endcsname{#3}% + \else + \ifnum\ekvc@keycount>9 + \ekvc@err@toomany{#1}% + \let#1\ekvc@undefined + \else + \ekvcSplit@build@argspec + \ekvc@any@long\expandafter + \def\csname ekvc@\string#1\expandafter\endcsname\ekvc@tmp{#3}% + \fi + \fi + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvcSplit@build@argspec,\ekvcSplit@build@argspec@} +% \begin{macrocode} +\protected\def\ekvcSplit@build@argspec + {% + \begingroup + \edef\ekvc@tmp + {\endgroup\def\ekv@unexpanded{\ekvc@tmp}{\ekvcSplit@build@argspec@{1}}}% + \ekvc@tmp + } +\def\ekvcSplit@build@argspec@#1% + {% + \ifnum#1>\ekvc@keycount + \ekv@fi@gobble + \fi + \@firstofone + {% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@#1\endcsname####}#1% + \expandafter\ekvcSplit@build@argspec@\expandafter{\the\numexpr#1+1}% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@SetupSplitKeys, \ekvc@SetupSplitKeys@a, \ekvc@SetupSplitKeys@b, +% \ekvc@SetupSplitKeys@c, \ekvc@SetupSplitKeys@d, \ekvc@SetupSplitKeys@e, +% \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|. +% \begin{macrocode} +\protected\long\def\ekvc@SetupSplitKeys + {% + \ekvc@keycount=\ekv@zero + \let\ekvc@any@long\ekv@empty + \let\ekvc@initials\ekv@empty + \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 +% whether this key has a |long| prefix so we initialise |\ekvc@long|. +% \begin{macrocode} +\protected\long\def\ekvc@SetupSplitKeys@a#1% + {\expandafter\ekvc@SetupSplitKeys@b\detokenize{#1}\ekvc@stop} +\protected\def\ekvc@SetupSplitKeys@b#1\ekvc@stop + {% + \advance\ekvc@keycount1 + \let\ekvc@long\ekv@empty + \ekvc@ifspace{#1}% + {\ekvc@SetupSplitKeys@c#1\ekvc@stop}% + {\ekvc@SetupSplitKeys@d{#1}}% + } +% \end{macrocode} +% If there was a space, there might be a prefix. If so call the prefix macro, +% else call the next step |\ekvc@SetupSplitKeys@d| which will define the key +% macro and add the key's value to the initials list. +% \begin{macrocode} +\protected\long\def\ekvc@SetupSplitKeys@c#1 #2\ekvc@stop + {% + \ekv@ifdefined{ekvc@split@p@#1}% + {\csname ekvc@split@p@#1\endcsname{#2}}% + {\ekvc@SetupSplitKeys@d{#1 #2}}% + } +% \end{macrocode} +% The inner definition is grouped, because we don't want to actually define +% the marks we build with |\csname|. We have to append the value to the +% |\ekvc@initials| list here with the correct split mark. The key macro will +% read everything up to those split marks and change the value following it to +% the value given to the key. Additionally we'll need a sorting macro for each +% key count in use so we set it up with |\ekvc@setup@splitmacro|. +% \begin{macrocode} +\protected\def\ekvc@SetupSplitKeys@d + {% + \begingroup\expandafter\endgroup + \expandafter\ekvc@SetupSplitKeys@e + \csname ekvc@splitmark@\the\ekvc@keycount\endcsname + } +\protected\long\def\ekvc@SetupSplitKeys@e#1#2#3% + {% + \long\def\ekvc@tmp##1##2#1##3{##2#1{##1}}% +% \end{macrocode} +% The short variant needs a bit of special treatment. The key macro will be +% short to throw the correct error, but since there might be long macros +% somewhere the reordering of arguments needs to be long, so for short keys we +% use a two step approach, first grabbing only the short argument, then +% reordering. +% \begin{macrocode} + \unless\ifx\ekvc@long\long + \expandafter\let\csname ekvc@\ekvc@set(#2)\endcsname\ekvc@tmp + \edef\ekvc@tmp##1% + {% + \ekv@unexpanded\expandafter{\csname ekvc@\ekvc@set(#2)\endcsname}% + {##1}% + }% + \fi + \ekvlet\ekvc@set{#2}\ekvc@tmp + \edef\ekvc@initials{\ekv@unexpanded\expandafter{\ekvc@initials#1{#3}}}% + \ekvc@helpers@needed + {\expandafter\ekvc@setup@splitmacro\expandafter{\the\ekvc@keycount}}% + {}% + } +% \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{\ekv@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} +% The |long| prefix lets the internals |\ekvc@long| and |\ekvc@any@long| to +% |\long| so that the key macro will be long. +% \begin{macrocode} +\protected\def\ekvc@split@p@long + {% + \let\ekvc@long\long + \let\ekvc@any@long\long + \ekvc@SetupSplitKeys@d + } +% \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@d} +% \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. +% \begin{macrocode} +\protected\def\ekvc@defarggobbler#1{\def\ekvc@tmp##1#1##2##{##1#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@setup@splitmacro, +% \ekvc@split@1, \ekvc@split@2, \ekvc@split@3, +% \ekvc@split@4, \ekvc@split@5, \ekvc@split@6, +% \ekvc@split@7 +% } +% Since the first few split macros are different from the others we manually +% set those up now. All the others will be defined as needed (always +% globally). The split macros just read up until the correct split mark, move +% that argument into a list and reinsert the rest, calling the next split +% macro afterwards. +% \begin{macrocode} +\begingroup +\edef\ekvc@tmp + {% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@1\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}% + ##1##2##3% + {##3{##1}##2}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@2\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + ##3##4% + {##4{##1}{##2}##3}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@3\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% + ##4##5% + {##5{##1}{##2}{##3}##4}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@4\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% + ##5##6% + {##6{##1}{##2}{##3}{##4}##5}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@5\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@5\endcsname}##5% + ##6##7% + {##7{##1}{##2}{##3}{##4}{##5}##6}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@6\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@5\endcsname}##5% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@6\endcsname}##6% + ##7##8% + {##8{##1}{##2}{##3}{##4}{##5}{##6}##7}% + \long\gdef\ekv@unexpanded\expandafter{\csname ekvc@split@7\endcsname}% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@1\endcsname}##1% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@2\endcsname}##2% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@3\endcsname}##3% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@4\endcsname}##4% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@5\endcsname}##5% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@6\endcsname}##6% + \ekv@unexpanded\expandafter{\csname ekvc@splitmark@7\endcsname}##7% + ##8##9% + {##9{##1}{##2}{##3}{##4}{##5}{##6}{##7}##8}% + } +\ekvc@tmp +\endgroup +\protected\def\ekvc@setup@splitmacro#1% + {% + \ekv@ifdefined{ekvc@split@#1}{}% + {% + \begingroup + \edef\ekvc@tmp + {% + \long\gdef + \ekv@unexpanded\expandafter{\csname ekvc@split@#1\endcsname}% + ####1% + \ekv@unexpanded\expandafter + {\csname ekvc@splitmark@#1\endcsname}% + ####2####3% + {% + \ekv@unexpanded\expandafter + {\csname ekvc@split@\the\numexpr#1-1\relax\endcsname}% + ####1{{####2}####3}% + }% + }% + \ekvc@tmp + \endgroup + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcHashAndUse} +% |\ekvcHashAndUse| works just like |\ekvcSplitAndUse|. +% \begin{macrocode} +\protected\long\def\ekvcHashAndUse#1#2% + {% + \let\ekvc@helpers@needed\@firstoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {\ekvcHashAndUse@#1{}{#2}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvcHashAndUse@} +% This is more or less the same as |\ekvcSplitAndUse@|. Instead of an empty +% group we place a marker after the initials, we don't use the sorting macros +% of |split|, but instead pack all the values in one argument. +% \begin{macrocode} +\protected\long\def\ekvcHashAndUse@#1#2#3% + {% + \edef\ekvc@set{\string#1}% + \ekvc@SetupHashKeys{#3}% + \ekvc@helpers@needed + {% + \ekvc@any@long\edef#1##1% + {% + \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% + \ekv@unexpanded{\ekvc@hash@pack@argument}% + \ekv@unexpanded\expandafter{\ekvc@initials\ekvc@stop#2}% + }% + }% + {% + \ekvc@any@long\edef#1##1% + {% + \expandafter\ekvc@ekvset@pre@expander\expandafter{\ekvc@set}% + \ekv@unexpanded{#2}% + \ekv@unexpanded\expandafter{\ekvc@initials\ekvc@stop}% + }% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcHashAndForward} +% |\ekvcHashAndForward| works just like |\ekvcSplitAndForward|. +% \begin{macrocode} +\protected\long\def\ekvcHashAndForward#1#2#3% + {% + \let\ekvc@helpers@needed\@firstoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {\ekvcHashAndUse@#1{{#2}}{#3}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcHash} +% |\ekvcHash| does the same as |\ekvcSplit|, but has the advantage of not +% needing to count arguments, so the definition of the internal macro is a bit +% more straight forward. +% \begin{macrocode} +\protected\long\def\ekvcHash#1#2#3% + {% + \let\ekvc@helpers@needed\@secondoftwo + \ekvc@ifdefined#1% + {\ekvc@err@already@defined#1}% + {% + \expandafter + \ekvcHashAndUse@\expandafter#1\csname ekvc@\string#1\endcsname{#2}% + \ekvc@any@long\expandafter\def\csname ekvc@\string#1\endcsname + ##1\ekvc@stop + {#3}% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@hash@pack@argument} +% All this macro does is pack the values into one argument and forward that to +% the next macro. +% \begin{macrocode} +\long\def\ekvc@hash@pack@argument#1\ekvc@stop#2{#2{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@SetupHashKeys,\ekvc@SetupHashKeys@a,\ekvc@SetupHashKeys@b, +% \ekvc@SetupHashKeys@c,\ekvc@SetupHashKeys@d,\ekvc@SetupHashKeys@e, +% \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} +\protected\long\def\ekvc@SetupHashKeys#1% + {% + \let\ekvc@any@long\ekv@empty + \let\ekvc@initials\ekv@empty + \ekvparse\ekvc@SetupHashKeys@check@unknown\ekvc@SetupHashKeys@a{#1}% + } +\protected\long\def\ekvc@SetupHashKeys@a#1% + {\expandafter\ekvc@SetupHashKeys@b\detokenize{#1}\ekvc@stop} +\protected\def\ekvc@SetupHashKeys@b#1\ekvc@stop + {% + \let\ekvc@long\ekv@empty + \ekvc@ifspace{#1}% + {\ekvc@SetupHashKeys@c#1\ekvc@stop}% + {\ekvc@SetupHashKeys@d{#1}}% + } +\protected\def\ekvc@SetupHashKeys@c#1 #2\ekvc@stop + {% + \ekv@ifdefined{ekvc@hash@p@#1}% + {\csname ekvc@hash@p@#1\endcsname{#2}}% + {\ekvc@SetupHashKeys@d{#1 #2}}% + } +% \end{macrocode} +% Again we build the marker, this time instead of a numbered one a named +% hashmark, inside a group to not actually define the macro used as a marker. +% \begin{macrocode} +\protected\long\def\ekvc@SetupHashKeys@d#1% + {% + \begingroup\expandafter\endgroup + \expandafter\ekvc@SetupHashKeys@e\csname ekvc@hashmark@#1\endcsname{#1}% + } +% \end{macrocode} +% Yes, even the defining macro looks awfully familiar. Instead of numbered we +% have named marks. Still the key macros grab everything up to their +% respective mark and reorder the arguments. The same quirk is applied for +% short keys. And instead of the |\ekvc@setup@splitmacro| we use +% |\ekvc@setup@hashmacro|. +% \begin{macrocode} +\protected\long\def\ekvc@SetupHashKeys@e#1#2#3% + {% + \long\def\ekvc@tmp##1##2#1##3{##2#1{##1}}% + \unless\ifx\ekvc@long\long + \expandafter\let\csname ekvc@\ekvc@set(#2)\endcsname\ekvc@tmp + \edef\ekvc@tmp##1% + {% + \ekv@unexpanded\expandafter{\csname ekvc@\ekvc@set(#2)\endcsname}% + {##1}% + }% + \fi + \ekvlet\ekvc@set{#2}\ekvc@tmp + \edef\ekvc@initials{\ekv@unexpanded\expandafter{\ekvc@initials#1{#3}}}% + \ekvc@setup@hashmacro{#2}% + } +% \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{\ekv@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} +% Nothing astonishing here either. +% \begin{macrocode} +\protected\def\ekvc@hash@p@long + {% + \let\ekvc@long\long + \let\ekvc@any@long\long + \ekvc@SetupHashKeys@d + } +% \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@d} +% \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. +% Most of the tests which have to be executed will already be done, but we +% have to play safe if the hash doesn't show up in the hash list. Therefore we +% use some |\ekvc@mark|s and |\ekvc@stop| to throw errors if the hash isn't +% found in the right place. The fast variants have an easier life and just +% return the correct value. +% \begin{macrocode} +\protected\def\ekvc@setup@hashmacro#1% + {% + \ekv@ifdefined{ekvc@fasthash@#1}{}% + {% + \begingroup + \edef\ekvc@tmp + {% + \long\gdef + \ekv@unexpanded\expandafter{\csname ekvc@fasthash@#1\endcsname}% + ####1% + \ekv@unexpanded\expandafter + {\csname ekvc@hashmark@#1\endcsname}% + ####2####3\ekv@unexpanded{\ekvc@stop}% + {####2}% + \long\gdef + \ekv@unexpanded\expandafter{\csname ekvc@safehash@#1\endcsname}% + ####1% + {% + \ekv@unexpanded\expandafter + {\csname ekvc@@safehash@#1\endcsname}% + ####1\ekv@unexpanded{\ekvc@mark}{}% + \ekv@unexpanded\expandafter + {% + \csname ekvc@hashmark@#1\endcsname{}% + \ekvc@mark{\ekvc@err@missing@hash{#1}}\ekvc@stop + }% + }% + \long\gdef + \ekv@unexpanded\expandafter + {\csname ekvc@@safehash@#1\endcsname}% + ####1% + \ekv@unexpanded\expandafter + {\csname ekvc@hashmark@#1\endcsname}% + ####2####3\ekv@unexpanded{\ekvc@mark}####4####5% + \ekv@unexpanded{\ekvc@stop}% + {% + ####4{####2}% + }% + \long\gdef\ekv@unexpanded\expandafter + {\csname ekvc@fastsplithash@#1\endcsname}% + ####1% + \ekv@unexpanded\expandafter + {\csname ekvc@hashmark@#1\endcsname}% + ####2####3\ekv@unexpanded{\ekvc@stop}####4% + {% + ####4{####2}% + }% + \long\gdef\ekv@unexpanded\expandafter + {\csname ekvc@safesplithash@#1\endcsname}####1% + {% + \ekv@unexpanded\expandafter + {\csname ekvc@@safesplithash@#1\endcsname}% + ####1\ekv@unexpanded{\ekvc@mark\ekvc@safe@after@hash}% + \ekv@unexpanded\expandafter + {% + \csname ekvc@hashmark@#1\endcsname{}% + \ekvc@mark + {\ekvc@err@missing@hash{#1}\ekvc@safe@after@hash}% + \ekvc@stop + }% + }% + \long\gdef\ekv@unexpanded\expandafter + {\csname ekvc@@safesplithash@#1\endcsname}% + ####1% + \ekv@unexpanded\expandafter + {\csname ekvc@hashmark@#1\endcsname}% + ####2####3\ekv@unexpanded{\ekvc@mark}####4####5% + \ekv@unexpanded{\ekvc@stop}% + {% + ####4{####2}% + }% + }% + \ekvc@tmp + \endgroup + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcValue} +% \begin{macro}[internal]{\ekvcValue@} +% All this does is a few consistency checks on the first argument (not empty, +% hash macro exists) and then call that hash-grabbing macro that will also +% test whether the hash is inside of |#2| or not. +% \begin{macrocode} +\long\def\ekvcValue#1% + {% + \ekv@unexpanded + \expandafter\ekvcValue@\detokenize{#1}\ekvc@stop + } +\def\ekvcValue@#1\ekvc@stop + {% + \ekv@ifdefined{ekvc@safehash@#1}% + {\csname ekvc@safehash@#1\endcsname}% + {\ekvc@err@unknown@hash{#1}\@firstoftwo{{}}}% + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvcValueFast} +% To be as fast as possible, this doesn't test for anything, assuming the user +% knows best. +% \begin{macrocode} +\long\def\ekvcValueFast#1#2% + {\csname ekvc@fasthash@\detokenize{#1}\endcsname#2\ekvc@stop} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcValueSplit} +% \begin{macro}[internal]{\ekvcValueSplit@,\ekvcValueSplit@recover} +% This splits off a single value. +% \begin{macrocode} +\long\def\ekvcValueSplit#1% + {\expandafter\ekvcValueSplit@\detokenize{#1}\ekvc@stop} +\def\ekvcValueSplit@#1\ekvc@stop + {% + \ekv@ifdefined{ekvc@safesplithash@#1}% + {\csname ekvc@safesplithash@#1\endcsname}% + {\ekvc@err@unknown@hash{#1}\ekvcValueSplit@recover}% + } +\long\def\ekvcValueSplit@recover#1#2{#2{}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@safe@after@hash} +% \begin{macrocode} +\long\def\ekvc@safe@after@hash#1#2% + {% + #2{#1}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcValueSplitFast} +% Again a fast approach which doesn't provide too many safety measurements. +% This needs to build the hash function and expand it before passing the +% results to the next control sequence. The first step only builds the control +% sequence. +% \begin{macrocode} +\long\def\ekvcValueSplitFast#1#2% + {\csname ekvc@fastsplithash@\detokenize{#1}\endcsname#2\ekvc@stop} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@safehash@,\ekvc@fasthash@, +% \ekvc@safesplithash@,\ekvc@fastsplithash@ +% } +% At least in the empty hash case we can provide a meaningful error message +% without affecting performance by just defining the macro that would be build +% in that case. There is of course a downside to this, the error will not be +% thrown by |\ekvcValueFast| in three expansion steps. The safe hash variant +% has to also stop the |\unexpanded| expansion. +% \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{}} +\long\def\ekvc@fastsplithash@#1\ekvc@stop#2{\ekvc@err@empty@hash#2{}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcSecondaryKeys} +% \begin{macro}[internal] +% {\ekvcSecondaryKeys@a,\ekvcSecondaryKeys@b,\ekvcSecondaryKeys@c} +% The secondary keys are defined pretty similar to the way the originals are, +% but here we also introduce some key types (those have a |@t@| in their name) +% additionally to the prefixes. +% \begin{macrocode} +\protected\long\def\ekvcSecondaryKeys#1#2% + {% + \edef\ekvc@set{\string#1}% + \ekvparse\ekvc@err@value@required\ekvcSecondaryKeys@a{#2}% + } +\protected\long\def\ekvcSecondaryKeys@a#1% + {\expandafter\ekvcSecondaryKeys@b\detokenize{#1}\ekvc@stop} +\protected\def\ekvcSecondaryKeys@b#1\ekvc@stop + {% + \let\ekvc@long\ekv@empty + \ekvc@ifspace{#1}% + {\ekvcSecondaryKeys@c#1\ekvc@stop}% + {\ekvc@err@missing@type{#1}\@gobble}% + } +\protected\def\ekvcSecondaryKeys@c#1 #2\ekvc@stop + {% + \ekv@ifdefined{ekvc@p@#1}% + {\csname ekvc@p@#1\endcsname}% + {% + \ekv@ifdefined{ekvc@t@#1}% + {\csname ekvc@t@#1\endcsname}% + {\ekvc@err@unknown@keytype{#1}\@firstoftwo\@gobble}% + }% + {#2}% + } +% \end{macrocode} +% \end{macro} +% \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,\ekvc@change@d,\ekvc@change@e +% } +% 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) and place |\ekvc@stop| instead of an +% argument as a marker for the last step. |#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\ekvc@stop}{#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{\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 |\edef|. Also +% we have to protect everything from further expanding with the exception of +% the redefined macro's argument, which is why we placed the |\ekvc@stop| +% earlier. Then we need to also protect the rest of the contents from further +% expanding using |\unexpanded| with another unbalanced opening brace. +% |#1| will be either empty or |\long| and |#2| is the macro. +% \begin{macrocode} +\protected\def\ekvc@change@d#1#2% + {#1\edef#2##1{\expandafter\ekvc@change@e\iffalse}\fi} +\long\def\ekvc@change@e#1\ekvc@stop + {\ekv@unexpanded{#1}##1\ekv@unexpanded\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} +% +% \begin{macro}[internal]{\ekvc@p@long,\ekvc@after@ptype} +% The prefixes are pretty straight forward again. Just set |\ekvc@long| and +% forward to the |@t@| type. +% \begin{macrocode} +\protected\def\ekvc@p@long#1% + {% + \ekvc@ifspace{#1}% + {% + \let\ekvc@long\long + \ekvc@after@ptype#1\ekvc@stop + }% + {\ekvc@err@missing@type{long #1}\@gobble}% + } +\protected\def\ekvc@after@ptype#1 #2\ekvc@stop + {% + \ekv@ifdefined{ekvc@t@#1}% + {\csname ekvc@t@#1\endcsname{#2}}% + {\ekvc@err@unknown@keytype{#1}\@gobble}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@t@meta, \ekvc@t@nmeta, +% \ekvc@type@meta, \ekvc@type@meta@a, \ekvc@type@meta@b +% } +% The |meta| and |nmeta| key types use a nested |\ekvset| to set other keys in +% the same macro's \set. +% \begin{macrocode} +\protected\def\ekvc@t@meta + {% + \edef\ekvc@tmp{\ekvc@set}% + \expandafter\ekvc@type@meta\expandafter{\ekvc@tmp}\ekvc@long{##1}\ekvlet + } +\protected\def\ekvc@t@nmeta#1% + {% + \ekvc@assert@not@long{nmeta #1}% + \edef\ekvc@tmp{\ekvc@set}% + \expandafter\ekvc@type@meta\expandafter{\ekvc@tmp}{}{}\ekvletNoVal{#1}% + } +\protected\long\def\ekvc@type@meta#1#2#3#4#5#6% + {% + \expandafter\ekvc@type@meta@a\expandafter{\ekvset{#1}{#6}}{#2}{#3}% + #4\ekvc@set{#5}\ekvc@tmp + } +\protected\def\ekvc@type@meta@a + {% + \expandafter\ekvc@type@meta@b\expandafter + } +\protected\long\def\ekvc@type@meta@b#1#2#3% + {% + #2\def\ekvc@tmp#3{#1}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@t@alias} +% |alias| just checks whether there is a key and/or |NoVal| key defined with +% the target name and |\let| the key to those. +% \begin{macrocode} +\protected\def\ekvc@t@alias#1#2% + {% + \ekvc@assert@not@long{alias #1}% + \let\ekvc@tmp\@firstofone + \ekvifdefined\ekvc@set{#2}% + {% + \ekvletkv\ekvc@set{#1}\ekvc@set{#2}% + \let\ekvc@tmp\@gobble + }% + {}% + \ekvifdefinedNoVal\ekvc@set{#2}% + {% + \ekvletkvNoVal\ekvc@set{#1}\ekvc@set{#2}% + \let\ekvc@tmp\@gobble + }% + {}% + \ekvc@tmp{\ekvc@err@unknown@key{#2}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@t@default} +% The |default| key can be used to set a |NoVal| key for an existing key. It +% will just pass the \val\ to the key macro of that other key. +% \begin{macrocode} +\protected\long\def\ekvc@t@default#1#2% + {% + \ekvifdefined\ekvc@set{#1}% + {% + \ekvc@assert@not@long{default #1}% + \edef\ekvc@tmp + {% + \ekv@unexpanded\expandafter + {\csname\ekv@name\ekvc@set{#1}\endcsname{#2}}% + }% + \ekvletNoVal\ekvc@set{#1}\ekvc@tmp + }% + {\ekvc@err@unknown@key{#1}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@t@enum} +% \begin{macro}[internal] +% {\ekvc@type@enum,\ekvc@h@enum,\ekvc@h@enum@,\ekvc@enum@name} +% Enums don't need to apply special trickery to make the parts of the names +% retrievable, so unlike in \expkvd\ we don't need catcode juggling. +% The setup of an |enum| requires unpacking the value in two different +% arguments so we need an auxiliary here. +% \begin{macrocode} +\def\ekvc@enum@name#1#2#3{ekvc#1(#2)#3} +\protected\long\def\ekvc@t@enum#1#2% + {\ekvc@assert@twoargs{#2}{enum #1}{\ekvc@type@enum{#1}#2}} +\protected\long\def\ekvc@type@enum#1#2% + {% + \ekvifdefined\ekvc@set{#2}% + {% +% \end{macrocode} +% At run time we need another helper and we need to expand the current +% |\ekvc@set| now. The helper will build a control sequence from each given +% value, those will be set up in the |\ekvcsvloop|. +% \begin{macrocode} + \ekvc@long\edef\ekvc@tmp##1% + {% + \ekv@unexpanded{\expandafter\ekvc@h@enum\detokenize}{##1}% + \ekv@unexpanded{\ekvc@stop}% + {\ekvc@set}{#1}% + }% + \ekvlet\ekvc@set{#1}\ekvc@tmp + \def\ekvc@tmp{0}% + \expandafter\ekvcsvloop\expandafter + {% + \expandafter\ekvc@type@enum@ + \csname\ekv@name\ekvc@set{#2}\endcsname + {#1}% + }% + }% + {% + \ekvc@err@unknown@key{#2}% + \@gobble + }% + } +% \end{macrocode} +% Here |#1| will be the key-macro of the underlying primary or secondary key, +% |#2| is the |enum| key's name, and |#3| will be the choice. The rest is +% pretty obvious. +% \begin{macrocode} +\ekv@exparg{\protected\long\def\ekvc@type@enum@#1#2#3}% + {% + \expandafter\expandafter\expandafter\edef\expandafter + \csname\ekvc@enum@name\ekvc@set{#2}{\detokenize{#3}}\endcsname + {\ekv@unexpanded{#1}{\ekvc@tmp}}% + \edef\ekvc@tmp{\the\numexpr\ekvc@tmp+1\relax}% + } +% \end{macrocode} +% The use-time helper will check if the macro for the passed in choice exists, +% if it doesn't throws an error, else calls that macro which will set the +% correct value. +% \begin{macrocode} +\ekv@if@lastnamedcs + {% + \ekv@exparg{\def\ekvc@h@enum#1\ekvc@stop#2#3}% + {% + \expandafter\ifcsname\ekvc@enum@name{#2}{#3}{#1}\endcsname + \expandafter\ekvc@h@enum@\lastnamedcs + \fi + \ekvc@err@unknown@enum{#2}{#3}{#1}% + } + \def\ekvc@h@enum@#1\fi\ekvc@err@unknown@enum#2#3#4% + {% + \fi + \ifx#1\relax + \ekvc@err@unknown@enum{#2}{#3}{#4}% + \expandafter\@gobble + \fi + #1% + } + } + {% + \def\ekvc@h@enum#1% + {% + \def\ekvc@h@enum##1\ekvc@stop##2##3% + {% + \expandafter\ekvc@h@enum@ + \csname\ifcsname#1\endcsname#1\else relax\fi\endcsname + {##2}{##3}{##1}% + }% + } + \expandafter\ekvc@h@enum\expandafter{\ekvc@enum@name{#2}{#3}{#1}} + \def\ekvc@h@enum@#1#2#3#4% + {% + \ifx#1\relax + \ekvc@err@unknown@enum{#2}{#3}{#4}% + \expandafter\@gobble + \fi + #1% + } + } +% \end{macrocode} +% We don't need |\ekvc@enum@name| anymore, so let's undefine it. +% \begin{macrocode} +\let\ekvc@enum@name\ekvc@undefined +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@t@aggregate} +% Aggregating isn't easy to define. We'll have to extract the correct mark for +% the specified key, branch correctly for short and long keys, and use a small +% hack to have the correct arguments on the user interface (|#1| as the +% current contents, |#2| as the new value). This is split into a few steps +% here. +% +% First, assert that the user input is well-behaved. +% \begin{macrocode} +\protected\def\ekvc@t@aggregate#1% + {% + \ekvc@assert@not@long{aggregate #1}% + \ekvc@type@aggregate + \ekvc@type@aggregate@long\ekvc@type@aggregate@short + {process}% + {#1}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekvc@type@aggregate,\ekvc@type@aggregate@a,\ekvc@type@aggregate@b} +% The next step stores the user defined processing in a temporary macro that's +% used to do the parameter number swapping later. It also builds the names of +% the key macro and the helper which would be used for processing a short key. +% \begin{macrocode} +\protected\long\def\ekvc@type@aggregate#1#2#3#4#5% + {% + \ekvc@assert@twoargs{#5}{#3 #4}{\ekvc@type@aggregate@a#1#2{#4}#5}% + } +\protected\long\def\ekvc@type@aggregate@a#1#2#3#4#5% + {% + \ekvifdefined\ekvc@set{#4}% + {% + \def\ekvc@type@aggregate@tmp##1##2{#5}% + \begingroup\expandafter\endgroup + \expandafter\ekvc@type@aggregate@b + \csname\ekv@name\ekvc@set{#4}\expandafter\endcsname + \csname ekvc@\ekvc@set(#4)\endcsname + #1#2% + {#3}% + }% + {\ekvc@err@unknown@key{#4}}% + } +\protected\long\def\ekvc@type@aggregate@b#1#2#3#4% + {% + \ekvc@type@aggregate@check@long#1#2% + {#3#1}% + {#4#2}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@type@aggregate@check@long, +% \ekvc@type@aggregate@check@long@a,\ekvc@type@aggregate@check@long@b +% } +% To check whether the primary key is long we see whether its |\meaning| +% contains the helper which would only be there for short keys. For this we +% have to get the stringified name of the internal (using |\detokenize|), +% and afterwards get the |\meaning| of the macro. A temporary helper does the +% real test by gobbling and forwarding the result to |\ekv@ifempty|. +% \begin{macrocode} +\protected\long\def\ekvc@type@aggregate@check@long#1#2% + {\expandafter\ekvc@type@aggregate@check@long@a\detokenize{#2}\ekv@stop#1} +\protected\long\def\ekvc@type@aggregate@check@long@a#1\ekv@stop#2% + {% + \def\ekvc@type@aggregate@check@long@@##1#1{}% + \expandafter\ekvc@type@aggregate@check@long@b\meaning#2\ekv@stop{#1}% + } +\protected\def\ekvc@type@aggregate@check@long@b#1\ekv@stop#2% + {\expandafter\ekv@ifempty\expandafter{\ekvc@type@aggregate@check@long@@#1#2}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@type@aggregate@long,\ekvc@type@aggregate@long@} +% The long variant just builds the split mark we extract, uses the hack to +% swap argument order, and then does the definition via |\ekvlet| and a +% temporary macro. +% \begin{macrocode} +\protected\long\def\ekvc@type@aggregate@long#1% + {% + \begingroup\expandafter\endgroup\expandafter + \ekvc@type@aggregate@long@ + \csname\ekvc@extract@mark#1\expandafter\endcsname + \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% + } +\protected\long\def\ekvc@type@aggregate@long@#1#2#3% + {% + \long\def\ekvc@type@aggregate@tmp##1##2#1##3{##2#1{#2}} + \ekvlet\ekvc@set{#3}\ekvc@type@aggregate@tmp + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekvc@type@aggregate@short,\ekvc@type@aggregate@short@} +% The short variant will have to build the marker and the name of the helper +% function, and swap the user argument order. Hence here are a few more +% |\expandafter|s involved. But afterwards we can do the definition of the key +% and the helper macro directly. +% \begin{macrocode} +\protected\long\def\ekvc@type@aggregate@short#1#2% + {% + \begingroup\expandafter\endgroup\expandafter + \ekvc@type@aggregate@short@ + \csname\ekvc@extract@mark#1\expandafter\endcsname + \csname ekvc@\ekvc@set(#2)\expandafter\endcsname + \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% + {#2}% + } +\protected\long\def\ekvc@type@aggregate@short@#1#2#3#4% + {% + \ekvdef\ekvc@set{#4}{#2{##1}}% + \long\def#2##1##2#1##3{##2#1{#3}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@t@process} +% The |process| type can reuse much of |aggregate|, just the last step of +% definition differ. +% \begin{macrocode} +\protected\def\ekvc@t@process + {% + \ifx\ekvc@long\long + \ekv@fi@firstoftwo + \fi + \@secondoftwo + {% + \ekvc@type@aggregate + \ekvc@type@process@long\ekvc@type@process@long + }% + {% + \ekvc@type@aggregate + \ekvc@type@process@short\ekvc@type@process@short + }% + {process}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@type@process@long,\ekvc@type@process@long@} +% This defines a temporary macro to grab the current value (found after the +% marker |#1|), executes the user code and puts everything back to where it +% belongs. Then |\ekvlet| is used to assign that meaning to the key macro. +% \begin{macrocode} +\protected\long\def\ekvc@type@process@long#1% + {% + \begingroup\expandafter\endgroup\expandafter + \ekvc@type@process@long@ + \csname\ekvc@extract@mark#1\expandafter\endcsname + \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% + } +\protected\long\def\ekvc@type@process@long@#1#2#3% + {% + \long\def\ekvc@type@aggregate@tmp##1##2#1##3{#2##2#1{##3}}% + \ekvlet\ekvc@set{#3}\ekvc@type@aggregate@tmp + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@type@process@short,\ekvc@type@process@short@} +% We define the key macro directly to just grab the argument once and forward +% it to the auxiliary. That one does essentially the same as the long variant. +% \begin{macrocode} +\protected\long\def\ekvc@type@process@short#1#2% + {% + \begingroup\expandafter\endgroup\expandafter + \ekvc@type@process@short@ + \csname\ekvc@extract@mark#1\expandafter\endcsname + \csname ekvc@\ekvc@set(#2)\expandafter\endcsname + \expandafter{\ekvc@type@aggregate@tmp{##3}{##1}}% + {#2}% + } +\protected\long\def\ekvc@type@process@short@#1#2#3#4% + {% + \ekvdef\ekvc@set{#4}{#2{##1}}% + \long\def#2##1##2#1##3{#3##2#1{##3}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@t@flag-bool} +% \begin{macrocode} +\protected\expandafter\def\csname ekvc@t@flag-bool\endcsname#1#2% + {% + \ekvc@assert@not@long{flag-bool #1}% + \unless\ifdefined#2\ekvcFlagNew#2\fi + \ekvdef\ekvc@set{#1}% + {% + \ekv@ifdefined{ekvc@flag@set@##1}% + {% + \csname ekvc@flag@set@##1\expandafter\endcsname + \ekvcFlagHeight#2\ekv@stop#2% + }% + {\ekvc@err@invalid@bool{##1}}% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% {\ekvc@t@flag-true,\ekvc@t@flag-false,\ekvc@t@flag-raise,\ekvc@type@flag} +% \begin{macrocode} +\protected\def\ekvc@type@flag#1#2#3#4% + {% + \ekvc@assert@not@long{flag-#1 #3}% + \unless\ifdefined#4\ekvcFlagNew#4\fi + \ekv@exparg{\ekvdefNoVal\ekvc@set{#3}}{#2#4}% + } +\protected\expandafter\def\csname ekvc@t@flag-true\endcsname + {\ekvc@type@flag{true}\ekvcFlagSetTrue} +\protected\expandafter\def\csname ekvc@t@flag-false\endcsname + {\ekvc@type@flag{false}\ekvcFlagSetFalse} +\protected\expandafter\def\csname ekvc@t@flag-raise\endcsname + {\ekvc@type@flag{raise}\ekvcFlagRaise} +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Flags} +% +% The basic idea of flags is to store information by the fact that \TeX\ +% expandably assigns the meaning |\relax| to undefined control sequences which +% were built with |\csname|. This mechanism is borrowed from \pkg{expl3}. +% +% \begin{macro}[internal]{\ekvc@flag@name,\ekvc@flag@namescheme} +% Flags follow a simple naming scheme which we define here. |\ekvc@flag@name| +% will store the name of an internal function that is used to build names of +% the second naming scheme defined by |\ekvc@flag@namescheme|. +% \begin{macrocode} +\def\ekvc@flag@name{ekvcf\string} +\def\ekvc@flag@namescheme#1#2{ekvch#2#1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagHeight} +% For semantic reasons we use |\number| with another name. +% \begin{macrocode} +\let\ekvcFlagHeight\number +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagNew} +% This macro defines a new flag. It stores the function build with the +% |\ekvc@flag@name| naming scheme after the internal function +% |\ekvc@flag@height| that'll determine the current flag height. It'll also +% define the macro named via |\ekvc@flag@name| to build names according to +% |\ekvc@flag@namescheme|. +% \begin{macrocode} +\protected\def\ekvcFlagNew#1% + {% + \edef#1% + {% + \ekv@unexpanded{\ekvc@flag@height}% + \ekv@unexpanded\expandafter{\csname\ekvc@flag@name#1\endcsname}% + }% + \ekv@expargtwice + {\expandafter\def\csname\ekvc@flag@name#1\endcsname##1}% + {\expandafter\ekvc@flag@namescheme\expandafter{\string#1}{##1}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@flag@height,\ekvc@flag@height@} +% This macro gets the height of a flag by a simple loop. The first loop +% iteration differs a bit from the following in that it doesn't have to get +% the current iteration count. The space at the end of |\ekvc@flag@height| +% ends the |\number| evaluation. +% \begin{macrocode} +\def\ekvc@flag@height#1% + {% + \ifcsname#10\endcsname + \ekvc@flag@height@1\ekv@stop#1% + \fi + \@firstofone{0} % leave this space + } +\def\ekvc@flag@height@#1\ekv@stop#2\fi\@firstofone#3% + {% + \fi + \ifcsname#2{#1}\endcsname + \expandafter\ekvc@flag@height@\the\numexpr#1+1\relax\ekv@stop#2% + \fi + \@firstofone{#1}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagRaise} +% Raising a flag simply means letting the |\ekvc@flag@namescheme| macro for +% the current height to relax. The result of raising a flag is that its height +% is bigger by $1$. +% \begin{macrocode} +\ekv@exparg{\def\ekvcFlagRaise#1}% + {% + \expandafter\expandafter\expandafter\@gobble\expandafter + \csname\ekvc@flag@namescheme{\string#1}{\ekvcFlagHeight#1}\endcsname + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagSetTrue,\ekvcFlagSetFalse} +% \begin{macro}[internal]{\ekvc@flag@set@true,\ekvc@flag@set@false} +% A flag is considered true if its current height is odd, and as false if it +% is even. Therefore |\ekvcFlagSetTrue| and |\ekvcFlagSetFalse| only need to +% raise the flag if the opposing boolean value is the current one. +% \begin{macrocode} +\def\ekvcFlagSetTrue#1% + {\expandafter\ekvc@flag@set@true\ekvcFlagHeight#1\ekv@stop#1} +\def\ekvcFlagSetFalse#1% + {\expandafter\ekvc@flag@set@false\ekvcFlagHeight#1\ekv@stop#1} +% \end{macrocode} +% We can expand |\ekvc@flag@namescheme| at definition time here, which is why +% we're using a temporary definition to set up |\ekvc@flag@set@true| and +% |\ekvc@flag@set@false|. +% \begin{macrocode} +\def\ekvc@flag@set@true#1% + {% + \def\ekvc@flag@set@true##1\ekv@stop##2% + {% + \ifodd##1 + \ekv@fi@gobble + \fi + \@firstofone{\expandafter\@gobble\csname#1\endcsname}% + }% + \def\ekvc@flag@set@false##1\ekv@stop##2% + {% + \ifodd##1 + \ekv@fi@firstofone + \fi + \@gobble{\expandafter\@gobble\csname#1\endcsname}% + }% + } +\expandafter\ekvc@flag@set@true\expandafter + {\ekvc@flag@namescheme{\string#2}{#1}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvcFlagIf} +% As already explained, truthiness is defined as a flag's height being odd, so +% we just branch accordingly here. +% \begin{macrocode} +\def\ekvcFlagIf#1% + {% + \ifodd#1% + \ekv@fi@firstoftwo + \fi + \@secondoftwo + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagIfRaised} +% This macro uses flags as a switch, if a flag's current height is bigger than +% $0$ this test yields true. +% \begin{macrocode} +\ekv@exparg{\def\ekvcFlagIfRaised#1}% + {% + \expandafter\ifcsname\ekvc@flag@namescheme{\string#1}0\endcsname + \ekv@fi@firstoftwo + \fi + \@secondoftwo + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekvcFlagReset} +% \begin{macro}[internal]{\ekvc@flag@reset,\ekvc@flag@reset@} +% Resetting works by locally letting all the defined internal macros named +% after |\ekvc@flag@namescheme| to undefined. +% \begin{macrocode} +\protected\def\ekvcFlagReset#1% + {\expandafter\ekvc@flag@reset\csname\ekvc@flag@name#1\endcsname} +\protected\def\ekvc@flag@reset#1% + {% + \ifcsname#10\endcsname + \expandafter\let\csname#10\endcsname\ekvc@undefined + \ekvc@flag@reset@1\ekv@stop#1% + \fi + } +\protected\def\ekvc@flag@reset@#1\ekv@stop#2\fi + {% + \fi + \ifcsname#2{#1}\endcsname + \expandafter\let\csname#2{#1}\endcsname\ekvc@undefined + \expandafter\ekvc@flag@reset@\the\numexpr#1+1\relax\ekv@stop#2% + \fi + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvcFlagGetHeight} +% \begin{macro}[internal]{\ekvc@flag@get@height@single} +% These are just small helpers, first getting the height of the flag and then +% passing it on to the user supplied code. +% \begin{macrocode} +\def\ekvcFlagGetHeight#1% + {\expandafter\ekvc@flag@get@height@single\ekvcFlagHeight#1\ekv@stop} +\long\def\ekvc@flag@get@height@single#1\ekv@stop#2{#2{#1}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ekvcFlagGetHeights} +% \begin{macro}[internal] +% {\ekvc@flag@get@heights,\ekvc@flag@get@heights@,\ekvc@flag@get@heights@done} +% This works by a simple loop that stops at |\ekv@stop|. As long as that +% marker isn't hit, get the next flags height and put it into a list after +% |\ekv@stop|. |\ekvc@flag@get@heights@| uses the same marker name for the +% end of the height, which shouldn't clash in any case. Once we're done we +% remove the remainder of the current iteration and leave the user supplied +% code in the input stream with all the flags' heights as a single argument. +% \begin{macrocode} +\def\ekvcFlagGetHeights#1% + {% + \ekvc@flag@get@heights#1\ekv@stop{}% + } +\def\ekvc@flag@get@heights#1% + {% + \ekv@gobbleto@stop#1\ekvc@flag@get@heights@done\ekv@stop + \expandafter\ekvc@flag@get@heights@\ekvcFlagHeight#1\ekv@stop + } +\def\ekvc@flag@get@heights@#1\ekv@stop#2\ekv@stop#3% + {\ekvc@flag@get@heights#2\ekv@stop{#3{#1}}} +\long\def\ekvc@flag@get@heights@done + \ekv@stop + \expandafter\ekvc@flag@get@heights@\ekvcFlagHeight\ekv@stop\ekv@stop#1#2% + {#2{#1}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% +% \subsubsection{Helper Macros} +% +% \begin{macro}[internal]{\ekvc@ifspace,\ekvc@ifspace@} +% A test which can be reduced to an if-empty by gobbling everything up to the +% first space. +% \begin{macrocode} +\long\def\ekvc@ifspace#1% + {% + \ekvc@ifspace@#1 \ekv@ifempty@B + \ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo + } +\long\def\ekvc@ifspace@#1 % keep this space + {% + \ekv@ifempty@\ekv@ifempty@A + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@ifnottwoargs,\ekvc@ifempty@gtwo} +% Used to test whether a token list contains exactly two \TeX\ arguments. +% \begin{macrocode} +\long\def\ekvc@ifnottwoargs#1% + {% + \ekvc@ifempty@gtwo#1\ekv@ifempty@B + \ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo + } +\long\def\ekvc@ifempty@gtwo#1#2{\ekv@ifempty@\ekv@ifempty@A} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@extract@mark,\ekvc@extract@mark@} +% This is used to extract the mark of a split or hash key from its definition. +% This is kind of fragile, it assumes |#1| is always a macro used for hashing +% or splitting. Also it assumes that the escape character is a backslash. +% \begin{macrocode} +\def\ekvc@extract@mark#1{\expandafter\ekvc@extract@mark@\meaning#1\ekv@stop} +\begingroup +\lccode`;=`# +\lccode`/=`\\ +\lowercase{\endgroup +\def\ekvc@extract@mark@#1:#2/#3 ;#4\ekv@stop{#3}% +} +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Assertions} +% \begin{macro}[internal]{\ekvc@assert@not@long} +% Some keys don't want to be |long| and we have to educate the user, so let's +% throw an error if someone wanted these to be long. +% \begin{macrocode} +\long\def\ekvc@assert@not@long#1{\ifx\ekvc@long\long\ekvc@err@no@long{#1}\fi} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@assert@twoargs} +% Some keys need exactly two arguments as their definition, so we have to +% somehow assert this. +% \begin{macrocode} +\protected\long\def\ekvc@assert@twoargs#1#2% + {\ekvc@ifnottwoargs{#1}{\ekvc@err@not@two{#2}}} +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Messages} +% +% \begin{macro}[internal] +% { +% \ekvc@err@toomany,\ekvc@err@value@required,\ekvc@err@missing@type, +% \ekvc@err@already@defined,\ekvc@err@no@key@macro, +% \ekvc@err@not@two +% } +% Boring unexpandable error messages. +% \begin{macrocode} +\protected\def\ekvc@err@toomany#1% + {% + \errmessage{expkv-cs Error: Too many keys for macro `\string#1'}% + } +\protected\long\def\ekvc@err@value@required#1% + {% + \errmessage{expkv-cs Error: Missing value for key `\ekv@unexpanded{#1}'}% + } +\protected\long\def\ekvc@err@missing@type#1% + {% + \errmessage + {expkv-cs Error: Missing type for secondary key `\ekv@unexpanded{#1}'}% + } +\protected\long\def\ekvc@err@no@long#1% + {% + \errmessage + {expkv-cs Error: prefix `long' not accepted for `\ekv@unexpanded{#1}'}% + } +\protected\long\def\ekvc@err@already@defined#1% + {% + \errmessage{expkv-cs Error: Macro `\string#1' already defined}% + } +\protected\long\def\ekvc@err@unknown@keytype#1% + {% + \errmessage{expkv-cs Error: Unknown key type `\ekv@unexpanded{#1}'}% + } +\protected\long\def\ekvc@err@unknown@key#1% + {% + \errmessage + {expkv-cs Error: Unknown key `\ekv@unexpanded{#1}' for macro `\ekvc@set'}% + } +\protected\long\def\ekvc@err@no@key@macro#1% + {\errmessage{expkv-cs Error: \string#1 is no key=val macro}} +\protected\long\def\ekvc@err@not@two#1% + {% + \errmessage + {% + expkv-cs Error: Definition of `\ekv@unexpanded{#1}' doesn't contain + exactly two arguments% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@err} +% We need a way to throw error messages expandably in some contexts. +% \begin{macrocode} +\ekv@exparg{\long\def\ekvc@err#1}{\ekverr{expkv-cs}{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal] +% { +% \ekvc@err@unknown@hash,\ekvc@err@empty@hash,\ekvc@err@missing@hash, +% \ekvc@err@invalid@bool,\ekvc@err@unknown@enum +% } +% And here are the expandable error messages. +% \begin{macrocode} +\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}} +\def\ekvc@err@unknown@enum#1#2#3% + {\ekvc@err{unknown value `#3' for enum `#2' in macro #1}} +% \end{macrocode} +% \end{macro} +% +% +% Now everything that's left is to reset the category code of |@|. +% \begin{macrocode} +\catcode`\@=\ekvc@tmp +% \end{macrocode} +% +% \gobbledocstriptag +%</tex> +%^^A=<< +% +%^^A=<< +% +% \end{implementation}^^A=<< +% +% \clearpage +% \PrintIndex +% +\endinput +% +^^A vim: ft=tex fdm=marker fmr=>>=,=<< |