diff options
Diffstat (limited to 'macros/generic/expkv-bundle/expkv-cs.dtx')
-rw-r--r-- | macros/generic/expkv-bundle/expkv-cs.dtx | 1748 |
1 files changed, 1748 insertions, 0 deletions
diff --git a/macros/generic/expkv-bundle/expkv-cs.dtx b/macros/generic/expkv-bundle/expkv-cs.dtx new file mode 100644 index 0000000000..4cf7946a87 --- /dev/null +++ b/macros/generic/expkv-bundle/expkv-cs.dtx @@ -0,0 +1,1748 @@ +% \iffalse meta-comment +% +% File: expkv-cs.dtx Copyright (C) 2020-2023 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\expkvDocNoGenerate{} +\input expkv-bundle.ins +\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}}} +\endbatchfile +%</driver>^^A=<< +% \fi +% +% \section{\expkvc} +%^^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-pop} +\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-pop] +\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-pop +% \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.3} +\def\ekvcDate{2023-01-23} +% \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} +\edef\ekvc@tripledots{\detokenize{...}} +% \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@unknown +% } +% These macros parse the list of keys and set up the key macros. First we need +% to initialise some macros and start an \expkvp\ parser. +% \begin{macrocode} +\protected\long\def\ekvc@SetupSplitKeys + {% + \ekvc@keycount=\ekv@zero + \let\ekvc@any@long\ekv@empty + \let\ekvc@initials\ekv@empty + \ekvpParse@unsafe\ekvp@@p@ekvc@setup@splitkeys + } +% \end{macrocode} +% We're calling a parser here without the safety nets testing for parser +% existence, so better define it now. +% \begin{macrocode} +\ekvpNewParser{ekvc@setup@splitkeys} +\ekvpDefType{ekvc@setup@splitkeys}{short} + {% + \let\ekvc@long\ekv@empty + \expandafter\ekvc@SetupSplitKeys@a\detokenize{#1}\ekv@stop{#3}% + } +\ekvpDefType{ekvc@setup@splitkeys}{long} + {% + \let\ekvc@long\long + \let\ekvc@any@long\long + \expandafter\ekvc@SetupSplitKeys@a\detokenize{#1}\ekv@stop{#3}% + } +\ekvpDefNoType{ekvc@setup@splitkeys} + {% + \let\ekvc@long\ekv@empty + \expandafter\ekvc@SetupSplitKeys@a\detokenize{#1}\ekv@stop{#3}% + } +% \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} +\ekvpDefNoValue{ekvc@setup@splitkeys} + {% + \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} +% +% Now everything is parsed down to the point that we got the key name and its +% value. We step the key counter and build the mark within a group to not +% accidentally define the |\csname|. +% \begin{macrocode} +\protected\long\def\ekvc@SetupSplitKeys@a + {% + \advance\ekvc@keycount1 + \begingroup\expandafter\endgroup + \expandafter\ekvc@SetupSplitKeys@b + \csname ekvc@splitmark@\the\ekvc@keycount\endcsname + } +\protected\long\def\ekvc@SetupSplitKeys@b#1#2\ekv@stop#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} + \ifx\ekvc@long\long + \else + \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} +% +% Here we get everything readily set up, |#1| contains the mark for unknown +% keys and they only have to do some reordering. +% \begin{macrocode} +\protected\long\def\ekvc@SetupSplitKeys@unknown#1% + {% + \long\def\ekvc@tmp##1##2##3##4#1##5{##4#1{##5, {##3} = {##1} }}% + \ekvletunknown\ekvc@set\ekvc@tmp + \long\def\ekvc@tmp##1##2##3#1##4{##3#1{##4, {##2} }}% + \ekvletunknownNoVal\ekvc@set\ekvc@tmp + \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@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 + \def\ekvc@tmp##1% + {% + \ekv@unexpanded\expandafter + {\csname ekvc@splitmark@\the\numexpr#1-##1\relax\endcsname}% + }% + \edef\ekvc@tmp + {% + \long\gdef + \ekv@unexpanded\expandafter{\csname ekvc@split@#1\endcsname}% + ####1% + \ekvc@tmp{6}####2% + \ekvc@tmp{5}####3% + \ekvc@tmp{4}####4% + \ekvc@tmp{3}####5% + \ekvc@tmp{2}####6% + \ekvc@tmp{1}####7% + \ekvc@tmp{0}####8% + ####9% + {% + \ekv@unexpanded\expandafter + {\csname ekvc@split@\the\numexpr#1-7\relax\endcsname}% + ####1{{####2}{####3}{####4}{####5}{####6}{####7}{####8}####9}% + }% + }% + \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@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 + {% + \let\ekvc@any@long\ekv@empty + \let\ekvc@initials\ekv@empty + \ekvpParse@unsafe\ekvp@@p@ekvc@setup@hashkeys + } +\ekvpNewParser{ekvc@setup@hashkeys} +\ekvpDefType{ekvc@setup@hashkeys}{short} + {% + \let\ekvc@long\ekv@empty + \expandafter\ekvc@SetupHashKeys@a\detokenize{#1}\ekv@stop{#3}% + } +\ekvpDefType{ekvc@setup@hashkeys}{long} + {% + \let\ekvc@long\long + \let\ekvc@any@long\long + \expandafter\ekvc@SetupHashKeys@a\detokenize{#1}\ekv@stop{#3}% + } +\ekvpDefNoType{ekvc@setup@hashkeys} + {% + \let\ekvc@long\ekv@empty + \expandafter\ekvc@SetupHashKeys@a\detokenize{#1}\ekv@stop{#3}% + } +\ekvpDefNoValue{ekvc@setup@hashkeys} + {% + \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} +% 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@a#1\ekv@stop + {% + \begingroup\expandafter\endgroup + \expandafter\ekvc@SetupHashKeys@b\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@b#1#2#3% + {% + \long\def\ekvc@tmp##1##2#1##3{##2#1{##1}}% + \ifx\ekvc@long\long + \else + \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} +% Another temporary definition, this time to get the hashmarks for the unknown +% handler without defining them. +% \begin{macrocode} +\def\ekvc@SetupHashKeys@unknown#1% + {% + \protected\def\ekvc@SetupHashKeys@unknown + {% + \ekvletunknown\ekvc@set\ekvc@hash@unknown@kv + \ekvletunknownNoVal\ekvc@set\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##4#1##5{##4#1{##5, {##3} = {##1} }}% + \long\def\ekvc@hash@unknown@k##1##2##3#1##4{##3#1{##4, {##2} }}% + } +\begingroup\expandafter\endgroup +\expandafter\ekvc@SetupHashKeys@unknown + \csname ekvc@hashmark@\ekvc@tripledots\endcsname +% \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} +% Secondary keys use yet another \expkvp\ parser, keys will be set up further +% down in their own subsection. +% \begin{macrocode} +\ekvpNewParser{ekvc@setup@secondary} +\ekvpValueAlwaysRequired{ekvc@setup@secondary} +\protected\long\def\ekvcSecondaryKeys#1% + {% + \edef\ekvc@set{\string#1}% + \let\ekvc@long\ekv@empty + \ekvpParse@unsafe\ekvp@@p@ekvc@setup@secondary + } +% \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} +% +% There is a single prefix named |long| and set up pretty simple. +% \begin{macrocode} +\ekvpDefPrefixLet{ekvc@setup@secondary}{long}\ekvc@long\long\ekv@empty +% \end{macrocode} +% +% \begin{macro}[internal] +% { +% meta, nmeta, +% \ekvc@type@meta, \ekvc@type@meta@ +% } +% The |meta| and |nmeta| key types use |\ekvmorekv| to set other keys in +% the same macro's \set. If the \kv\ list ist fixed (|nmeta|) we can expand +% |\ekvmorekv| as far as possible (twice), else we expand it only once. This +% makes a difference only if the \val\ of the |meta| key isn't forwarded in +% braces and commas are active at the time of usage, in which case expanding +% |\ekvmorekv| twice would result in wrong behaviour. +% \begin{macrocode} +\ekvpDefType{ekvc@setup@secondary}{meta} + {\ekvc@type@meta\ekv@exparg\ekvc@long{##1}\ekvlet{#1}{#3}} +\ekvpDefType{ekvc@setup@secondary}{nmeta} + {\ekvc@assert@not@long\ekvc@type@meta\ekv@expargtwice{}{}\ekvletNoVal{#1}{#3}} +\protected\long\def\ekvc@type@meta#1#2#3#4#5#6% + {% + #1\ekvc@type@meta@{\ekvmorekv{#6}}{#2}{#3}% + #4\ekvc@set{#5}\ekvc@tmp + } +\protected\long\def\ekvc@type@meta@#1#2#3{#2\def\ekvc@tmp#3{#1}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{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} +\ekvpDefType{ekvc@setup@secondary}{alias} + {% + \ekvc@assert@not@long + \ekvc@assert@k@or@p@defined{#3}% + \ekvifdefined\ekvc@set{#3}{\ekvletkv\ekvc@set{#1}\ekvc@set{#3}}{}% + \ekvifdefinedNoVal\ekvc@set{#3}{\ekvletkvNoVal\ekvc@set{#1}\ekvc@set{#3}}{}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{default} +% The |default| key can be used to set a \Nkey\ for an existing \Vkey. It +% will just pass the \val\ to the key macro of that other key. +% \begin{macrocode} +\ekvpDefType{ekvc@setup@secondary}{default} + {% + \ekvc@assert@defined{#1}% + \ekvc@assert@not@long + \edef\ekvc@tmp + {% + \ekv@unexpanded\expandafter + {\csname\ekv@name\ekvc@set{#1}\endcsname{#3}}% + }% + \ekvletNoVal\ekvc@set{#1}\ekvc@tmp + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{enum,choice} +% \begin{macro}[internal] +% { +% \ekvc@type@enum,\ekvc@h@enum,\ekvc@h@enum@,\ekvc@enum@name, +% \ekvc@type@choice@ +% } +% 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} +\ekvpDefType{ekvc@setup@secondary}{enum} + {\ekvpAssertTwoValues{#3}\ekvc@type@enum\ekvc@type@enum@{#1}#3} +\ekvpDefType{ekvc@setup@secondary}{choice} + {\ekvpAssertTwoValues{#3}\ekvc@type@enum\ekvc@type@choice@{#1}#3} +\protected\long\def\ekvc@type@enum#1#2#3% + {% + \ekvc@assert@defined{#3}% +% \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}{#2}% + }% + \ekvlet\ekvc@set{#2}\ekvc@tmp + \def\ekvc@tmp{0}% + \expandafter\ekvcsvloop\expandafter + {\expandafter#1\csname\ekv@name\ekvc@set{#3}\endcsname{#2}}% + } +% \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}% + } +\ekv@exparg{\protected\long\def\ekvc@type@choice@#1#2#3}% + {% + \expandafter\expandafter\expandafter\edef\expandafter + \csname\ekvc@enum@name\ekvc@set{#2}{\detokenize{#3}}\endcsname + {\ekv@unexpanded{#1{#3}}}% + } +% \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}{aggregate,e-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} +\ekvpDefType{ekvc@setup@secondary}{aggregate} + {% + \let\ekvc@type@aggregate@ifexpanded\@secondoftwo + \ekvc@type@aggregate{#1}{#3}% + } +\ekvpDefType{ekvc@setup@secondary}{e-aggregate} + {% + \let\ekvc@type@aggregate@ifexpanded\@firstoftwo + \ekvc@type@aggregate{#1}{#3}% + } +% \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% + {% + \ekvc@assert@not@long + \ekvpAssertTwoValues{#2}% + \ekvc@type@aggregate@a + \ekvc@type@aggregate@long\ekvc@type@aggregate@short + {#1}#2% + } +\protected\long\def\ekvc@type@aggregate@a#1#2#3#4#5% + {% + \ekvc@assert@defined{#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}% + } +\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% + {% + \ekvc@type@aggregate@ifexpanded + {% + \long\def\ekvc@type@aggregate@tmp##1##2#1##3% + {\ekv@expanded{\ekv@unexpanded{##2#1}{#2}}}% + }% + {\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}}% + \ekvc@type@aggregate@ifexpanded + {\long\def#2##1##2#1##3{\ekv@expanded{\ekv@unexpanded{##2#1}{#3}}}}% + {\long\def#2##1##2#1##3{##2#1{#3}}}% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{process} +% The |process| type can reuse much of |aggregate|, just the last step of +% definition differ. +% \begin{macrocode} +\ekvpDefType{ekvc@setup@secondary}{process} + {% + \ekvpAssertTwoValues{#3}% + \ifx\ekvc@long\long + \ekv@fi@firstoftwo + \fi + \@secondoftwo + {% + \ekvc@type@aggregate@a + \ekvc@type@process@long\ekvc@type@process@long + }% + {% + \ekvc@type@aggregate@a + \ekvc@type@process@short\ekvc@type@process@short + }% + {#1}#3% + } +% \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}{flag-bool} +% \begin{macrocode} +\ekvpDefType{ekvc@setup@secondary}{flag-bool} + {% + \ekvc@assert@not@long + \ekvpAssertOneValue{#3}% + \ifdefined#3\else\ekvcFlagNew#3\fi + \ekvdef\ekvc@set{#1}% + {% + \ekv@ifdefined{ekvc@flag@set@##1}% + {% + \csname ekvc@flag@set@##1\expandafter\endcsname + \ekvcFlagHeight#3\ekv@stop#3% + }% + {\ekvc@err@invalid@bool{##1}}% + }% + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{flag-true,flag-false,flag-raise} +% \begin{macro}[internal]{\ekvc@type@flag} +% \begin{macrocode} +\protected\def\ekvc@type@flag#1#2#3% + {% + \ekvc@assert@not@long + \ekvpAssertOneValue{#3}% + \ifdefined#3\else\ekvcFlagNew#3\fi + \ekv@exparg{\ekvdefNoVal\ekvc@set{#2}}{#1#3}% + } +\ekvpDefType{ekvc@setup@secondary}{flag-true} + {\ekvc@type@flag\ekvcFlagSetTrue{#1}{#3}} +\ekvpDefType{ekvc@setup@secondary}{flag-false} + {\ekvc@type@flag\ekvcFlagSetFalse{#1}{#3}} +\ekvpDefType{ekvc@setup@secondary}{flag-raise} + {\ekvc@type@flag\ekvcFlagRaise{#1}{#3}} +% \end{macrocode} +% \end{macro} +% \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,\ekvcFlagResetGlobal} +% \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\ekvcFlagResetGlobal#1% + {\expandafter\ekvc@flag@reset\csname\ekvc@flag@name#1\endcsname\global} +\protected\def\ekvc@flag@reset#1#2% + {% + \ifcsname#10\endcsname + #2\expandafter\let\csname#10\endcsname\ekvc@undefined + \ekvc@flag@reset@1\ekv@stop#1{#2}% + \fi + } +\protected\def\ekvc@flag@reset@#1\ekv@stop#2#3\fi + {% + \fi + \ifcsname#2{#1}\endcsname + #3\expandafter\let\csname#2{#1}\endcsname\ekvc@undefined + \expandafter\ekvc@flag@reset@\the\numexpr#1+1\relax\ekv@stop#2{#3}% + \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@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} +\ekv@exparg{\def\ekvc@assert@not@long}% + {\ekvpAssertIfNot{\ifx\ekvc@long\long}{`long' not accepted}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@assert@defined,\ekvc@assert@k@or@p@defined} +% Since some keys interact with existing other keys we need to assert those +% exist. +% \begin{macrocode} +\long\def\ekvc@assert@defined#1% + {\ekvpAssertTF{\ekvifdefined\ekvc@set{#1}}{undefined key `#1'}} +\long\def\ekvc@assert@k@or@p@defined#1% + {% + \ekvpAssertTF + {\ekvifdefined\ekvc@set{#1}\@firstoftwo{\ekvifdefinedNoVal\ekvc@set{#1}}}% + {undefined key `#1'}% + } +% \end{macrocode} +% \end{macro} +% +% +% \subsubsection{Messages} +% +% \begin{macro}[internal] +% { +% \ekvc@errm, +% \ekvc@err@toomany,\ekvc@err@value@required, +% \ekvc@err@already@defined,\ekvc@err@no@key@macro, +% } +% Boring unexpandable error messages. +% \begin{macrocode} +\protected\long\def\ekvc@errm#1{\errmessage{expkv-cs Error: #1}} +\protected\long\def\ekvc@err@toomany#1% + {\ekvc@errm{Too many keys for macro `\string#1'}} +\protected\long\def\ekvc@err@value@required#1% + {\ekvc@errm{Missing value for key `\ekv@unexpanded{#1}'}} +\protected\long\def\ekvc@err@already@defined#1% + {\ekvc@errm{Macro `\string#1' already defined}} +\protected\long\def\ekvc@err@no@key@macro#1% + {\ekvc@errm{\string#1 is no key=val macro}} +% \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@key@or@macro, +% \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 #1}} +\def\ekvc@err@unknown@enum#1#2#3% + {\ekvc@err{unknown choice `#3' for `#2' in #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=<< |