% \iffalse meta-comment % % File: expkv-def.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-def.sty}{\from{expkv-def.dtx}{pkg}}} \generate{\file{expkv-def.tex}{\from{expkv-def.dtx}{tex}}} \generate{\file{t-expkv-def.tex}{\from{expkv-def.dtx}{ctx}}} \endbatchfile %^^A=<< % \fi % % \section{\expkvd} %^^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\ekvd@tmp {% \ProvidesFile{expkv-def.tex}% [\ekvdDate\space v\ekvdVersion\space a key-defining frontend for expkv]% } \input{expkv-def.tex} \ProvidesPackage{expkv-def}% [\ekvdDate\space v\ekvdVersion\space a key-defining frontend for expkv] % \end{macrocode} % \gobbledocstriptag % %^^A=<< %^^A the ConTeXt module >>= % \subsection{The \ConTeXt\ module} % \gobbledocstriptag %<*ctx> % \begin{macrocode} \writestatus{loading}{ConTeXt User Module / expkv-def} \usemodule[expkv-pop] \unprotect \input expkv-def.tex \writestatus{loading} {ConTeXt User Module / expkv-def / Version \ekvdVersion\space loaded} \protect\endinput % \end{macrocode} % \gobbledocstriptag % %^^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. Additionally load \expkvp, which aids in defining the type % system (and \expkvp\ will actually do the \expkv\ loading). % \begin{macrocode} \input expkv-pop % \end{macrocode} % % We make sure that \file{expkv-def.tex} is only input once: % \begin{macrocode} \expandafter\ifx\csname ekvdVersion\endcsname\relax \else \expandafter\endinput \fi % \end{macrocode} % % \begin{macro}{\ekvdVersion,\ekvdDate} % We're on our first input, so lets store the version and date in a macro. % \begin{macrocode} \def\ekvdVersion{1.0} \def\ekvdDate{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 |\ekvd@tmp| to use % |\ProvidesFile|, else this will expand to a |\relax| and do no harm. % \begin{macrocode} \csname ekvd@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 ekvd@tmp\endcsname=\catcode`\@ \catcode`\@=11 % \end{macrocode} % |\ekvd@tmp| will be reused later to handle expansion during the key % defining. But we don't need it to ever store information long-term after % \expkvd\ was initialized. % % \begin{macro}[internal]{\ekvd@ifprimitive} % \begin{macrocode} \protected\long\def\ekvd@ifprimitive#1% {% \begingroup \edef\ekvd@tmpa{\string #1}% \edef\ekvd@tmpb{\meaning#1}% \expandafter \endgroup \ifx\ekvd@tmpa\ekvd@tmpb \ekv@fi@firstoftwo \fi \@secondoftwo } % \end{macrocode} % \end{macro} % % \begin{macro}[internal] % {\ekvd@long,\ekvd@prot,\ekvd@clear@prefixes,\ekvd@ifalso} % \expkvd\ will use |\ekvd@long|, |\ekvd@prot|, and |\ekvd@ifalso| to store % whether a key should be defined as |\long| or |\protected| or adds an action % to an existing key, and we have to clear them for every new key. By default % |long| and |protected| will just be empty, |ifalso| will be % |\@secondoftwo|, and |ifnew| will just use its third argument. % \begin{macrocode} \protected\def\ekvd@clear@prefixes {% \let\ekvd@long\ekv@empty \let\ekvd@prot\ekv@empty \let\ekvd@ifalso\@secondoftwo \let\ekvd@do@new\@gobbletwo } \ekvd@clear@prefixes % \end{macrocode} % \end{macro} % % We define the parser for our front-facing macro and its \prefix{}es here: % \begin{macrocode} \ekvpNewParser{ekvd@definekeys} \ekvpDefNoValuePrefix{ekvd@definekeys} {\let\ekvd@ifnoarg\@firstoftwo} {\let\ekvd@ifnoarg\@secondoftwo} \ekvpDefPrefixLet{ekvd@definekeys}{long} \ekvd@long\long \ekv@empty \ekvpDefPrefixLet{ekvd@definekeys}{protected}\ekvd@prot\protected\ekv@empty \ekvpDefPrefixLet{ekvd@definekeys}{protect} \ekvd@prot\protected\ekv@empty \ekvpDefPrefixLet{ekvd@definekeys}{also}\ekvd@ifalso\@firstoftwo\@secondoftwo \ekvpDefPrefixLet{ekvd@definekeys}{new}\ekvd@do@new\ekvd@assert@new\@gobbletwo % \end{macrocode} % We ease the process of error throwing a bit for now by using our own macro % instead of relying on \expkvp's argument forwarding. % \begin{macrocode} \ekvpDefAutoPrefix{ekvd@definekeys}{\edef\ekvd@cur{\detokenize{#3}}}{} % \end{macrocode} % % \begin{macro}{\ekvdefinekeys} % This is the one front-facing macro which provides the interface to define % keys. It stores the \set\ for which the keys should be defined in % |\ekvd@set| and calls a parser defined with \expkvp. % \begin{macrocode} \protected\edef\ekvdefinekeys#1% {% \ekv@unexpanded{\def\ekvd@set}{#1}% \ekv@unexpanded{\ekvpParse@unsafe@auto\ekvp@@p@ekvd@definekeys}% \ekv@unexpanded\expandafter{\csname\ekvp@@p@ekvd@definekeys{ppa}\endcsname}% } % \end{macrocode} % \end{macro} % % \subsubsection{Key Types} % % \begin{macro}[internal]{\ekvd@def@type,\ekvd@def@type@fwd} % To reduce some typing the following is a shortcut to |\ekvpDefType|. The % |@fwd| variant will forward the key name and value and remove the % unprocessed key. The other variant automatically sets up a helper macro, % unfortunately this is necessary due to the design decision of \expkvp\ to % not |\detokenize| the key names while \expkvd\ used to do this very early. % \begin{macrocode} \protected\def\ekvd@def@type#1#2% {% \ekv@exparg{\ekvpDefType{ekvd@definekeys}{#1}} {% \expandafter\expandafter\csname ekvd@th@#1\endcsname \detokenize{##1}\ekv@stop{##2}{##3}% }% \long\expandafter\def\csname ekvd@th@#1\endcsname##1\ekv@stop##2##3{#2}% } \protected\long\def\ekvd@def@type@fwd#1#2% {\ekvpDefType{ekvd@definekeys}{#1}{\ekv@exparg{#2}{\detokenize{##1}}{##3}}} % \end{macrocode} % \end{macro} % % \begin{macro}{set} % \begin{macro}[internal]{\ekvd@type@set} % \begin{macrocode} \ekvd@def@type{set} {\ekvd@ifnoarg{\ekvd@type@set{#1}{#1}}{\ekvd@type@set{#1}{#3}}} \protected\def\ekvd@type@set#1#2% {% \ekvd@assert@not@long \ekvd@assert@not@protected \ekvd@do@new{NoVal}{#1}% \ekv@ifempty{#2}% {\ekvd@err@missing@definition}% {% \ekvd@ifalso {% \ekv@expargtwice{\ekvd@add@noval{#1}}% {\ekvchangeset{#2}}% \ekvd@assert@not@protected@also }% {\ekv@expargtwice{\ekvdefNoVal\ekvd@set{#1}}{\ekvchangeset{#2}}}% }% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{noval,enoval} % \begin{macro}[internal]{\ekvd@type@noval} % Another pretty simple type, |noval| just needs to assert that there is a % definition and that |long| wasn't specified. % There are types where the difference in the variants is so small, that we % define a common handler for them, those common handlers are named with % |@type@|. |noval| and |enoval| are so similar that we can use such a |@type@| % macro, even if we could've done |noval| in a slightly faster way without it. % \begin{macrocode} \ekvd@def@type@fwd{noval}{\ekvd@type@noval\def} \ekvd@def@type@fwd{enoval}{\ekvd@type@noval\edef} \protected\long\def\ekvd@type@noval#1#2#3% {% \ekvd@do@new{NoVal}{#2}% \ekvd@assert@arg \ekvd@assert@not@long \ekvd@prot#1\ekvd@tmp{#3}% \ekvd@ifalso {\ekv@exparg{\ekvd@add@noval{#2}}\ekvd@tmp{}}% {\ekvletNoVal\ekvd@set{#2}\ekvd@tmp}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{code,ecode} % \begin{macro}[internal]{\ekvd@type@code} % |code| is simple as well, |ecode| has to use |\edef| on a temporary macro, % since \expkv\ doesn't provide an |\ekvedef|. % \begin{macrocode} \ekvd@def@type@fwd{code}{\ekvd@type@code\def} \ekvd@def@type@fwd{ecode}{\ekvd@type@code\edef} \protected\long\def\ekvd@type@code#1#2#3% {% \ekvd@do@new{}{#2}% \ekvd@assert@arg \ekvd@prot\ekvd@long#1\ekvd@tmp##1{#3}% \ekvd@ifalso {\ekv@exparg{\ekvd@add@val{#2}}{\ekvd@tmp{##1}}{}}% {\ekvlet\ekvd@set{#2}\ekvd@tmp}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{default,qdefault,odefault,fdefault,edefault} % \begin{macro}[internal] % { % \ekvd@type@default,\ekvd@t@default,\ekvd@t@qdefault,\ekvd@t@odefault, % \ekvd@t@fdefault % } % |\ekvd@type@default| asserts there was an argument, also the key for which one % wants to set a default has to be already defined (this is not so important for % |default|, but |qdefault| requires it). If everything is good, |\edef| a % temporary macro that expands |\ekvd@set| and the |\csname| for the key. % The different expansion variants are implemented via |\ekv@unexpanded| and % some |\expandafter|s. % \begin{macrocode} \ekvd@def@type@fwd{default}{\ekvd@type@default{\ekv@unexpanded\expandafter}{}} \ekvd@def@type@fwd{odefault} {\ekvd@type@default{\ekv@unexpanded\expandafter\expandafter\expandafter}{}} \ekvpLet{ekvd@definekeys}{type}{qdefault}{odefault} \ekvd@def@type@fwd{fdefault} {\ekvd@type@default{\ekv@unexpanded\expandafter}{\romannumeral`\^^@}} \protected\long\def\ekvd@type@default#1#2#3#4% {% \ekvd@assert@arg \ekvd@do@new{NoVal}{#3}% \ekvd@assert@not@long \ekvifdefined\ekvd@set{#3}% {% \ekvd@prot\edef\ekvd@tmp {#1{#2\csname\ekv@name\ekvd@set{#3}\endcsname{#4}}}% \ekvd@ifalso {\ekv@exparg{\ekvd@add@noval{#3}}\ekvd@tmp{}}% {\ekvletNoVal\ekvd@set{#3}\ekvd@tmp}% }% {\ekvd@err@undefined@key{#3}}% } \ekvd@def@type{edefault} {% \ekvd@assert@arg \ekvd@do@new{NoVal}{#1}% \ekvd@assert@not@long \ekvifdefined\ekvd@set{#1}% {% \ekvd@prot\edef\ekvd@tmp {% \ekv@unexpanded\ekv@expanded {{\csname\ekv@name\ekvd@set{#1}\endcsname{#3}}}% }% \ekvd@ifalso {\ekv@exparg{\ekvd@add@noval{#1}}\ekvd@tmp{}}% {\ekvletNoVal\ekvd@set{#1}\ekvd@tmp}% }% {\ekvd@err@undefined@key{#1}}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{initial,oinitial,finitial,einitial} % \begin{macro}[internal]{\ekvd@type@initial} % \begin{macrocode} \ekvd@def@type@fwd{initial}{\ekvd@type@initial\@firstofone} \ekvd@def@type@fwd{oinitial}{\ekvd@type@initial\ekv@exparg} \ekvd@def@type@fwd{einitial}{\ekvd@type@initial\ekv@expandedarg} \ekvd@def@type@fwd{finitial}{\ekvd@type@initial\ekv@fexparg} \long\def\ekvd@type@initial#1#2#3% {% \ekvd@assert@not@new \ekvd@assert@not@also \ekvd@assert@not@long \ekvd@assert@not@protected \ekvd@ifnoarg {% \ekvifdefinedNoVal\ekvd@set{#2}% {\csname\ekv@name\ekvd@set{#2}N\endcsname}% {\ekvd@err@undefined@noval{#2}}% }% {% \ekvifdefined\ekvd@set{#2}% {#1{\csname\ekv@name\ekvd@set{#2}\endcsname}{#3}}% {\ekvd@err@undefined@key{#2}}% }% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{bool,gbool,boolTF,gboolTF,invbool,ginvbool,invboolTF,ginvboolTF} % \begin{macro}[internal]{\ekvd@type@bool} % The boolean types are a quicker version of a |choice| that accept |true| and % |false|, and set up the |NoVal| action to be identical to \texttt{\key=true}. % The |true| and |false| actions are always just |\let|ting the macro in |#7| to % some other macro (\emph{e.g.}, \cs[no-index]{iftrue}). % \begin{macrocode} \ekvd@def@type@fwd{bool}{\ekvd@type@bool{}\iftrue\iffalse} \ekvd@def@type@fwd{gbool}{\ekvd@type@bool\global\iftrue\iffalse} \ekvd@def@type@fwd{boolTF}{\ekvd@type@bool{}\@firstoftwo\@secondoftwo} \ekvd@def@type@fwd{gboolTF}{\ekvd@type@bool\global\@firstoftwo\@secondoftwo} \ekvd@def@type@fwd{invbool}{\ekvd@type@bool{}\iffalse\iftrue} \ekvd@def@type@fwd{ginvbool}{\ekvd@type@bool\global\iffalse\iftrue} \ekvd@def@type@fwd{invboolTF}{\ekvd@type@bool{}\@secondoftwo\@firstoftwo} \ekvd@def@type@fwd{ginvboolTF}{\ekvd@type@bool\global\@secondoftwo\@firstoftwo} \protected\def\ekvd@type@bool#1#2#3#4#5% {% \ekvd@do@new{}{#4}% \ekvd@do@new{NoVal}{#4}% \ekvd@assert@filledarg{#5}% \ekvd@newlet#5#3% \ekvd@type@choice{#4}% \protected\ekvdefNoVal\ekvd@set{#4}{#1\let#5#2}% \protected\expandafter\def \csname\ekvd@choice@name\ekvd@set{#4}{true}\endcsname {#1\let#5#2}% \protected\expandafter\def \csname\ekvd@choice@name\ekvd@set{#4}{false}\endcsname {#1\let#5#3}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{boolpair,gboolpair,boolpairTF,gboolpairTF} % \begin{macro}[internal]{\ekvd@type@boolpair} % The boolean pair types are essentially the same as the boolean types, but set % two macros instead of one. % \begin{macrocode} \ekvd@def@type{boolpair} {\ekvd@assert@twoargs{#3}\ekvd@type@boolpair{}\iftrue\iffalse{#1}#3} \ekvd@def@type{gboolpair} {\ekvd@assert@twoargs{#3}\ekvd@type@boolpair\global\iftrue\iffalse{#1}#3} \ekvd@def@type{boolpairTF} {\ekvd@assert@twoargs{#3}\ekvd@type@boolpair{}\@firstoftwo\@secondoftwo{#1}#3} \ekvd@def@type{gboolpairTF} {% \ekvd@assert@twoargs{#3}% \ekvd@type@boolpair\global\@firstoftwo\@secondoftwo{#1}#3% } \protected\def\ekvd@type@boolpair#1#2#3#4#5#6% {% \ekvd@do@new{}{#4}% \ekvd@do@new{NoVal}{#4}% \ekvd@newlet#5#3% \ekvd@newlet#6#2% \ekvd@type@choice{#4}% \protected\ekvdefNoVal\ekvd@set{#4}{#1\let#5#2#1\let#6#3}% \protected\expandafter\def \csname\ekvd@choice@name\ekvd@set{#4}{true}\endcsname {#1\let#5#2#1\let#6#3}% \protected\expandafter\def \csname\ekvd@choice@name\ekvd@set{#4}{false}\endcsname {#1\let#5#3#1\let#6#2}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{data,gdata,dataT,gdataT} % \begin{macro}[internal]{\ekvd@type@data} % \begin{macrocode} \ekvd@def@type@fwd{data} {\ekvd@type@data\@secondoftwo\edef{####2}{\ekv@unexpanded{##1}}} \ekvd@def@type@fwd{edata} {% \ekvd@type@data\@secondoftwo\edef {####2}{\ekv@unexpanded\ekv@expanded{{##1}}}% } \ekvd@def@type@fwd{gdata} {\ekvd@type@data\@secondoftwo\xdef{####2}{\ekv@unexpanded{##1}}} \ekvd@def@type@fwd{xdata} {% \ekvd@type@data\@secondoftwo\xdef {####2}{\ekv@unexpanded\ekv@expanded{{##1}}}% } \ekvd@def@type@fwd{dataT}{\ekvd@type@data\@gobble\edef{}{\ekv@unexpanded{##1}}} \ekvd@def@type@fwd{edataT} {\ekvd@type@data\@gobble\edef{}{\ekv@unexpanded\ekv@expanded{{##1}}}} \ekvd@def@type@fwd{gdataT}{\ekvd@type@data\@gobble\xdef{}{\ekv@unexpanded{##1}}} \ekvd@def@type@fwd{xdataT} {\ekvd@type@data\@gobble\xdef{}{\ekv@unexpanded\ekv@expanded{{##1}}}} \protected\def\ekvd@type@data#1#2#3#4#5#6% {% \ekvd@do@new{}{#5}% \ekvd@assert@filledarg{#6}% \ekvd@newlet#6#1% \ekvd@ifalso {% \let\ekvd@prot\protected \ekvd@add@val{#5}{\long#2#6####1#3{####1{#4}}}{}% }% {% \protected\ekvd@long\ekvdef\ekvd@set{#5}% {\long#2#6####1#3{####1{#4}}}% }% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{box,gbox} % \begin{macro}[internal]{\ekvd@type@box} % Set up our boxes. Though we're a generic package we want to be colour safe, so % we put an additional grouping level inside the box contents, for the case that % someone uses \pkg{color}. |\ekvd@newreg| is a small wrapper which tests % whether the first argument is defined and if not does % |\csname new#2\endcsname#1|. % \begin{macrocode} \ekvd@def@type@fwd{box}{\ekvd@type@box{}} \ekvd@def@type@fwd{gbox}{\ekvd@type@box\global} \protected\def\ekvd@type@box#1#2#3% {% \ekvd@do@new{}{#2}% \ekvd@assert@filledarg{#3}% \ekvd@newreg#3{box}% \ekvd@ifalso {% \let\ekvd@prot\protected \ekvd@add@val{#2}{#1\setbox#3=\hbox{\begingroup##1\endgroup}}{}% }% {% \protected\ekvd@long\ekvdef\ekvd@set{#2}% {#1\setbox#3=\hbox{\begingroup##1\endgroup}}% }% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{toks,gtoks} % \begin{macro}[internal]{\ekvd@type@toks} % Similar to |box|, but set the |toks|. % \begin{macrocode} \ekvd@def@type@fwd{toks}{\ekvd@type@toks{}} \ekvd@def@type@fwd{gtoks}{\ekvd@type@toks\global} \protected\def\ekvd@type@toks#1#2#3% {% \ekvd@do@new{}{#2}% \ekvd@assert@filledarg{#3}% \ekvd@newreg#3{toks}% \ekvd@ifalso {% \let\ekvd@prot\protected \ekvd@add@val{#2}{#1#3={##1}}{}% }% {\protected\ekvd@long\ekvdef\ekvd@set{#2}{#1#3={##1}}}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[internal]{\ekvd@type@preapptoks,\ekvd@t@apptoks,\ekvd@t@gapptoks} % Just like |toks|, but expand the current contents of the |toks| register to % append the new contents. % \begin{macrocode} \ekvd@ifprimitive\toksapp {% \ekvd@def@type@fwd{apptoks}{\ekvd@type@preapptoks\toksapp} \ekvd@def@type@fwd{gapptoks}{\ekvd@type@preapptoks\gtoksapp} \ekvd@def@type@fwd{pretoks}{\ekvd@type@preapptoks\tokspre} \ekvd@def@type@fwd{gpretoks}{\ekvd@type@preapptoks\gtokspre} \protected\def\ekvd@type@preapptoks#1#2#3% {% \ekvd@do@new{}{#2}% \ekvd@assert@filledarg{#3}% \ekvd@newreg#3{toks}% \ekvd@ifalso {% \let\ekvd@prot\protected \ekvd@add@val{#2}{#1#3{##1}}{}% }% {\protected\ekvd@long\ekvdef\ekvd@set{#2}{#1#3{##1}}}% } } {% \ekvd@def@type@fwd{apptoks}{\ekvd@type@apptoks{}} \ekvd@def@type@fwd{gapptoks}{\ekvd@type@apptoks\global} \protected\def\ekvd@type@apptoks#1#2#3% {% \ekvd@do@new{}{#2}% \ekvd@assert@filledarg{#3}% \ekvd@newreg#3{toks}% \ekvd@ifalso {% \let\ekvd@prot\protected \ekvd@add@val{#2}{#1#3=\expandafter{\the#3##1}}{}% }% {% \protected\ekvd@long\ekvdef\ekvd@set{#2}% {#1#3=\expandafter{\the#3##1}}% }% } \ekvd@def@type@fwd{pretoks}{\ekvd@type@pretoks{}} \ekvd@def@type@fwd{gpretoks}{\ekvd@type@pretoks\global} \newtoks\ekvd@toks \protected\def\ekvd@type@pretoks#1#2#3% {% \ekvd@do@new{}{#2}% \ekvd@assert@filledarg{#3}% \ekvd@newreg#3{toks}% \ekvd@ifalso {% \let\ekvd@prot\protected \ekvd@add@val{#2}% {#1#3=\ekv@expanded{{\ekv@unexpanded{##1}\the#3}}}% {}% }% {% \protected\ekvd@long\ekvdef\ekvd@set{#2}% {#1#3=\ekv@expanded{{\ekv@unexpanded{##1}\the#3}}}% }% } } % \end{macrocode} % \end{macro} % % \begin{macro} % {int,eint,gint,xint,dimen,edimen,gdimen,xdimen,skip,eskip,gskip,xskip} % \begin{macro}[internal]{\ekvd@type@register} % The |\ekvd@type@register| can handle all the types for which the assignment will % just be \texttt{\meta{register}=\meta{value}}. % \begin{macrocode} \ekvd@def@type@fwd {int}{\ekvd@type@register{count}{}{}} \ekvd@def@type@fwd{eint}{\ekvd@type@register{count}{}\numexpr} \ekvd@def@type@fwd{gint}{\ekvd@type@register{count}\global{}} \ekvd@def@type@fwd{xint}{\ekvd@type@register{count}\global\numexpr} \ekvd@def@type@fwd {dimen}{\ekvd@type@register{dimen}{}{}} \ekvd@def@type@fwd{edimen}{\ekvd@type@register{dimen}{}\dimexpr} \ekvd@def@type@fwd{gdimen}{\ekvd@type@register{dimen}\global{}} \ekvd@def@type@fwd{xdimen}{\ekvd@type@register{dimen}\global\dimexpr} \ekvd@def@type@fwd {skip}{\ekvd@type@register{skip}{}{}} \ekvd@def@type@fwd{eskip}{\ekvd@type@register{skip}{}\glueexpr} \ekvd@def@type@fwd{gskip}{\ekvd@type@register{skip}\global{}} \ekvd@def@type@fwd{xskip}{\ekvd@type@register{skip}\global\glueexpr} \protected\def\ekvd@type@register#1#2#3#4#5% {% \ekvd@do@new{}{#4}% \ekvd@assert@filledarg{#5}% \ekvd@newreg#5{#1}% \ekvd@ifalso {% \let\ekvd@prot\protected \ekvd@add@val{#4}{#2#5=#3##1\relax}{}% }% {\protected\ekvd@long\ekvdef\ekvd@set{#4}{#2#5=#3##1\relax}}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{store,gstore,estore,xstore} % \begin{macro}[internal]{\ekvd@type@store} % The none-expanding |store| types use an |\edef| or |\xdef| and |\unexpanded| % to be able to also store |#| easily. % \begin{macrocode} \ekvd@def@type@fwd{store}{\ekvd@type@store\edef{\ekv@unexpanded{##1}}} \ekvd@def@type@fwd{gstore}{\ekvd@type@store\xdef{\ekv@unexpanded{##1}}} \ekvd@def@type@fwd{estore} {\ekvd@type@store\edef{\ekv@unexpanded\ekv@expanded{{##1}}}} \ekvd@def@type@fwd{xstore} {\ekvd@type@store\xdef{\ekv@unexpanded\ekv@expanded{{##1}}}} \protected\def\ekvd@type@store#1#2#3#4% {% \ekvd@do@new{}{#3}% \ekvd@assert@filledarg{#4}% \ekvd@newlet#4\ekv@empty \ekvd@ifalso {% \let\ekvd@prot\protected \ekvd@add@val{#3}{#1#4{#2}}{}% }% {\protected\ekvd@long\ekvdef\ekvd@set{#3}{#1#4{#2}}}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{meta,nmeta} % \begin{macro}[internal]{\ekvd@type@meta} % |meta| sets up things such that another instance of |\ekvset| will be run on % the argument, with the same \set. % \begin{macrocode} \ekvd@def@type@fwd{meta} {\ekvd@type@meta{}{##1}\ekvd@add@val{{##1}}{}\ekv@exparg} \ekvd@def@type@fwd{nmeta} {% \ekvd@assert@not@long \ekvd@type@meta {NoVal}{}\ekvd@add@noval{}\ekvd@assert@not@long@also\ekv@expargtwice } \protected\long\def\ekvd@type@meta#1#2#3#4#5#6#7#8% {% \ekvd@do@new{#1}{#7}% \ekvd@assert@filledarg{#8}% #6{\ekvd@prot\ekvd@long\def\ekvd@tmp#2}{\ekvmorekv{#8}}% \ekvd@ifalso {\ekv@exparg{#3{#7}}{\ekvd@tmp#4}{#5}}% {\csname ekvlet#1\endcsname\ekvd@set{#7}\ekvd@tmp}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{smeta,snmeta} % \begin{macro}[internal] % {\ekvd@type@smeta,\ekvd@type@smeta@} % |smeta| is pretty similar to |meta|, but needs two arguments inside of \val, % such that the first is the \set\ for which the sub-|\ekvset| and the second is % the \kv\ list. % \begin{macrocode} \ekvd@def@type@fwd{smeta}{\ekvd@type@smeta{}{##1}\ekvd@add@val{{##1}}{}} \ekvd@def@type@fwd{snmeta} {% \ekvd@assert@not@long \ekvd@type@smeta{NoVal}{}\ekvd@add@noval{}\ekvd@assert@not@long@also } \protected\long\def\ekvd@type@smeta#1#2#3#4#5#6#7% {% \ekvd@do@new{#1}{#6}% \ekvd@assert@twoargs{#7}% \ekvd@type@smeta@#7{#2}% \ekvd@ifalso {\ekv@exparg{#3{#6}}{\ekvd@tmp#4}{#5}}% {\csname ekvlet#1\endcsname\ekvd@set{#6}\ekvd@tmp}% } \protected\long\def\ekvd@type@smeta@#1#2#3% {\ekvd@prot\ekvd@long\ekv@expargtwice{\def\ekvd@tmp#3}{\ekvset{#1}{#2}}} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{choice} % \begin{macro}[internal]{\ekvd@type@choice,\ekvd@populate@choice} % The real key definition of a |choice| type is pretty simple, the heavy % lifting is done by a helper macro at run time. Though setting up the choices % needs a bit of work. First the key macro definition. % \begin{macrocode} \protected\def\ekvd@type@choice#1% {% \ekvd@assert@not@long \ekv@expargtwice{\ekvd@prot\def\ekvd@tmp##1}% {% \expandafter\expandafter\expandafter \ekvd@h@choice \expandafter\expandafter\expandafter {\expandafter\ekvd@choice@name\expandafter{\ekvd@set}{#1}{##1}}% }% \ekvd@ifalso {% \ekvd@assert@val{#1}% \ekvd@if@not@already@choice{#1}% {% \ekv@exparg {% \expandafter\ekvd@add@aux \csname\ekv@name\ekvd@set{#1}\endcsname{{##1}}{#1}% }% {\ekvd@tmp{##1}}% {\ekvd@long\ekvdef}\ekvd@assert@not@long@also }% }% {\ekvlet\ekvd@set{#1}\ekvd@tmp}% } % \end{macrocode} % The set up of different choices is done through another parser, that one % needs relatively few types though. % \begin{macrocode} \ekvpNewParser{ekvd@populate@choice} \ekvpValueAlwaysRequired{ekvd@populate@choice} \ekvpDefType{ekvd@populate@choice}{protect} {\ekvd@populate@choice\protected{#1}{#3}} \ekvpDefType{ekvd@populate@choice}{unprotect}{\ekvd@populate@choice{}{#1}{#3}} \ekvpDefNoType{ekvd@populate@choice} {\ekvd@populate@choice{}{#1}{#3}} \ekvpLet{ekvd@populate@choice}{type}{protected}{protect} \ekvpLet{ekvd@populate@choice}{type}{unprotected}{unprotect} % \end{macrocode} % The choice definition is done by the following little helper: % \begin{macrocode} \protected\long\def\ekvd@populate@choice#1#2#3% {% #1\expandafter\edef \csname\ekvd@choice@name\ekvd@set\ekvd@set@choice{#2}\endcsname {\ekv@unexpanded{#3}}% } % \end{macrocode} % And we define the real type: % \begin{macrocode} \ekvd@def@type{choice} {% \ekvd@do@new{}{#1}% \ekvd@assert@arg \ekvd@type@choice{#1}% \def\ekvd@set@choice{#1}% \ekvpParse@unsafe\ekvp@@p@ekvd@populate@choice{#3}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{choice-store,choice-enum} % These two types define a special kind of |choice| key and are quite similar, % the only difference is what the different choices do (hence they use a % shared initialisation which differs in the chosen |populate| step). % \begin{macrocode} \ekvd@def@type@fwd{choice-store} {\ekvd@type@choicespecial\ekvd@populate@choicestore} \ekvd@def@type@fwd{choice-enum} {\ekvd@type@choicespecial\ekvd@populate@choiceenum} % \end{macrocode} % \begin{macro}[internal]{\ekvd@type@choicespecial} % Initialise similar to a |choice| key. The difference is that we require two % arguments (which we assert), a macro to store things in, and a |csv|-list % containing the allowed values. |#1| is the |populate| macro according to the % type used. % \begin{macrocode} \protected\long\def\ekvd@type@choicespecial#1#2#3% {% \ekvd@do@new{}{#2}% \ekvd@assert@twoargs{#3}% \ekvd@type@choice{#2}% \def\ekvd@set@choice{#2}% #1#3% } % \end{macrocode} % \end{macro} % \begin{macro}[internal] % {\ekvd@populate@choicestore,\ekvd@populate@choicestore@} % We initialise the storing macro if it doesn't yet exist, and then we loop % over the value list. The |\edef|s with |\unexpanded| are both necessary to % be able to store macro parameter tokens (the outer protects at define time, % the inner at use time). % \begin{macrocode} \protected\long\def\ekvd@populate@choicestore#1% {% \ekvd@newlet#1\ekv@empty \ekvparse{\ekvd@populate@choicestore@k#1}{\ekvd@populate@choicestore@kv#1}% } \protected\long\def\ekvd@populate@choicestore@kv#1#2#3% {% \protected\expandafter\edef \csname\ekvd@choice@name\ekvd@set\ekvd@set@choice{#2}\endcsname {\ekv@unexpanded{\edef#1{\ekv@unexpanded{#3}}}}% } \ekv@exparg{\protected\long\def\ekvd@populate@choicestore@k#1#2}% {\ekvd@populate@choicestore@kv{#1}{#2}{#2}} % \end{macrocode} % \end{macro} % \begin{macro}[internal] % {\ekvd@populate@choiceenum,\ekvd@populate@choiceenum@} % This is similar to the population of a |choice-store| type, but instead of % storing the values in a macro this initialises a count and stores the % position of the value in the list inside that count (zero-indexed). The % space is necessary to terminate the number scanning, which is the reason we % use |\@firstofone| (so that the space after the macro name isn't gobbled by % \TeX). % \begin{macrocode} \protected\long\def\ekvd@populate@choiceenum#1% {% \ekvd@newreg#1{count}% \def\ekvd@tmp{0}% \ekvcsvloop{\ekvd@populate@choiceenum@#1}% } \protected\long\def\ekvd@populate@choiceenum@#1#2% {% \protected\expandafter\edef \csname\ekvd@choice@name\ekvd@set\ekvd@set@choice{#2}\endcsname {#1=\@firstofone{\ekvd@tmp} }% \edef\ekvd@tmp{\the\numexpr\ekvd@tmp+1\relax}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{unknown-choice} % \begin{macrocode} \ekvd@def@type{unknown-choice} {% \ekvd@do@new@for@name{\ekvd@unknown@choice@name\ekvd@set{#1}}% \ekvd@assert@arg \ekvd@assert@not@long \ekvd@assert@not@also \ekvd@prot\expandafter \def\csname\ekvd@unknown@choice@name\ekvd@set{#1}\endcsname##1{#3}% } % \end{macrocode} % \end{macro} % % \begin{macro}{unknown} % \begin{macro}[internal]{\ekvd@type@unknown@code,\ekvd@type@unknown@noval} % The |unknown| type has different subtypes which would be the key names for % other types. It is first checked whether that subtype is defined, if it % isn't throw an error, else use that subtype. % \begin{macrocode} \ekvd@def@type{unknown} {% \ekv@ifdefined{ekvd@type@unknown@\detokenize{#1}}% {\csname ekvd@type@unknown@\detokenize{#1}\endcsname{#3}}% \ekvd@err@misused@unknown } % \end{macrocode} % The |unknown noval| type can use |\ekvdefunknownNoVal| directly (after % asserting some prefixes). % \begin{macrocode} \protected\long\def\ekvd@type@unknown@noval#1% {% \ekvd@do@new@for@name{\ekv@name\ekvd@set{}uN}% \ekvd@assert@arg \ekvd@assert@not@also \ekvd@prot\ekvd@long\ekvdefunknownNoVal\ekvd@set{#1}% } % \end{macrocode} % The |unknown code| type uses some trickery during the definition in order to % swap out |#1| and |#2| in the user supplied definition. This is done via a % temporary macro that stores the definition but gets the parameter numbers % reversed while the real definition is done. % \begin{macrocode} \protected\long\def\ekvd@type@unknown@code#1% {% \ekvd@do@new@for@name{\ekv@name\ekvd@set{}u}% \ekvd@assert@arg \ekvd@assert@not@also \begingroup \def\ekvd@tmp##1##2##3{#1}% \ekv@exparg {% \endgroup \ekvd@prot\ekvd@long\ekvdefunknown\ekvd@set }% {\ekvd@tmp{##2}{##1}{##3}}% } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[internal] % { % \ekvd@type@unknown@redirect, % \ekvd@type@unknown@redirect-code, % \ekvd@type@unknown@redirect-noval % } % The |unknown redirect| types also just forward to |\ekvredirectunknown| % after asserting some prefixes. % \begin{macrocode} \protected\edef\ekvd@type@unknown@redirect#1% {% \expandafter\noexpand\csname ekvd@type@unknown@redirect-code\endcsname{#1}% \expandafter\noexpand\csname ekvd@type@unknown@redirect-noval\endcsname{#1}% } \protected\expandafter\def\csname ekvd@type@unknown@redirect-code\endcsname#1% {% \ekvd@do@new@for@name{\ekv@name\ekvd@set{}u}% \ekvd@assert@arg \ekvd@assert@not@also \ekvd@assert@not@protected \expandafter\ekvredirectunknown\expandafter{\ekvd@set}{#1}% } \protected\expandafter\def\csname ekvd@type@unknown@redirect-noval\endcsname#1% {% \ekvd@do@new@for@name{\ekv@name\ekvd@set{}uN}% \ekvd@assert@arg \ekvd@assert@not@also \ekvd@assert@not@protected \expandafter\ekvredirectunknownNoVal\expandafter{\ekvd@set}{#1}% } % \end{macrocode} % \end{macro} % % \subsubsection{Key Type Helpers} % There are some keys that might need helpers during their execution (not during % their definition, which are gathered as |@type@| macros). These helpers are % named |@h@|. % % \begin{macro}[internal]{\ekvd@h@choice,\ekvd@h@choice@} % The |choice| helper will just test whether the given choice was defined, if % not throw an error expandably, else call the macro which stores the code for % this choice. % \begin{macrocode} \def\ekvd@h@choice#1% {% \expandafter\ekvd@h@choice@ \csname\ifcsname#1\endcsname#1\else relax\fi\endcsname {#1}% } \def\ekvd@h@choice@#1#2% {% \ifx#1\relax \ekvd@err@choice@invalid{#2}% \expandafter\@gobble \fi #1% } % \end{macrocode} % \end{macro} % % % \subsubsection{Handling \texttt{also}} % % \begin{macro}[internal] % {\ekvd@add@val,\ekvd@add@noval,\ekvd@add@aux,\ekvd@add@aux@} % \begin{macrocode} \protected\long\def\ekvd@add@val#1#2#3% {% \ekvd@assert@val{#1}% \expandafter\ekvd@add@aux\csname\ekv@name\ekvd@set{#1}\endcsname{{##1}}% {#1}{#2}{\ekvd@long\ekvdef}{#3}% } \protected\long\def\ekvd@add@noval#1#2#3% {% \ekvd@assert@noval{#1}% \expandafter\ekvd@add@aux\csname\ekv@name\ekvd@set{#1}N\endcsname{}% {#1}{#2}\ekvdefNoVal{#3}% } \protected\long\def\ekvd@add@aux#1#2% {% \ekvd@extract@prefixes#1% \expandafter\ekvd@add@aux@\expandafter{#1#2}% } % \end{macrocode} % Once we're done with adding something to the definition of a key we need to % clear the prefixes. Maybe it would be better to only restore them if they % were changed by the |\ekvd@extract@prefixes| mechanism, but I think this is % fine with just resetting all (there should be no code dependent on any of % the prefix storing macros after this was run). % \begin{macrocode} \protected\long\def\ekvd@add@aux@#1#2#3#4#5% {% #5% \ekvd@prot#4\ekvd@set{#2}{#1#3}% \ekvd@clear@prefixes } % \end{macrocode} % \end{macro} % % \begin{macro}[internal] % { % \ekvd@extract@prefixes,\ekvd@extract@prefixes@, % \ekvd@extract@prefixes@long,\ekvd@extract@prefixes@prot % } % This macro checks which prefixes were used for the definition of a macro and % sets |\ekvd@long| and |\ekvd@prot| accordingly. % \begin{macrocode} \protected\def\ekvd@extract@prefixes#1% {% \expandafter\ekvd@extract@prefixes@\meaning#1\ekvd@stop } % \end{macrocode} % In the following definition |#1| will get replaced by |macro:|, |#2| by % |\long| and |#3| by |\protected| (in each, all tokens will have category % other). This allows us to parse the |\meaning| of a macro for those strings. % \begin{macrocode} \protected\def\ekvd@extract@prefixes@#1#2#3% {% \protected\def\ekvd@extract@prefixes@##1#1##2\ekvd@stop {% \ekvd@extract@prefixes@long ##1\ekvd@mark\@firstofone#2\ekvd@mark\@gobble\ekvd@stop {\let\ekvd@long\long}% \ekvd@extract@prefixes@prot ##1\ekvd@mark\@firstofone#3\ekvd@mark\@gobble\ekvd@stop {\let\ekvd@prot\protected}% }% \protected\def\ekvd@extract@prefixes@long##1#2##2\ekvd@mark##3##4\ekvd@stop {##3}% \protected\def\ekvd@extract@prefixes@prot##1#3##2\ekvd@mark##3##4\ekvd@stop {##3}% } % \end{macrocode} % We use a temporary macro to expand the three arguments of % |\ekvd@extract@prefixes@|, which will set up the real meaning of itself and % the parsing for |\long| and |\protected|. % \begin{macrocode} \begingroup \edef\ekvd@tmp {% \endgroup \ekvd@extract@prefixes@ {\detokenize{macro:}}% {\string\long}% {\string\protected}% } \ekvd@tmp % \end{macrocode} % \end{macro} % % % \subsubsection{Tests} % % \begin{macro}[internal]{\ekvd@newlet,\ekvd@newreg} % These macros test whether a control sequence is defined, if it isn't they % define it, either via |\let| or via the correct \cs[no-index]{new\meta{reg}}. % \begin{macrocode} \protected\def\ekvd@newlet#1#2% {% \ifdefined#1% \ifx#1\relax\ekv@fi@gobble\fi\@firstofone \ekv@fi@gobble \fi \@firstofone{\let#1#2}% } \protected\def\ekvd@newreg#1#2% {% \ifdefined#1% \ifx#1\relax\ekv@fi@gobble\fi\@firstofone \ekv@fi@gobble \fi \@firstofone{\csname new#2\endcsname#1}% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal] % {\ekvd@assert@twoargs,\ekvd@ifnottwoargs,\ekvd@ifempty@gtwo} % A test for exactly two tokens can be reduced for an empty-test after gobbling % two tokens, in the case that there are fewer tokens than two in the argument, % only macros will be gobbled that are needed for the true branch, which doesn't % hurt, and if there are more this will not be empty. % \begin{macrocode} \long\def\ekvd@assert@twoargs#1% {\ekvd@ifnottwoargs{#1}{\ekvd@err@missing@definition\ekvpGobbleT}{}} \long\def\ekvd@ifnottwoargs#1% {% \ekvd@ifempty@gtwo#1\ekv@ifempty@B \ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo } \long\def\ekvd@ifempty@gtwo#1#2{\ekv@ifempty@\ekv@ifempty@A} % \end{macrocode} % \end{macro} % % \begin{macro}[internal] % { % \ekvd@assert@val,\ekvd@assert@val@,\ekvd@assert@noval,\ekvd@assert@noval@, % \ekvd@extract@args,\ekvd@extracted@args,\ekvd@one@arg@string % } % Assert that a given key is defined as a value taking key or a |NoVal| key with % the correct argument structure, respectively. % \begin{macrocode} \protected\def\ekvd@assert@val#1% {% \ekvifdefined\ekvd@set{#1}% {\expandafter\ekvd@assert@val@\csname\ekv@name\ekvd@set{#1}\endcsname}% {% \ekvifdefinedNoVal\ekvd@set{#1}% \ekvd@err@add@val@on@noval {\ekvd@err@undefined@key{#1}}% \ekvpGobbleT }% } \protected\def\ekvd@assert@val@#1% {% \expandafter\ekvd@extract@args\meaning#1\ekvd@stop \unless\ifx\ekvd@extracted@args\ekvd@one@arg@string \ekvd@err@unsupported@arg \expandafter\ekvpGobbleT \fi }% \protected\def\ekvd@assert@noval#1% {% \ekvifdefinedNoVal\ekvd@set{#1}% {\expandafter\ekvd@assert@noval@\csname\ekv@name\ekvd@set{#1}N\endcsname}% {% \ekvifdefined\ekvd@set{#1}% \ekvd@err@add@noval@on@val {\ekvd@err@undefined@key{#1}}% \ekvpGobbleT }% } \protected\def\ekvd@assert@noval@#1% {% \expandafter\ekvd@extract@args\meaning#1\ekvd@stop \unless\ifx\ekvd@extracted@args\ekv@empty \ekvd@err@unsupported@arg \expandafter\ekvpGobbleT \fi } \protected\def\ekvd@extract@args#1% {% \protected\def\ekvd@extract@args##1#1##2->##3\ekvd@stop {\def\ekvd@extracted@args{##2}}% } \expandafter\ekvd@extract@args\expandafter{\detokenize{macro:}} \edef\ekvd@one@arg@string{\string#1} % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\ekvd@assert@arg,\ekvd@ifnoarg} % The |\ekvd@ifnoarg| macro is initialised as |\@secondoftwo|. Each time a key % without an argument is encountered it will be set to |\@firstoftwo| for the % scope of that key's parsing. % \begin{macrocode} \def\ekvd@assert@arg{\ekvd@ifnoarg{\ekvd@err@missing@definition\ekvpGobbleT}{}} \let\ekvd@ifnoarg\@secondoftwo % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\ekvd@assert@filledarg,\ekvd@ifnoarg@or@empty} % \begin{macrocode} \long\def\ekvd@assert@filledarg#1% {\ekvd@ifnoarg@or@empty{#1}{\ekvd@err@missing@definition\ekvpGobbleT}{}} \long\def\ekvd@ifnoarg@or@empty#1% {% \ekvd@ifnoarg \@firstoftwo {\ekv@ifempty{#1}}% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal] % { % \ekvd@assert@not@long,\ekvd@assert@not@protected,\ekvd@assert@not@also, % \ekvd@assert@not@long@also,\ekvd@assert@not@protected@also, % \ekvd@assert@new,\ekvd@assert@not@new % } % Some key-types don't want to be |also|, |\long| or |\protected|, so we provide % macros to test this and throw an error, this could be silently ignored but now % users will learn to not use unnecessary stuff which slows the compilation % down. % \begin{macrocode} \def\ekvd@assert@not@long{\ifx\ekvd@long\long\ekvd@err@no@prefix{long}\fi} \def\ekvd@assert@not@protected {\ifx\ekvd@prot\protected\ekvd@err@no@prefix{protected}\fi} \def\ekvd@assert@not@also{\ekvd@ifalso{\ekvd@err@no@prefix{also}}{}} \def\ekvd@assert@not@long@also {\ifx\ekvd@long\long\ekvd@err@no@prefix@also{long}\fi} \def\ekvd@assert@not@protected@also {\ifx\ekvd@prot\protected\ekvd@err@no@prefix@also{protected}\fi} \def\ekvd@assert@new#1#2% {% \csname ekvifdefined#1\endcsname\ekvd@set{#2}% {\ekvd@err@not@new\ekvpGobbleT}% {}% } \def\ekvd@assert@not@new {\ifx\ekvd@do@new\ekvd@assert@new\ekvd@err@no@prefix{new}\fi} \def\ekvd@do@new@for@name#1% {% \ifx\ekvd@do@new\ekvd@assert@new \ekv@fi@firstofone \fi \@gobble{\ekv@ifdefined{#1}{\ekvd@err@not@new\ekvpGobbleT}{}}% } % \end{macrocode} % \end{macro} % % \begin{macro}[internal] % { % \ekvd@if@not@already@choice, \ekvd@if@not@already@choice@a, % \ekvd@if@not@already@choice@b % } % It is bad to use |also| on a key that already contains a |choice|, as both % choices would share the same valid values and thus lead to each callback being % used twice. The following is a rudimentary test against this. % \begin{macrocode} \protected\def\ekvd@if@not@already@choice#1% {% \expandafter\ekvd@if@not@already@choice@a \csname\ekv@name\ekvd@set{#1}\endcsname {}\ekvd@h@choice\ekvd@stop } \protected\def\ekvd@if@not@already@choice@a {% \expandafter\ekvd@if@not@already@choice@b } \long\protected\def\ekvd@if@not@already@choice@b#1\ekvd@h@choice#2\ekvd@stop {% \ekv@ifempty{#2}\@firstofone\@gobble } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\ekvd@ifspace,\ekvd@ifspace@} % Yet another test which can be reduced to an if-empty, this time by gobbling % everything up to the first space. % \begin{macrocode} \long\def\ekvd@ifspace#1% {% \ekvd@ifspace@#1 \ekv@ifempty@B \ekv@ifempty@false\ekv@ifempty@A\ekv@ifempty@B\@firstoftwo } \long\def\ekvd@ifspace@#1 % keep this space {% \ekv@ifempty@\ekv@ifempty@A } % \end{macrocode} % \end{macro} % % \subsubsection{Messages} % % Most messages of \expkvd\ are not expandable, since they only appear during % key-definition, which is not expandable anyway. % % \begin{macro}[internal] % { % \ekvd@errm,\ekvd@err@missing@definition, % \ekvd@err@missing@type,\ekvd@err@undefined@prefix,\ekvd@err@undefined@key, % \ekvd@err@no@prefix,\ekvd@err@no@prefix@also, % \ekvd@err@add@val@on@noval,\ekvd@err@add@noval@on@val, % \ekvd@err@unsupported@arg,\ekvd@err@not@new % } % The non-expandable error messages are boring, so here they are: % \begin{macrocode} \protected\long\def\ekvd@errm#1{\errmessage{expkv-def Error: #1}} \protected\def\ekvd@err@missing@definition {\ekvd@errm{Missing definition for key `\ekvd@cur'}} \protected\def\ekvd@err@missing@type {\ekvd@errm{Missing type prefix for key `\ekvd@cur'}} \protected\def\ekvd@err@undefined@prefix#1% {% \ekvd@errm {% Undefined prefix `\ekv@unexpanded{#1}' found while processing `\ekvd@cur'% }% } \protected\def\ekvd@err@undefined@key#1% {% \ekvd@errm {Undefined key `\ekv@unexpanded{#1}' found while processing `\ekvd@cur'}% } \protected\def\ekvd@err@undefined@noval#1% {% \ekvd@errm {% Undefined noval key `\ekv@unexpanded{#1}' found while processing `\ekvd@cur'% }% } \protected\def\ekvd@err@no@prefix#1% {\ekvd@errm{prefix `#1' not accepted in `\ekvd@cur'}} \protected\def\ekvd@err@no@prefix@also#1% {\ekvd@errm{`\ekvd@cur' not allowed with a `#1' key}} \protected\def\ekvd@err@add@val@on@noval {\ekvd@errm{`\ekvd@cur' not allowed with a NoVal key}} \protected\def\ekvd@err@add@noval@on@val {\ekvd@errm{`\ekvd@cur' not allowed with a value taking key}} \protected\def\ekvd@err@unsupported@arg {% \ekvd@errm {% Existing key-macro has the unsupported argument string `\ekvd@extracted@args' for key `\ekvd@cur'% }% } \protected\def\ekvd@err@not@new {\ekvd@errm{The key for `\ekvd@cur' is already defined}} \protected\long\def\ekvd@err@misused@unknown {\ekvd@errm{Misuse of the unknown type found while processing `\ekvd@cur'}} % \end{macrocode} % \end{macro} % % \begin{macro}[internal] % { % \ekvd@err@choice@invalid,\ekvd@err@choice@invalid@,\ekvd@choice@name, % \ekvd@unknown@choice@name % } % |\ekvd@err@choice@invalid| will have to use this mechanism to throw its % message. Also we have to retrieve the name parts of the choice in an easy way, % so we use parentheses of catcode 8 here, which should suffice in most cases to % allow for a correct separation. % \begin{macrocode} \def\ekvd@err@choice@invalid#1% {% \ekvd@err@choice@invalid@#1% } \begingroup \catcode40=8 \catcode41=8 \@firstofone{\endgroup \def\ekvd@choice@name#1#2#3% {% ekvd#1(#2)\detokenize{#3}% } \def\ekvd@unknown@choice@name#1#2% {% ekvd:u:#1(#2)% } \def\ekvd@err@choice@invalid@ ekvd#1(#2)\detokenize#3% {% \ekv@ifdefined{\ekvd@unknown@choice@name{#1}{#2}}% {\csname\ekvd@unknown@choice@name{#1}{#2}\endcsname{#3}}% {\ekvd@err{invalid choice `#3' for `#2' in set `#1'}}% } } % \end{macrocode} % \end{macro} % % \begin{macro}[internal]{\ekvd@err} % The expandable error messages use |\ekvd@err|, which is just like |\ekv@err| % from \expkv. It uses a runaway argument to start the error message. % \begin{macrocode} \ekv@exparg{\long\def\ekvd@err#1}{\ekverr{expkv-def}{#1}} % \end{macrocode} % \end{macro} % % Now everything that's left is to reset the category code of |@|. % \begin{macrocode} \catcode`\@=\ekvd@tmp % \end{macrocode} % % \gobbledocstriptag % %^^A=<<