summaryrefslogtreecommitdiff
path: root/macros/generic/expkv-bundle/expkv-pop.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/generic/expkv-bundle/expkv-pop.dtx')
-rw-r--r--macros/generic/expkv-bundle/expkv-pop.dtx700
1 files changed, 700 insertions, 0 deletions
diff --git a/macros/generic/expkv-bundle/expkv-pop.dtx b/macros/generic/expkv-bundle/expkv-pop.dtx
new file mode 100644
index 0000000000..dc05d6bd0b
--- /dev/null
+++ b/macros/generic/expkv-bundle/expkv-pop.dtx
@@ -0,0 +1,700 @@
+% \iffalse meta-comment
+%
+% File: expkv-pop.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-pop.sty}{\from{expkv-pop.dtx}{pkg}}}
+\generate{\file{expkv-pop.tex}{\from{expkv-pop.dtx}{tex}}}
+\generate{\file{t-expkv-pop.tex}{\from{expkv-pop.dtx}{ctx}}}
+\endbatchfile
+%</driver>^^A=<<
+% \fi
+%
+% \section{\expkvp}
+%^^A the LaTeX package >>=
+% \subsection{The \LaTeX\ Package}
+% Set up the \LaTeX\ package.
+% \gobbledocstriptag
+%<*pkg>
+% \begin{macrocode}
+\RequirePackage{expkv}
+\def\ekvp@tmp
+ {%
+ \ProvidesFile{expkv-pop.tex}%
+ [\ekvpDate\space v\ekvpVersion\space a prefix oriented parser]%
+ }
+\input{expkv-pop.tex}
+\ProvidesPackage{expkv-pop}%
+ [\ekvpDate\space v\ekvpVersion\space a prefix oriented parser]
+% \end{macrocode}
+% \gobbledocstriptag
+%</pkg>
+%^^A=<<
+%^^A the ConTeXt module >>=
+% \subsection{The \ConTeXt\ module}
+% Set up the \ConTeXt\ module.
+% \gobbledocstriptag
+%<*ctx>
+% \begin{macrocode}
+\writestatus{loading}{ConTeXt User Module / expkv-pop}
+\usemodule[expkv]
+\unprotect
+\input expkv-pop.tex
+\writestatus{loading}
+ {ConTeXt User Module / expkv-pop / Version \ekvpVersion\space loaded}
+\protect\endinput
+% \end{macrocode}
+% \gobbledocstriptag
+%</ctx>
+%^^A=<<
+%^^A main file >>=
+% \subsection{The Generic Code}
+% And another generic code package we need to set up.
+% \gobbledocstriptag
+%<*tex>
+%
+% Loading the generic \expkv\ package if it wasn't already loaded, utilizing
+% that \expkv\ prevents itself from loading multiple times.
+% \begin{macrocode}
+\input expkv
+% \end{macrocode}
+%
+% Introduce an own guard against being loaded multiple times:
+% \begin{macrocode}
+\expandafter\ifx\csname ekvpVersion\endcsname\relax
+\else
+ \expandafter\endinput
+\fi
+% \end{macrocode}
+%
+% \begin{macro}{\ekvpVersion,\ekvpDate}
+% Specify the own version and date
+% \begin{macrocode}
+\def\ekvpVersion{1.0}
+\def\ekvpDate{2023-01-23}
+% \end{macrocode}
+% \end{macro}
+%
+% Reporting back who we are for \LaTeX\ (the package will have set up things for
+% us).
+% \begin{macrocode}
+\csname ekvp@tmp\endcsname
+% \end{macrocode}
+%
+% Package internal category code setup (stored to restore it at the end -- we
+% have to be careful to not lose this definition of |\ekvp@tmp| inside the
+% package).
+% \begin{macrocode}
+\expandafter\chardef\csname ekvp@tmp\endcsname=\catcode`\@
+\catcode`\@=11
+% \end{macrocode}
+%
+% \subsubsection{Parsing}^^A>>=
+%
+% \begin{macro}{\ekvpParse}
+% \begin{macro}[internal]
+% {
+% \ekvpParse@a,\ekvpParse@b,\ekvpParse@c,
+% \ekvpParse@unsafe,\ekvpParse@unsafe@auto
+% }
+% Parsing should be done in two steps of expansion, hence we put
+% |\unexpanded\expanded| around it. Next we check whether the parser is
+% defined, and afterwards run |\ekvparse|. For that we expand it once and
+% remove the |\unexpanded| which it uses itself.
+% \begin{macrocode}
+\long\def\ekvpParse#1#2%
+ {%
+ \ekv@unexpanded\ekv@expanded
+ {{\expandafter\ekvpParse@a\detokenize{#1}\ekv@mark{#2}}}%
+ }
+\def\ekvpParse@a#1\ekv@mark
+ {%
+ \ekv@ifdefined{ekvp@@p@#1}%
+ {\expandafter\ekvpParse@b\csname ekvp@@p@#1\endcsname}%
+ {\ekvp@err@unknownparser{#1}\@gobble}%
+ }
+\def\ekvpParse@b#1%
+ {%
+ \ekv@ifdefined{#1{ppa}}%
+ {%
+ \expandafter\ekvpParse@c\expandafter
+ {\expandafter#1\csname#1{ppa}\endcsname}%
+ }%
+ {\ekvpParse@c{#1{}}}%
+ }
+\def\ekvpParse@c#1#2#3{#2#3}
+\ekv@expargtwice{\long\def\ekvpParse@c#1#2}%
+ {\expandafter\ekvpParse@c\ekvparse{\ekvp@k#1}{\ekvp@p#1}{#2}}
+\long\def\ekvpParse@unsafe#1#2#3{\ekv@unexpanded\ekv@expanded{{#2#3}}}
+\ekv@expargtwice{\long\def\ekvpParse@unsafe@auto#1#2#3}
+ {\expandafter\ekvpParse@unsafe\ekvparse{\ekvp@k#1#2}{\ekvp@p#1#2}{#3}}
+\ekv@expargtwice{\long\def\ekvpParse@unsafe#1#2}%
+ {\expandafter\ekvpParse@unsafe\ekvparse{\ekvp@k#1{}}{\ekvp@p#1{}}{#2}}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekvp@k}
+% For |NoVal| we use the parser specific rule.
+% \begin{macrocode}
+\def\ekvp@k#1{\csname #1k\endcsname#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekvp@ifspace,\ekvp@ifspace@}
+% This checks whether a space is inside its argument (gobbling up to the first
+% space and checking for an empty remainder).
+% \begin{macrocode}
+\long\def\ekvp@ifspace#1%
+ {%
+ \ekvp@ifspace@#1 \ekv@ifempty@B\ekv@ifempty@false
+ \ekv@ifempty@A\ekv@ifempty@B\@firstoftwo
+ }
+\long\def\ekvp@ifspace@#1 % keep this space
+ {\ekv@ifempty@\ekv@ifempty@A}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekvp@p}
+% \begin{macro}[internal]{\ekvp@prefix}
+% \begin{macro}[internal]{\ekvp@prefix@a,\ekvp@prefix@b}
+% If there should be a prefix there needs to be a space, so we check for one.
+% Then we split the first prefix of the rest.
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekvp@prefix#1}%
+ {%
+ \ekvp@ifspace{#1}%
+ {\ekvp@prefix@a#1\ekv@stop}%
+ {\ekvp@noprefix{#1}}%
+ }
+% \end{macrocode}
+% A prefix is parsed with a few additional logical groups. The input |#1| is
+% the parser's name macro, |#2| is the full item which should be parsed. And
+% we place an additional group in which the prefix macros will be collected.
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekvp@p#1#2#3}{\ekvp@prefix{#3}#1{#2}{#3}}
+% \end{macrocode}
+% The prefix is |\detokenize|d and forwarded. Then we check whether it's a
+% defined type or prefix.
+% \begin{macrocode}
+\long\def\ekvp@prefix@a#1 % keep this space
+ {\expandafter\ekvp@prefix@b\detokenize{#1}\ekv@mark{#1}}
+\ekv@exparg{\long\def\ekvp@prefix@b#1\ekv@mark#2#3\ekv@stop#4}%
+ {%
+ \ekv@ifdefined{#4{pt}@#1}
+ {\expandafter\ekvp@prefix@pt\csname #4{pt}@#1\endcsname{#1}}%
+ {%
+ \ekv@ifdefined{#4{pp}@#1}%
+ {\expandafter\ekvp@prefix@pp\csname #4{pp}@#1\endcsname}%
+ {\@firstoftwo{\ekvp@noprefix{#2 #3}}}%
+ }%
+ {#3}#4%
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekvp@prefix@pt,\ekvp@prefix@pt@}
+% A type ends the prefix parsing (every item can only have one type). The
+% argument |#1| is that type macro, and in |#2| is the types name. |#3| then
+% contains the name of the element, |#4| is the parser's name macro (which is
+% no longer needed here), |#5| is the list of prefix macros, |#6| the entire
+% unchanged item, and |#7| is the value provided to the current item. The
+% prefix macro list will be stepped through, each macro will get the type
+% name, element name, and complete unprocessed item, whereas the type macro
+% will not receive the type string (but instead the value), which is why we
+% put a |\@firstoftwo| there to remove the type. |\ekvp@prefix@pt@| is a
+% helper to fetch the first prefix macro (or the type should |#5| be empty).
+% \begin{macrocode}
+\long\def\ekvp@prefix@pt#1#2#3#4#5#6#7%
+ {%
+ \ekvp@prefix@pt@{#2}{#3}{#6}#5{\@firstoftwo#1}{#7}\ekvpEOT
+ \ekv@unexpanded{\ekvpEOA{#6}}%
+ }
+\long\def\ekvp@prefix@pt@#1#2#3#4{#4{#1}{#2}{#3}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekvp@prefix@pp}
+% A prefix macro is added to the list of prefixes (argument |#4|) and the next
+% prefix is searched.
+% \begin{macrocode}
+\ekv@exparg{\long\def\ekvp@prefix@pp#1#2#3#4}{\ekvp@prefix{#2}#3{#4#1}}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekvp@noprefix}
+% If no prefix was found (either because there was no space, or the first
+% space delimited thing wasn't a valid prefix) we check if there is a notype
+% rule or throw an error.
+% \begin{macrocode}
+\long\def\ekvp@noprefix#1#2%
+ {%
+ \ekv@ifdefined{#2{pn}}
+ {\expandafter\ekvp@prefix@pt\csname #2{pn}\endcsname{}{#1}}
+ \ekvp@err@missingtype
+ #2%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%^^A=<<
+%
+% \subsubsection{Defining Parsers}^^A>>=
+%
+% \begin{macro}{\ekvpNewParser}
+% \begin{macro}[internal]{\ekvpNewParser@}
+% To define a new parser we need to assert that it doesn't already exists (if
+% so we throw an error) and define the parser name. A parser name is defined
+% as the macro \cs[no-index]{ekvp@@p@\meta{name}}, that macro will take one
+% argument and build the name of a prefix or type. Additionally a new parser
+% gets its |NoVal| rule set up as the default rule.
+% \begin{macrocode}
+\protected\long\def\ekvpNewParser#1%
+ {\expandafter\ekvpNewParser@\detokenize{#1}\ekv@mark}
+\protected\def\ekvpNewParser@#1\ekv@mark
+ {%
+ \ekv@ifdefined{ekvp@@p@#1}%
+ {\ekvp@errm{Parser `#1' already defined}}%
+ {%
+ \expandafter\def\csname ekvp@@p@#1\endcsname##1{ekvp@@##1@#1}
+ \expandafter
+ \let\csname\csname ekvp@@p@#1\endcsname k\endcsname\ekvp@k@default
+ }%
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ekvpValueAlwaysRequired}
+% \begin{macro}[internal]{\ekvpValueAlwaysRequired@}
+% This just changes the default |NoVal| rule to throw an error.
+% \begin{macrocode}
+\protected\long\def\ekvpValueAlwaysRequired#1%
+ {\ekvp@parser@def{#1}\ekvpValueAlwaysRequired@{}}
+\protected\def\ekvpValueAlwaysRequired@#1%
+ {\expandafter\let\csname#1k\endcsname\ekvp@err@noval}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ekvpDefNoValue}
+% \begin{macro}[internal]{\ekvpDefNoValue@}
+% \begin{macrocode}
+\protected\long\def\ekvpDefNoValue#1%
+ {\ekvp@parser@def{#1}\ekvpDefNoValue@\@gobble}
+\protected\long\def\ekvpDefNoValue@#1#2%
+ {%
+ \def\ekvp@tmp##1{\ekv@unexpanded{#2}}%
+ \ekv@exparg{\long\expandafter\def\csname#1k\endcsname##1##2##3}%
+ {\ekvp@tmp{##3}}%
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ekvpUseNoValueMarker}
+% \begin{macro}[internal]{\ekvpUseNoValueMarker@}
+%
+% \begin{macrocode}
+\protected\long\def\ekvpUseNoValueMarker#1%
+ {\ekvp@parser@def{#1}\ekvpUseNoValueMarker@\@gobble}
+\protected\long\def\ekvpUseNoValueMarker@#1#2%
+ {%
+ \long\expandafter\edef\csname#1k\endcsname##1##2##3%
+ {\ekv@unexpanded{\ekvp@p}##1{##2}{##3}{\ekv@unexpanded{#2}}}%
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ekvpDefNoValuePrefix}
+% \begin{macro}[internal]{\ekvpDefNoValuePrefix@,\ekvpDefNoValuePrefix@@}
+% This might be the most complicated of the |NoVal|-behaviour changes. The
+% first two steps should be clear.
+% \begin{macrocode}
+\protected\long\def\ekvpDefNoValuePrefix#1%
+ {\ekvp@parser@def{#1}\ekvpDefNoValuePrefix@\@gobbletwo}
+\protected\def\ekvpDefNoValuePrefix@#1%
+ {\expandafter\ekvpDefNoValuePrefix@@\csname#1{ppn}\endcsname#1}
+% \end{macrocode}
+% This step is the complicated one combining the effects of \cs{ekvpDefPrefix}
+% with the standard |NoVal|-rule (but for which the no-value marker might have
+% been changed), so here's what happens:
+% We set up a temporary meaning for the control sequence name
+% forwarded as |#1| to extract the no-value marker of the current parser. Then
+% we set up the |NoVal|-macro of the current parser to expand to the normal
+% parsing route, there we inject |#1| as the first prefix macro in the
+% corresponding first argument of \cs[no-index]{ekvp@p}, and we extract the
+% no-value marker with the |\expandafter#1|-chain. This last step might
+% actually fail badly if \cs{ekvpValueAlwaysRequired} or \cs{ekvpDefNoValue}
+% was used.
+% \begin{macrocode}
+\protected\long\def\ekvpDefNoValuePrefix@@#1#2%
+ {%
+ \long\def#1\ekvp@p##1##2##3%
+ {%
+ \ekv@unexpanded{\ekvp@p}%
+ ####1{####2\ekv@unexpanded{#1}}{####3}{\ekv@unexpanded{##3}}%
+ }%
+ \long\expandafter\edef\csname#2k\endcsname##1##2##3%
+ {\expandafter\expandafter\expandafter#1\csname#2k\endcsname{}{}{}}%
+ \ekvp@parser@def@prefix#1%
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ekvpDefAutoPrefix}
+% \begin{macro}[internal]{\ekvpDefAutoPrefix@}
+% \begin{macrocode}
+\protected\long\def\ekvpDefAutoPrefix#1%
+ {\ekvp@parser@def{#1}\ekvpDefAutoPrefix@\@gobbletwo}
+\protected\long\def\ekvpDefAutoPrefix@#1%
+ {\expandafter\ekvp@parser@def@prefix\csname#1{ppa}\endcsname}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekvp@parser@def,\ekvp@parser@def@}
+% This just makes sure that the parser is defined and builds the parser name.
+% |#1| should be the user-level parser name, |#2| the macro that gets the
+% code-level parser forwarded, and |#3| should gobble all additional arguments
+% needed by whichever frontend macro this is used for in case of an error.
+% \begin{macrocode}
+\long\def\ekvp@parser@def#1%
+ {\expandafter\ekvp@parser@def@\detokenize{#1}\ekv@mark}
+\def\ekvp@parser@def@#1\ekv@mark#2#3%
+ {%
+ \ekv@ifdefined{ekvp@@p@#1}%
+ {\ekv@exparg{#2}{\csname ekvp@@p@#1\endcsname}}%
+ {\ekvp@errm{Parser `#1' not defined}#3}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvpDefPrefix}
+% \begin{macro}[internal]{\ekvpDefPrefix@,\ekvp@parser@def@prefix}
+% A prefix is stored as a macro. At first we define a temporary meaning just
+% to make sure that the user uses only the first three arguments.
+% The real macro is a bit more complicated. It places the user provided before
+% action (|#3|) where it currently is, the after action (|#4|) after
+% |\ekvpEOT| (so after the type action). It also fetches the next prefix or
+% the type macro and forwards the arguments to it.
+% \begin{macrocode}
+\protected\long\def\ekvpDefPrefix#1%
+ {\ekvp@parser@def{#1}\ekvpDefPrefix@\@gobblethree}
+\protected\long\def\ekvpDefPrefix@#1#2%
+ {\expandafter\ekvp@parser@def@prefix\csname#1{pp}@\detokenize{#2}\endcsname}
+\protected\long\def\ekvp@parser@def@prefix#1#2#3%
+ {%
+ \def#1##1##2##3{#2#3}%
+ \long\def#1##1##2##3##4##5\ekvpEOT
+ {\ekv@unexpanded{#2}##4{##1}{##2}{##3}##5\ekvpEOT\ekv@unexpanded{#3}}%
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ekvpDefPrefixLet,\ekvpDefPrefixStore}
+% These are just special cases of |\ekvpDefPrefix|, so nothing complicated
+% here.
+% \begin{macrocode}
+\ekv@exparg{\protected\long\def\ekvpDefPrefixLet#1#2#3#4#5}%
+ {\ekvpDefPrefix{#1}{#2}{\ekvpProtect{\let#3= #4}}{\ekvpProtect{\let#3= #5}}}
+\ekv@exparg{\protected\long\def\ekvpDefPrefixStore#1#2#3#4#5}%
+ {%
+ \ekvpDefPrefix{#1}{#2}%
+ {\ekvpProtect{\edef#3{\ekv@unexpanded{#4}}}}%
+ {\ekvpProtect{\edef#3{\ekv@unexpanded{#5}}}}%
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvpDefType,\ekvpDefNoType}
+% \begin{macro}[internal]{\ekvpDefType@,\ekvpDefNoType@,\ekvpDefType@NoType}
+% A type macro and a notype-macro are pretty similar, the whole difference is
+% the naming scheme. The macro leaves the user definition in the input and
+% places the |\ekvpEOP| and |\ekvpEOT| markers.
+% \begin{macrocode}
+\protected\long\def\ekvpDefType#1{\ekvp@parser@def{#1}\ekvpDefType@\@gobbletwo}
+\protected\long\def\ekvpDefType@#1#2%
+ {\ekvpDefType@NoType{#1{pt}@\detokenize{#2}}}%
+\protected\long\def\ekvpDefNoType#1{\ekvp@parser@def{#1}\ekvpDefNoType@\@gobble}
+\protected\long\def\ekvpDefNoType@#1{\ekvpDefType@NoType{#1{pn}}}
+\protected\long\def\ekvpDefType@NoType#1#2%
+ {%
+ \long\expandafter\def\csname#1\endcsname##1##2##3\ekvpEOT
+ {\ekv@unexpanded{\ekvpEOP{##2}#2\ekvpEOT{##2}}}%
+ }%
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ekvpLet}
+% \begin{macro}[internal]{\ekvpLet@a,\ekvpLet@b,\ekvpLet@c,\ekvpLet@d}
+% Letting has an optional argument, so we can't use |\ekvp@parser@def|
+% directly here, first we need to convert the optional argument to a normal
+% one so that we can gobble it normally. Then we check that the two parsers
+% are defined, and afterwards that the copy macro is. If all this is correct
+% we do the |\let|. Since arguments are shuffled around here, I'll list them
+% each time they get reordered (some might be curried).
+%
+% |#1| is the parser name, |#2| the type, |#3| the new \prefix/\type\ name,
+% |#4| the optional other parser name, |#5| the already existing
+% \prefix/\type\ which should be copied.
+% \begin{macrocode}
+\protected\long\def\ekvpLet#1#2#3{\ekvoptarg{\ekvpLet@a{#1}{#2}{#3}}{#1}}
+\protected\long\def\ekvpLet@a#1%
+ {\ekvp@parser@def{#1}\ekvpLet@b{\@firstoftwo\@gobblethree}}
+\protected\long\def\ekvpLet@b#1#2#3#4%
+ {\ekvp@parser@def{#4}\ekvpLet@c{\@firstoftwo\@gobblethree}#1{#2}{#3}}
+% \end{macrocode}
+% |#1| the other parser's macro, |#2| the parser's macro, |#3| the type, |#4|
+% the new \prefix/\type, |#5| the already existing \prefix/\type.
+% \begin{macrocode}
+\protected\long\def\ekvpLet@c#1#2#3%
+ {%
+ \ekv@ifdefined{ekvpLet@@\detokenize{#3}}%
+ {\expandafter\ekvpLet@d\csname ekvpLet@@\detokenize{#3}\endcsname{#1}{#2}}%
+ {\ekvp@errm{unknown type \detokenize{#3}}\@gobbletwo}%
+ }
+% \end{macrocode}
+% |#1| the type's macro, |#2| the other parser's macro, |#3| the parser's
+% macro, |#4| the new \prefix/\type, |#5| the already existing \prefix/\type.
+% \begin{macrocode}
+\protected\long\def\ekvpLet@d#1#2#3#4#5%
+ {%
+ \ekv@ifdefined{#2{#1}@\detokenize{#5}}%
+ {%
+ \expandafter\let
+ \csname#3{#1}@\detokenize{#4}\expandafter\endcsname
+ \csname#2{#1}@\detokenize{#5}\endcsname
+ }%
+ {\ekvp@errm{Undefined prefix/type \detokenize{#5} can't be copied}}%
+ }
+\def\ekvpLet@@prefix{pp}
+\def\ekvpLet@@type{pt}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+%^^A=<<
+%
+% \subsubsection{\texttt{NoVal} Handling}^^A>>=
+%
+% The idea of |NoVal| handling is taken from \pkg{expl3}. We define a marker
+% (or directly use \pkg{expl3}'s). The defined test is not as robust as
+% \pkg{expl3}'s, but pretty fast (and based on |\ekv@ifempty|).
+%
+% \begin{macro}{\ekvpIfNoVal}
+% \begin{macro}[internal]{\ekvp@novalflag,\ekvp@ifnoval}
+% \begin{macro}[internal]{\ekvp@k@default}
+% \begin{macrocode}
+\ekv@ifdefined{c_novalue_tl}
+ {\expandafter\let\expandafter\ekvp@novalflag\csname c_novalue_tl\endcsname}
+ {%
+ \begingroup
+ \lccode`\Z=`\-
+ \lccode`\:=`\-
+ \lccode`\N=\z@
+ \lccode`\V=\z@
+ \lowercase{\endgroup\def\ekvp@novalflag{ZNoValue:}}%
+ }
+\def\ekvpIfNoVal#1%
+ {%
+ \long\def\ekvpIfNoVal##1%
+ {%
+ \ekvp@ifnoval{}##1{}?!#1??!\ekv@ifempty@B\ekv@ifempty@true
+ \ekv@ifempty@A\ekv@ifempty@B\@secondoftwo
+ }%
+ \long\def\ekvp@ifnoval##1#1##2?##3?!{\ekv@ifempty@\ekv@ifempty@A##1##2}%
+ \long\def\ekvp@k@default##1##2##3{\ekvp@p##1{##2}{##3}{#1}}%
+ }
+\expandafter\ekvpIfNoVal\expandafter{\ekvp@novalflag}
+\let\ekvp@novalflag\ekvp@undefined
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+%^^A=<<
+%
+% \subsubsection{Assertions}^^A>>=
+%
+% Assertions are just wrappers around user provided code to throw errors. Each
+% of them
+%
+% \begin{macro}{\ekvpAssertIf,\ekvpAssertIfNot}
+% \begin{macro}[internal]{\ekvpAssertIf@,\ekvpAssertIf@good,\ekvpAssert@notgood}
+% \begin{macrocode}
+\def\ekvpAssertIf{\romannumeral\ekv@alignsafe\ekvoptarg{\ekvpAssertIf@{}}{EOT}}
+\def\ekvpAssertIfNot
+ {\romannumeral\ekv@alignsafe\ekvoptarg{\ekvpAssertIf@\else}{EOT}}
+\long\def\ekvpAssertIf@#1#2#3%
+ {%
+ #3#1\ekvpAssertIf@good\fi
+ \ekvpAssert@notgood{#2}%
+ }
+\long\def\ekvpAssertIf@good\fi\ekvpAssert@notgood#1#2%
+ {\fi\ekv@endalignsafe\ekv@zero}
+\long\def\ekvpAssert@notgood#1%
+ {%
+ \ekv@ifdefined{ekvpAssert@@#1}%
+ {\csname ekvpAssert@@#1\endcsname}%
+ {\ekvp@err@unknownmarker{#1}\ekvpAssert@@EOA}%
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\ekvpAssertTF,\ekvpAssertTFNot}
+% \begin{macro}[internal]{\ekvpAssertTF@}
+% \begin{macrocode}
+\def\ekvpAssertTF{\ekvoptarg{\ekvpAssertTF@{}}{EOT}}
+\def\ekvpAssertTFNot{\ekvoptarg{\ekvpAssertTF@{\@secondoftwo\@firstoftwo}}{EOT}}
+\long\def\ekvpAssertTF@#1#2#3%
+ {%
+ \ekv@alignsafe
+ #3#1%
+ {\ekv@endalignsafe\@gobble}%
+ {\romannumeral\ekvpAssert@notgood{#2}}%
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \ekvpAssertValue,\ekvpAssertNoValue,\ekvpAssertOneValue,
+% \ekvpAssertTwoValues
+% }
+% \begin{macro}[internal]{\ekvpAssert@further}
+% \begin{macro}[internal]{\ekvp@assert@num@args}
+% These here are special cases of assertions. First we define a helper, the
+% following might look like a recursive call, but we'll redefine the helper
+% later and need this definition only temporary.
+% \begin{macrocode}
+\ekv@exparg{\def\ekvpAssert@further#1#2#3}%
+ {\ekvoptarg{\ekvpAssert@further{#1}{#2}{#3}}{EOT}}
+% \end{macrocode}
+% Now the definitions, we just need to set up the tests and corresponding
+% error messages. All this passes through the \cs{ekvpAssertTF} check (which
+% we shortcut to its internal auxiliary function here).
+% \begin{macrocode}
+\ekv@exparg{\def\ekvpAssertValue}%
+ {\ekvpAssert@further{\@secondoftwo\@firstoftwo}{\ekvpIfNoVal}{missing value}}
+\ekv@exparg{\def\ekvpAssertNoValue}%
+ {\ekvpAssert@further{}{\ekvpIfNoVal}{superfluous value}}
+\ekv@exparg{\def\ekvpAssertOneValue}%
+ {\ekvpAssert@further{}{\ekvp@assert@num@args\@gobble}{argument count != 1}}
+\ekv@exparg{\def\ekvpAssertTwoValues}%
+ {\ekvpAssert@further{}{\ekvp@assert@num@args\@gobbletwo}{argument count != 2}}
+\long\def\ekvp@assert@num@args#1#2%
+ {%
+ \expandafter\ekv@ifempty@\expandafter\ekv@ifempty@A#1#2\ekv@ifempty@B
+ \ekv@ifempty@true\ekv@ifempty@A\ekv@ifempty@B\@secondoftwo
+ }
+% \end{macrocode}
+% And finally redefine our auxiliary that just does a bit of argument
+% reordering for us.
+% \begin{macrocode}
+\long\def\ekvpAssert@further#1#2#3#4#5{\ekvpAssertTF@{#1}{#4}{#2{#5}}{#3}}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[internal]{\ekvpAssert@@EOP,\ekvpAssert@@EOT,\ekvpAssert@@EOA}
+% \begin{macrocode}
+\def\ekvpAssert@@EOA#1#2%
+ {\long\def#1##1##2#2##3{\ekvp@err{##1 at `##3'}\ekv@endalignsafe\ekv@zero}}
+\ekvpAssert@@EOA\ekvpAssert@@EOP\ekvpEOP
+\ekvpAssert@@EOA\ekvpAssert@@EOT\ekvpEOT
+\ekvpAssert@@EOA\ekvpAssert@@EOA\ekvpEOA
+% \end{macrocode}
+% \end{macro}
+%
+%^^A=<<
+%
+% \subsubsection{Markers}^^A>>=
+%
+% \begin{macro}
+% {\ekvpGobbleP,\ekvpGobbleT,\ekvpGobbleA,\ekvpEOP,\ekvpEOT,\ekvpEOA}
+% Since after the |\ekvpEOT|, \emph{etc.} markers the full element is placed,
+% they are defined as |\@gobble|, and the functions gobbling up to them gobble
+% that argument as well.
+% \begin{macrocode}
+\long\def\ekvpGobbleP#1\ekvpEOP#2{}
+\long\def\ekvpGobbleT#1\ekvpEOT#2{}
+\long\def\ekvpGobbleA#1\ekvpEOA#2{}
+\let\ekvpEOP\@gobble
+\let\ekvpEOT\@gobble
+\let\ekvpEOA\@gobble
+% \end{macrocode}
+% \end{macro}
+%
+%^^A=<<
+%
+% \subsubsection{Miscellaneous Auxiliaries}^^A>>=
+%
+% \begin{macro}{\ekvpProtect}
+% \begin{macro}[internal]{\ekvpProtect@,\ekvpProtect@@}
+% The idea of this macro is pretty simple. In contexts in which a |\protected|
+% macro isn't expanded the |\ekvpProtect@@| expansion will just expand to
+% itself and in the process protect the argument using |\unexpanded|. If a
+% |\protected| macro would be expanded the |\ekvpProtect@| will remove
+% |\ekvpProtect@@| and unpack the argument.
+% \begin{macrocode}
+\def\ekvpProtect{\ekvpProtect@\ekvpProtect@@}
+\protected\long\def\ekvpProtect@\ekvpProtect@@#1{#1}
+\long\def\ekvpProtect@@#1{\ekv@unexpanded{\ekvpProtect@@{#1}}}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+%^^A=<<
+%
+% \subsubsection{Error messages}^^A>>=
+%
+% \begin{macro}[internal]
+% {
+% \ekvp@err,\ekvp@err@unknownparser,\ekvp@err@noval,\ekvp@err@missingtype,
+% \ekvp@errm
+% }
+% These should be pretty straight forward. We use |\ekverr| to throw
+% expandable errors.
+% \begin{macrocode}
+\protected\long\def\ekvp@errm#1{\errmessage{expkv-pop Error: #1}}
+\ekv@exparg{\long\def\ekvp@err#1}{\ekverr{expkv-pop}{#1}}
+\def\ekvp@err@unknownparser#1{\ekvp@err{unknown parser `#1'}}
+\long\def\ekvp@err@noval#1#2#3{\ekvp@err{missing value for `#3'}}
+\long\def\ekvp@err@missingtype#1#2#3#4{\ekvp@err{missing type in `#3'}}
+\long\def\ekvp@err@unknownmarker#1{\ekvp@err{unknown marker `#1'}}
+% \end{macrocode}
+% \end{macro}
+%
+%^^A=<<
+%
+% Undefine the no longer needed |\ekvp@prefix| and restore the category code of
+% |@|:
+% \begin{macrocode}
+\let\ekvp@prefix\ekvp@undefined
+\catcode`\@=\ekvp@tmp
+% \end{macrocode}
+%
+% \gobbledocstriptag
+%</tex>
+%^^A=<<