texlive[53516] Master: expkv (23jan20)

commits+karl at tug.org commits+karl at tug.org
Thu Jan 23 22:56:51 CET 2020


Revision: 53516
          http://tug.org/svn/texlive?view=revision&revision=53516
Author:   karl
Date:     2020-01-23 22:56:50 +0100 (Thu, 23 Jan 2020)
Log Message:
-----------
expkv (23jan20)

Modified Paths:
--------------
    trunk/Master/tlpkg/bin/tlpkg-ctan-check
    trunk/Master/tlpkg/tlpsrc/collection-plaingeneric.tlpsrc

Added Paths:
-----------
    trunk/Master/texmf-dist/doc/latex/expkv/
    trunk/Master/texmf-dist/doc/latex/expkv/README.md
    trunk/Master/texmf-dist/doc/latex/expkv/expkv.pdf
    trunk/Master/texmf-dist/source/latex/expkv/
    trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx
    trunk/Master/texmf-dist/tex/generic/expkv/
    trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex
    trunk/Master/texmf-dist/tex/latex/expkv/
    trunk/Master/texmf-dist/tex/latex/expkv/expkv.sty
    trunk/Master/tlpkg/tlpsrc/expkv.tlpsrc

Added: trunk/Master/texmf-dist/doc/latex/expkv/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/expkv/README.md	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/expkv/README.md	2020-01-23 21:56:50 UTC (rev 53516)
@@ -0,0 +1,31 @@
+-------------------------------------------------------------------------------
+# expkv -- an expandable key=val implementation
+
+Version 2020-01-22 v0.3
+
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+
+Hosted at https://github.com/Skillmon/tex_expkv
+
+-------------------------------------------------------------------------------
+
+Copyright (C) 2020 Jonathan P. Spratte
+
+This  work may be  distributed and/or  modified under  the conditions  of the
+LaTeX Project Public License (LPPL),  either version 1.3c  of this license or
+(at your option) any later version.  The latest version of this license is in
+the file:
+
+  http://www.latex-project.org/lppl.txt
+
+This work is "maintained" (as per LPPL maintenance status) by
+  Jonathan P. Spratte.
+
+-------------------------------------------------------------------------------
+
+This provides an expandable key=val implementation that is somewhat fast, but
+not the fastest key=val implementation available. It is generic code and
+completely self-contained. There is a LaTeX package `expkv.sty` included to play
+nice on LaTeX's package loading system, but that package is not needed and does
+not provide more functionality than the generic code in `expkv.tex`.


Property changes on: trunk/Master/texmf-dist/doc/latex/expkv/README.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/expkv/expkv.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/doc/latex/expkv/expkv.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/latex/expkv/expkv.pdf	2020-01-23 21:55:34 UTC (rev 53515)
+++ trunk/Master/texmf-dist/doc/latex/expkv/expkv.pdf	2020-01-23 21:56:50 UTC (rev 53516)

Property changes on: trunk/Master/texmf-dist/doc/latex/expkv/expkv.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx	2020-01-23 21:56:50 UTC (rev 53516)
@@ -0,0 +1,1451 @@
+% \iffalse meta-comment
+%
+% File: expkv.dtx Copyright (C) 2020 Jonathan P. Spratte
+%
+% This work  may be  distributed and/or  modified under  the conditions  of the
+% LaTeX Project Public License (LPPL),  either version 1.3c  of this license or
+% (at your option) any later version.  The latest version of this license is in
+% the file:
+%
+%   http://www.latex-project.org/lppl.txt
+%
+% ------------------------------------------------------------------------------
+%
+%<*driver>^^A>>=
+\def\nameofplainTeX{plain}
+\ifx\fmtname\nameofplainTeX\else
+  \expandafter\begingroup
+\fi
+\input l3docstrip.tex
+\askforoverwritefalse
+\preamble
+
+--------------------------------------------------------------
+expkv -- an expandable key=val implementation
+E-mail: jspratte at yahoo.de
+Released under the LaTeX Project Public License v1.3c or later
+See http://www.latex-project.org/lppl.txt
+--------------------------------------------------------------
+
+Copyright (C) 2020 Jonathan P. Spratte
+
+This  work may be  distributed and/or  modified under  the conditions  of the
+LaTeX Project Public License (LPPL),  either version 1.3c  of this license or
+(at your option) any later version.  The latest version of this license is in
+the file:
+
+  http://www.latex-project.org/lppl.txt
+
+This work is "maintained" (as per LPPL maintenance status) by
+  Jonathan P. Spratte.
+
+This work consists of the file  expkv.dtx
+and the derived files           expkv.pdf
+                                expkv.sty
+                                expkv.tex
+
+\endpreamble
+% stop docstrip adding \endinput
+\postamble
+\endpostamble
+\generate{\file{expkv.sty}{\from{expkv.dtx}{pkg}}}
+\generate{\file{expkv.tex}{\from{expkv.dtx}{tex}}}
+\ifx\fmtname\nameofplainTeX
+  \expandafter\endbatchfile
+\else
+  \expandafter\endgroup
+\fi
+%
+\IfFileExists{expkv.tex}{\input{expkv.tex}}{}
+\ProvidesFile{expkv.dtx}
+  [\csname ekvDate\endcsname\ an expandable key=val implementation]
+\PassOptionsToPackage{full}{textcomp}
+\documentclass{l3doc}
+\RequirePackage[oldstylenums,nott]{kpfonts}
+\input{glyphtounicode}
+\pdfgentounicode=1
+\RequirePackage{xfp} ^^A required for an example
+\RequirePackage{listings}
+\RequirePackage{booktabs}
+\RequirePackage{siunitx}
+\RequirePackage{xcolor}
+\RequirePackage{caption}
+\RequirePackage{microtype}
+\RequirePackage{accsupp}
+\lstset
+  {
+    ,flexiblecolumns=false
+    ,basewidth=.53em
+    ,gobble=2
+    ,basicstyle=\fontfamily{jkp}\itshape
+    ,morekeywords=^^A
+      {^^A
+        \ekvdef,\ekvdefNoVal,\ekvlet,\ekvletNoVal,\ekvletkv,\ekvletkvNoVal,^^A
+        \ekvset,\ekvparse,\ekvVersion,\ekvDate,\ekvifdefined,^^A
+        \ekvifdefinedNoVal,\ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak,^^A
+        \ekvsneak,\ekvsneakPre
+      }
+    ,morecomment=[l]\%
+    ,commentstyle=\color[gray]{0.4}
+    ,literate={\{}{{\CodeSymbol\{}}{1}
+              {\}}{{\CodeSymbol\}}}{1}
+    ^^A,literate=*{<key>}{\key}{4}{<set>}{\set}{4}
+  }
+\newcommand*\CodeSymbol[1]{\textbf{#1}}
+\RequirePackage{randtext}
+\let\metaORIG\meta
+\protected\def\meta #1{\texttt{\metaORIG{#1}}}
+\renewcommand*\thefootnote{\fnsymbol{footnote}}
+\definecolor{expkvred}{HTML}{9F393D}
+\colorlet{expkvgrey}{black!75}
+\makeatletter
+\newcommand*\expkv
+  {^^A
+    \texorpdfstring
+      {^^A
+        \mbox
+          {^^A
+            \BeginAccSupp{ActualText=expkv}^^A
+            \bfseries
+            {\color{expkvgrey}e\kern-.05em x\kern-.05em}^^A
+            \lower.493ex
+              \hbox{{\color{expkvgrey}P}\kern-.1em{\color{expkvred}k}}^^A
+            \kern-.18em{\color{expkvred}v}^^A
+            \EndAccSupp{}^^A
+          }^^A
+      }
+      {expkv}^^A
+  }
+\newcommand\kv{\meta{key}=\meta{value}}
+\newcommand\key{\meta{key}}
+\newcommand\val{\meta{value}}
+\newcommand\set{\meta{set}}
+\hypersetup{linkcolor=red!80!black,urlcolor=purple!80!black}
+\DoNotIndex{\def,\edef,\,,\=,\begingroup,\catcode,\chardef,\csname,\endcsname}
+\DoNotIndex{\endgroup,\endinput,\errmessage,\expandafter,\input,\let,\long}
+\DoNotIndex{\noexpand,\protected,\ProvidesFile,\ProvidesPackage,\relax,\space}
+\DoNotIndex{\@,\unexpanded,\string,\expanded}
+\DoNotIndex{\ifcsname}
+\DoNotIndex{\ifx}
+\DoNotIndex{\else}
+\DoNotIndex{\fi}
+\@gobble\fi ^^A ignoring \ifx and \ifcsname, but only one \fi
+\@ifdefinable\gobbledocstriptag{\def\gobbledocstriptag#1>{}}
+\newcommand*\pmso[1] ^^A poor man's strike out
+  {^^A
+    \leavevmode
+    \begingroup
+      \sbox0{#1}^^A
+      \rlap{\vrule height .6ex depth -.5ex width \wd0\relax}^^A
+      \usebox0\relax
+    \endgroup
+  }
+\makeatother
+\begin{document}
+  \title
+    {^^A
+      \texorpdfstring
+        {^^A
+          \huge
+          \href{https://github.com/Skillmon/tex_expkv}
+            {\expkv}^^A
+          \\[\medskipamount]
+          \Large an expandable \kv\ implementation^^A
+        }{expkv - an expandable <key>=<value> implementation}^^A
+    }
+  \date{\ekvDate\space v\ekvVersion}
+  \author{Jonathan P. Spratte\thanks{\protect\randomize{jspratte at yahoo.de}}}
+  \DocInput{expkv.dtx}
+\end{document}
+%</driver>^^A=<<
+% \fi
+%
+% \maketitle
+%
+% \begin{abstract}
+% \noindent\parfillskip=0pt
+% \expkv\ provides a small interface for \kv\ parsing. The parsing macro is
+% fully expandable, the \meta{code} of your keys might be not. \expkv\ is pretty
+% fast, but not the fastest available \kv\ solution (\pkg{keyval} for instance
+% is thrice as fast, but not expandable and it might strip braces it shouldn't
+% have stripped).
+% \end{abstract}
+%
+% \tableofcontents
+%
+% \begin{documentation}^^A>>=
+%
+% \section{Documentation}
+%
+% \expkv\ provides an expandable \kv\ parser. The \kv\ pairs should be given as
+% a comma separated list and the separator between a \key\ and the associated
+% \val\ should be an equal sign. Both, the commas and the equal signs, might be
+% of category 12 (other) or 13 (active). To support this is necessary as for
+% example \pkg{babel} turns characters active for some languages, for instance
+% the equal sign is turned active for Turkish.
+%
+% \expkv\ is usable as generic code or as a \LaTeX\ package. To use it, just
+% use one of:
+% \begin{lstlisting}
+% \usepackage{expkv} % LaTeX
+% \input expkv       % plainTeX
+% \end{lstlisting}
+% The \LaTeX\ package doesn't do more than \file{expkv.tex}, except calling
+% |\ProvidesPackage| and setting things up such that \file{expkv.tex} will use
+% |\ProvidesFile|.
+%
+% \subsection{Setting up Keys}\label{sec:define}
+%
+% Keys in \expkv\ (as in almost all other \kv\ implementations) belong to a
+% \emph{set} such that different sets can contain keys of the same name. Unlike
+% many other implementations \expkv\ doesn't provide means to set a default
+% value, instead we have keys that take values and keys that don't (the latter
+% are called |NoVal| keys by \expkv), but both can have the same name (on the
+% user level).
+%
+% The following macros are available to define new keys. Those macros containing
+% ``|def|'' in their name can be prefixed by anything allowed to prefix |\def|,
+% prefixes allowed for |\let| can prefix those with ``|let|'' in their name,
+% accordingly. Neither \set\ nor \key\ are allowed to be empty for new keys and
+% must not contain a |\par| or tokens that expand to it -- they must be legal
+% inside of |\csname ...\endcsname|.
+%
+% \begin{function}{\ekvdef}
+%   \begin{syntax}
+%     \cs{ekvdef}\marg{set}\marg{key}\marg{code}
+%   \end{syntax}
+%   Defines a \key\ taking a value in a \set\ to expand to \meta{code}. In
+%   \meta{code} you can use |#1| to refer to the given value.
+% \end{function}
+%
+% \begin{function}{\ekvdefNoVal}
+%   \begin{syntax}
+%     \cs{ekvdefNoVal}\marg{set}\marg{key}\marg{code}
+%   \end{syntax}
+%   Defines a no value taking \key\ in a \set\ to expand to \meta{code}.
+% \end{function}
+%
+% \begin{function}{\ekvlet}
+%   \begin{syntax}
+%     \cs{ekvlet}\marg{set}\marg{key}\meta{cs}
+%   \end{syntax}
+%   Let the value taking \key\ in \set\ to \meta{cs}, there are no checks on
+%   \meta{cs} enforced.
+% \end{function}
+%
+% \begin{function}{\ekvletNoVal}
+%   \begin{syntax}
+%     \cs{ekvletNoVal}\marg{set}\marg{key}\meta{cs}
+%   \end{syntax}
+%   Let the no value taking \key\ in \set\ to \meta{cs}, it is not checked
+%   whether \meta{cs} exists or that it takes no parameter.
+% \end{function}
+%
+% \begin{function}{\ekvletkv}
+%   \begin{syntax}
+%     \cs{ekvletkv}\marg{set}\marg{key}\marg{set2}\marg{key2}
+%   \end{syntax}
+%   Let the \key\ in \set\ to \meta{key2} in \meta{set2}, it is not checked
+%   whether that second key exists.
+% \end{function}
+%
+% \begin{function}{\ekvletkvNoVal}
+%   \begin{syntax}
+%     \cs{ekvletkvNoVal}\marg{set}\marg{key}\marg{set2}\marg{key2}
+%   \end{syntax}
+%   Let the \key\ in \set\ to \meta{key2} in \meta{set2}, it is not checked
+%   whether that second key exists.
+% \end{function}
+%
+% \subsection{Parsing Keys}
+%
+% \begin{function}{\ekvset}
+%   \begin{syntax}
+%     \cs{ekvset}\marg{set}\{\kv,\ldots\}
+%   \end{syntax}
+%   Splits \kv\ pairs on commas. From both \key\ and \val\ up to one space is
+%   stripped from both ends, if then only a braced group remains the braces are
+%   stripped as well. So |\ekvset{foo}{bar=baz}| and
+%   |\ekvset{foo}{ {bar}= {baz} }| will both do
+%   \texttt{\cs[no-index]{\meta{foobarcode}}\{baz\}}, so you can hide commas,
+%   equal signs and spaces at the ends of either \key\ or \val\ by putting
+%   braces around them. If you omit the equal sign the code of the key created
+%   with the |NoVal| variants described in \autoref{sec:define} will be
+%   executed. If \kv\ contains more than a single unhidden equal sign, it will
+%   be split at the first one and the others are considered part of the value.
+%   |\ekvset| should be nestable.
+% \end{function}
+%
+% \begin{function}{\ekvparse}
+%   \begin{syntax}
+%     \cs{ekvparse}\meta{cs1}\meta{cs2}\{\kv,\ldots\}
+%   \end{syntax}
+%   This macro parses the \kv\ pairs and provides those list elements which are
+%   only keys as the argument to \meta{cs1}, and those which are a \kv\ pair to
+%   \meta{cs2} as two arguments. It expands in exactly two steps of expansion.
+%   |\ekvbreak| and |\ekvsneak| and their relatives don't work in |\ekvparse|.
+%   It is analogue to \pkg{expl3}'s |\keyval_parse:NNn|.
+%
+%   As a small example:
+% \begin{lstlisting}
+% \ekvparse\handlekey\handlekeyval{foo = bar, key, baz={zzz}}
+% \end{lstlisting}
+%   would expand in exactly two steps to
+% \begin{lstlisting}
+% \handlekeyval{foo}{bar}\handlekey{key}\handlekeyval{baz}{zzz}
+% \end{lstlisting}
+%   and afterwards |\handlekey| and |\handlekeyval| would have to further handle
+%   the \key. There are no macros like these two contained in \expkv, you have
+%   to set them up yourself if you want to use |\ekvparse| (of course the names
+%   might differ).
+% \end{function}
+%
+% \subsection{Miscellaneous}
+%
+% \subsubsection{Other Macros}
+%
+% \expkv\ provides some other macros which might be of interest.
+%
+% \begin{function}{\ekvVersion,\ekvDate}
+%   These two macros store the version and date of the package.
+% \end{function}
+% \begin{function}{\ekvifdefined,\ekvifdefinedNoVal}
+%   \begin{syntax}
+%     \cs{ekvifdefined}\marg{set}\marg{key}\marg{true}\marg{false}
+%     \cs{ekvifdefinedNoVal}\marg{set}\marg{key}\marg{true}\marg{false}
+%   \end{syntax}
+%   These two macros test whether there is a \key\ in \set. It is false if
+%   either a hash table entry doesn't exist for that key or its meaning is
+%   |\relax|.
+% \end{function}
+%
+% \begin{function}{\ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak}
+%   \begin{syntax}
+%     \cs{ekvbreak}\marg{after}
+%   \end{syntax}
+%   Gobbles the remainder of the current |\ekvset| macro and its argument list
+%   and reinserts \meta{after}. So this can be used to break out of |\ekvset|.
+%   The first variant will also gobble anything that has been sneaked out using
+%   |\ekvsneak| or |\ekvsneakPre|, while |\ekvbreakPreSneak| will put
+%   \meta{after} before anything that has been smuggled and |\ekvbreakPostSneak|
+%   will put \meta{after} after the stuff that has been sneaked out.
+% \end{function}
+%
+% \begin{function}{\ekvsneak,\ekvsneakPre}
+%   \begin{syntax}
+%     \cs{ekvsneak}\marg{after}
+%   \end{syntax}
+%   Puts \meta{after} after the effects of |\ekvset|. The first variant will put
+%   \meta{after} after any other tokens which might have been sneaked before,
+%   while |\ekvsneakPre| will put \meta{after} before other smuggled stuff.
+%   This reads and reinserts the remainder of the current |\ekvset| macro and
+%   its argument list to do its job. A small usage example is shown in
+%   \autoref{sec:sneakex}.
+% \end{function}
+%
+% \bigskip
+%
+% \begin{function}{\ekv at name,\ekv at name@set,\ekv at name@key}
+%   \begin{syntax}
+%     \cs{ekv at name}\marg{set}\marg{key}\\
+%     \cs{ekv at name@set}\marg{set}\\
+%     \cs{ekv at name@key}\marg{key}
+%   \end{syntax}
+%   The names of the macros that correspond to a key in a set are build with
+%   these macros. The default definition of |\ekv at name@set| is
+%   ``\texttt{\csname ekv at name@set\endcsname{\set}}'' and the default of
+%   |\ekv at name@key| is ``\texttt{\csname ekv at name@key\endcsname{\key}}''.
+%   The complete name is build using |\ekv at name| which is equivalent to
+%   \texttt{\cs[no-index]{ekv at name@set}\marg{set}\cs{ekv at name@key}\marg{key}}.
+%   For |NoVal| keys an additional |N| gets appended irrespective of these
+%   macros' definition, so their name is
+%   \cs[no-index]{\csname ekv at name\endcsname{\set}{\key}N}. You might redefine
+%   |\ekv at name@set| and |\ekv at name@key| locally but \emph{don't redefine}
+%   |\ekv at name|!
+% \end{function}
+%
+% \subsubsection{Bugs}
+%
+% Just like \pkg{keyval}, \expkv\ is bug free. But if you find \pmso{bugs}hidden
+% features\footnote{Thanks, David!} you can tell me about them either via mail
+% (see the first page) or directly on GitHub if you have an account there:
+% \url{https://github.com/Skillmon/tex_expkv}
+%
+% \subsubsection{Comparisons}
+%
+% Comparisons of speed are done with a very simple test key and the help of the
+% \pkg{l3benchmark} package. The key and its usage should be equivalent to
+% \begin{lstlisting}
+% \protected\ekvdef{test}{height}{\def\myheight{#1}}
+% \ekvset{test}{ height = 6 }
+% \end{lstlisting}
+% and only the usage of the key, not its definition, is benchmarked.
+%
+% As far as I know \expkv\ is the only fully expandable \kv\ parser. I tried to
+% compare \expkv\ to every \kv\ package listed on
+% \href{https://ctan.org/topic/keyval}{CTAN}, however, one might notice that
+% some of those are missing from this list. That's because I didn't get the
+% others to work due to bugs, or because they just provide wrappers around other
+% packages in this list. For the impatient, the essence of these comparisons
+% regarding speed and buggy behaviour is contained in \autoref{tab:comp}.
+%
+% In this subsubsection is no benchmark of |\ekvparse| and |\keyval_parse:NNn|
+% contained, as most other packages don't provide equivalent features to my
+% knowledge. |\ekvparse| is slightly faster than |\ekvset|, but keep in mind
+% that it does less. The same is true for |\keyval_parse:NNn| compared to
+% |\keys_set:nn| of \pkg{expl3} (where the difference is much bigger).
+%
+% \paragraph{\pkg{keyval}} is between two and three times faster and has a
+% comparable feature set just a slightly different way how it handles keys
+% without values. That might be considered a drawback, as it limits the
+% versatility, but also as an advantage, as it might reduce doubled code. Keep
+% in mind that as soon as someone loads \pkg{xkeyval} the performance of
+% \pkg{keyval} gets replaced by \pkg{xkeyval}'s.
+%
+% Also \pkg{keyval} has a bug, which unfortunately can't really be resolved
+% without breaking backwards compatibility for \emph{many} documents, namely it
+% strips braces from the argument before stripping spaces if the argument isn't
+% surrounded by spaces, also it might strip more than one set of braces. Hence
+% all of the following are equivalent in their outcome, though the last two
+% lines should result in something different than the first two:
+% \begin{lstlisting}[belowskip=0pt]
+% \setkeys{foo}{bar=baz}
+% \setkeys{foo}{bar= {baz}}
+% \setkeys{foo}{bar={ baz}}
+% \setkeys{foo}{bar={{baz}}}
+% \end{lstlisting}
+%
+% \paragraph{\pkg{xkeyval}} is more than ten times slower, but it provides more
+% functionality, e.g., it has choice keys, boolean keys, and so on. It contains
+% the same bug as \pkg{keyval} as it has to be compatible with it by design (it
+% replaces \pkg{keyval}'s frontend), but also adds even more cases in which
+% braces are stripped that shouldn't be stripped, worsening the situation.
+%
+% \paragraph{\pkg{ltxkeys}} is over 200 times slower -- which is funny, because
+% it aims to be ``[\ldots] faster than these earlier packages [referring to
+% \pkg{keyval} and \pkg{xkeyval}].'' Since it aims to have a bigger feature set
+% than \pkg{xkeyval}, it most definitely also has a bigger feature set than
+% \expkv. Also, it can't parse |\long| input, so as soon as your values contain
+% a |\par|, it'll throw errors. Furthermore, \pkg{ltxkeys} doesn't strip outer
+% braces at all by design, which, imho, is a weird design choice. In addition
+% \pkg{ltxkeys} loads \pkg{catoptions} which is known to introduce bugs (e.g.,
+% see \url{https://tex.stackexchange.com/questions/461783}).
+%
+% \paragraph{\pkg{l3keys}} is at least three times slower, but has an, imho,
+% great interface to define keys. It strips \emph{all} outer spaces, even if
+% somehow multiple spaces ended up on either end. It offers more features, but
+% is pretty much bound to \pkg{expl3} code. Whether that's a drawback is up to
+% you.
+%
+% \paragraph{\pkg{pgfkeys}} is about one and a half times slower for one key,
+% but has an \emph{enormous} feature set. However, since adding additional keys
+% doesn't add as much needed time for \pkg{pgfkeys} compared to \expkv, it gets
+% faster than \expkv\ at around eight \kv\ pairs.
+% It has the same or a very similar bug \pkg{keyval} has. The brace bug (and
+% also the category fragility) can be fixed by \pkg{pgfkeyx}, but this package
+% was last updated in 2012 and it slows down |\pgfkeys| by factor~$8$. Also I
+% don't know whether this might introduce new bugs.
+%
+% \paragraph{\pkg{kvsetkeys} with \pkg{kvdefinekeys}} is about two times slower,
+% but it works even if commas and equals have category codes different from 12.
+% Else the features of the keys are equal to those of \pkg{keyval}, the parser
+% has more features, though.
+%
+% \paragraph{\pkg{options}} is a bit faster than \expkv\ (almost
+% \SI{10}{\percent}) and has a much bigger feature set. Unfortunately it also
+% suffers from the premature unbracing bug \pkg{keyval} has.
+%
+% \paragraph{\pkg{simplekv}} is hard to compare because I don't speak French (so
+% I don't understand the documentation) and from what I can see, there is no
+% direct way to define the equivalent test key. Nevertheless, I tested the
+% closest possible equivalent of my test key while siding for \pkg{simplekv}'s
+% design not forcing something into it it doesn't seem to be designed for. It is
+% more than four times slower and has hard to predict behaviour regarding brace
+% and space stripping, similar to \pkg{keyval}. The tested definition was:
+% \begin{lstlisting}[belowskip=0pt]
+% \usepackage{simplekv}
+% \setKVdefault[simplekv]{height={ abc}}                     % key setup
+% \setKV[simplekv]{ height = 6 }                             % benchmarked
+% \end{lstlisting}
+%
+% \paragraph{\pkg{yax}} is about 13 times slower. It has a pretty strange
+% syntax, imho, and again a direct equivalent is hard to define. It has the
+% premature unbracing bug, too. Also somehow loading \pkg{yax} broke
+% \pkg{options} for me. The tested definition was:
+% \begin{lstlisting}[belowskip=0pt]
+% \usepackage{yax}
+% \defactiveparameter yax {\storevalue\myheight yax:height } % key setup
+% \setparameterlist{yax}{ height = 6 }                       % benchmarked
+% \end{lstlisting}
+%
+% \begin{table}
+%   \def\fnsym{\textcolor{red!80!black}{*}}
+%   \begingroup
+%   \centering
+%   \newcommand*\yes{\textcolor{red!80!black}  {yes}}^^A
+%   \newcommand*\no {\textcolor{green!80!black}{no}}^^A
+%   \caption[Comparison of \kv\ packages]
+%     {^^A
+%       Comparison of \kv\ packages. The packages are ordered from
+%       fastest to slowest for one \kv\ pair. Benchmarking was done using
+%       \pkg{l3benchmark} and the scripts in the \file{Benchmarks} folder
+%       of the \href{https://github.com/Skillmon/tex_expkv}{git repository}.
+%       The columns $p_i$ are the polynomial coefficients of a linear fit to the
+%       run-time, $p_0$ can be interpreted as the overhead for initialisation
+%       and $p_1$ the cost per key. The column ``Category Fragile'' lists
+%       whether the parsing breaks with active commas or equal signs.^^A
+%       \label{tab:comp}^^A
+%     }
+%   \begin{tabular}{l*2{S[table-format=4.3]}ccc}
+%     \toprule
+%     Package & {$p_1$} & {$p_0$} & Brace-Bug & Category Fragile & Date \\
+%     \midrule
+%     \pkg{keyval}    &   13.415 &    2.229 & \yes & \yes & 2014-10-28 \\
+%     \pkg{options}   &   25.254 &   12.160 & \yes & \yes & 2015-03-01 \\
+%     \expkv          &   31.711 &    5.124 & \no  & \no  & 2020-01-15 \\
+%     \pkg{pgfkeys}   &   26.215 &   43.141 & \yes & \yes & 2020-01-08 \\
+%     \pkg{kvsetkeys} & {\fnsym} & {\fnsym} & \no  & \no  & 2019-12-15 \\
+%     \pkg{l3keys}    &  107.434 &   23.372 & \no  & \no  & 2020-01-12 \\
+%     \pkg{simplekv}  &  162.046 &    1.014 & \yes & \yes & 2017-08-08 \\
+%     \pkg{xkeyval}   &  258.505 &  168.761 & \yes & \yes & 2014-12-03 \\
+%     \pkg{yax}       &  510.662 &   37.961 & \yes & \yes & 2010-01-22 \\
+%     \pkg{ltxkeys}   & 3937.979 & 4512.835 & \no  & \no  & 2012-11-17 \\
+%     \bottomrule
+%   \end{tabular}
+%   \par
+%   \endgroup
+%   \medskip
+%   \fnsym For \pkg{kvsetkeys} the linear model used for the other
+%   packages is a poor fit, \pkg{kvsetkeys} seems to have approximately
+%   quadratic run-time, the coefficients of the second degree polynomial fit are
+%   $p_2=\num{9.653}$, $p_1=\num{40.896}$, and $p_0=\num{67.268}$. Of course the
+%   other packages might not really have linear run-time, but at least from 1~to
+%   20~keys the fits don't seem too bad (the maximum ratio $p_2/p_1$
+%   for the other packages is \num{8.987e-3}). If one extrapolates the fits
+%   for 100 \kv\ pairs one finds that most of them match pretty well, the
+%   exception being \pkg{ltxkeys}, which behaves quadratic as well with
+%   $p_2=\num{29.773}$, $p_1=\num{3312.744}$, and $p_0=\num{6805.363}$.
+% \end{table}
+%
+% \subsection{Examples}
+%
+% \subsubsection{Standard Use-Case}
+%
+% Say we have a macro for which we want to create a \kv\ interface. The macro
+% has a parameter, which is stored in the dimension |\ourdim| having a default
+% value from its initialization. Now we want to be able to change that dimension
+% with the |width| key to some specified value. For that we'd do
+% \begin{lstlisting}
+% \newdimen\ourdim
+% \ourdim=150pt
+% \protected\ekvdef{our}{width}{\ourdim=#1\relax}
+% \end{lstlisting}
+% as you can see, we use the set |our| here. We want the key to behave different
+% if no value is specified. In that case the key should not use the its initial
+% value, but be smart and determine the available space from
+% \cs[no-index]{hsize}, so we also define
+% \begin{lstlisting}
+% \protected\ekvdefNoVal{our}{width}{\ourdim=.9\hsize}
+% \end{lstlisting}
+% Now we set up our macro to use this \kv\ interface
+% \begin{lstlisting}
+% \protected\def\ourmacro#1{\begingroup\ekvset{our}{#1}\the\ourdim\endgroup}
+% \end{lstlisting}
+% Finally we can use our macro like in the following\\
+% \begin{minipage}{6cm}
+% \begin{lstlisting}
+% \ourmacro{}\par
+% \ourmacro{width}\par
+% \ourmacro{width=5pt}\par
+% \end{lstlisting}
+% \end{minipage}^^A
+% \begin{minipage}{\dimexpr\linewidth-6cm\relax}
+%   \newdimen\ourdim\ourdim=150pt
+%   \protected\ekvdef{our}{width}{\ourdim=#1\relax}^^A
+%   \protected\ekvdefNoVal{our}{width}{\ourdim=.9\hsize}^^A
+%   \protected\def\ourmacro#1^^A
+%     {\begingroup\ekvset{our}{#1}\the\ourdim\endgroup}^^A
+%   \ourmacro{}\par
+%   \ourmacro{width}\par
+%   \ourmacro{width=5pt}\par
+% \end{minipage}
+%
+% \subsubsection{An Expandable \kv\ Macro Using \cs[no-index]{ekvsneak}}
+% \label{sec:sneakex}
+%
+% Let's set up an expandable macro, that uses a \kv\ interface. The problems
+% we'll face for this are:
+% \begin{enumerate}
+%   \item ignoring duplicate keys
+%   \item default values for keys which weren't used
+%   \item providing the values as the correct argument to a macro (ordered)
+% \end{enumerate}
+%
+% First we need to decide which \kv\ parsing macro we want to do this with,
+% |\ekvset| or |\ekvparse|. For this example we also want to show the
+% usage of |\ekvsneak|, hence we'll choose |\ekvset|. And we'll have to use
+% |\ekvset| such that it builds a parsable list for our macro internals. To
+% gain back control after |\ekvset| is done we have to put an internal of our
+% macro at the start of that list, so we use an internal key that uses
+% |\ekvsneakPre| after any user input.
+%
+% To ignore duplicates will be easy if the value of the key used last will be
+% put first in the list, so the following will use |\ekvsneakPre| for the
+% user-level keys. If we wanted some key for which the first usage should be the
+% the binding one we would use |\ekvsneak| instead for that key.
+%
+% Providing default values can be done in different ways, we'll use a simple
+% approach in which we'll just put the outcome of our keys if they were used
+% with default values before the parsing list terminator.
+%
+% Ordering the keys can be done simply by searching for a specific token for
+% each argument which acts like a flag, so our sneaked out values will include
+% specific tokens acting as markers.
+%
+% Now that we have answers for our technical problems, we have to decide what
+% our example macro should do. How about we define a macro that calculates the
+% sine of a number and rounds that to a specified precision? As a small extra
+% this macro should understand input in radian and degree and the used
+% trigonometric function should be selectable as well. For the hard part of this
+% task (expandably evaluating trigonometric functions) we'll use the \pkg{xfp}
+% package.
+%
+% First we set up our keys according to our earlier considerations and set up
+% the user facing macro |\sine|. The end marker of the parsing list will be a
+% |\sine at stop| token, which we don't need to define and we put our defaults
+% right before it.
+%
+% \begin{lstlisting}
+% \RequirePackage{xfp}
+% \makeatletter
+% \ekvdef{expex}{f}{\ekvsneakPre{\f{#1}}}
+% \ekvdef{expex}{round}{\ekvsneakPre{\rnd{#1}}}
+% \ekvdefNoVal{expex}{degree}{\ekvsneakPre{\deg{d}}}
+% \ekvdefNoVal{expex}{radian}{\ekvsneakPre{\deg{}}}
+% \ekvdefNoVal{expex}{internal}{\ekvsneakPre{\sine at rnd}}
+% \newcommand*\sine[2]
+%   {\ekvset{expex}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine at stop{#2}}
+% \end{lstlisting}
+% For the sake of simplicity we defined the macro |\sine| with two mandatory
+% arguments, the first being the \kv\ list, the second the argument to the
+% trigonometric function. We could've used \pkg{xparse}'s facilities here to
+% define an expandable macro which takes an optional argument instead.
+%
+% Now we need to define some internal macros to extract the value of each key's
+% last usage (remember that this will be the group after the first special
+% flag-token). For that we use one delimited macro per key.
+% \begin{lstlisting}
+% \def\sine at rnd#1\rnd#2#3\sine at stop{\sine at deg#1#3\sine at stop{#2}}
+% \def\sine at deg#1\deg#2#3\sine at stop{\sine at f#1#3\sine at stop{#2}}
+% \def\sine at f#1\f#2#3\sine at stop{\sine at final{#2}}
+% \end{lstlisting}
+% After the macros |\sine at rnd|, |\sine at deg|, and |\sine at f| the macro
+% |\sine at final| will see
+% \texttt
+%   {^^A
+%     \cs[no-index]{sine at final}^^A
+%     \marg{f}\marg{degree/radian}\marg{round}\marg{num}^^A
+%   }.
+% Now |\sine at final| has to expandably deal with those arguments such that the
+% |\fpeval| macro of \pkg{xfp} gets the correct input. Luckily this is pretty
+% straight forward in this example. In |\fpeval| the trigonometric functions
+% have names such as |sin| or |cos| and the degree taking variants |sind| or
+% |cosd|. And since the |degree| key puts a |d| in |#2| and the |radian| key
+% leaves |#2| empty all we have to do to get the correct function name is stick
+% the two together.
+% \begin{lstlisting}
+% \newcommand*\sine at final[4]{\fpeval{round(#1#2(#4),#3)}}
+% \makeatother
+% \end{lstlisting}
+% Let's test our macro:\\
+% \begin{minipage}{.7\linewidth}
+%   \begin{lstlisting}[gobble=4]
+%   \sine{}{60}\par
+%   \sine{round=10}{60}\par
+%   \sine{f=cos,radian}{pi}\par
+%   \edef\myval{\sine{f=tan}{1}}\texttt{\meaning\myval}
+%   \end{lstlisting}
+% \end{minipage}\hfill
+% \begin{minipage}{.2\linewidth}
+%   \makeatletter
+%   \ekvdef{expex}{f}{\ekvsneakPre{\f{#1}}}^^A
+%   \ekvdef{expex}{round}{\ekvsneakPre{\rnd{#1}}}^^A
+%   \ekvdefNoVal{expex}{degree}{\ekvsneakPre{\deg{d}}}^^A
+%   \ekvdefNoVal{expex}{radian}{\ekvsneakPre{\deg{}}}^^A
+%   \ekvdefNoVal{expex}{internal}{\ekvsneakPre{\sine at rnd}}^^A
+%   \newcommand*\sine[2]
+%     {\ekvset{expex}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine at stop{#2}}^^A
+%   \def\sine at rnd#1\rnd#2#3\sine at stop{\sine at deg#1#3\sine at stop{#2}}^^A
+%   \def\sine at deg#1\deg#2#3\sine at stop{\sine at f#1#3\sine at stop{#2}}^^A
+%   \def\sine at f#1\f#2#3\sine at stop{\sine at final{#2}}^^A
+%   \newcommand*\sine at final[4]{\fpeval{round(#1#2(#4),#3)}}^^A
+%   \makeatother
+%   \sine{}{60}\par
+%   \sine{round=10}{60}\par
+%   \sine{f=cos,radian}{pi}\par
+%   \edef\myval{\sine{f=tan}{1}}\texttt{\meaning\myval}
+% \end{minipage}
+%
+%
+% \subsection{Error Messages}
+%
+% \expkv\ should only send messages in case of errors, there are no warnings and
+% no info messages. In this subsection those errors are listed.
+%
+% \subsubsection{Load Time}
+%
+% \file{expkv.tex} checks whether \eTeX\ is available. If it isn't, an error
+% will be thrown using |\errmessage|:
+% \begin{lstlisting}
+% ! expkv Error: e-TeX required.
+% \end{lstlisting}
+%
+% \subsubsection{Defining Keys}
+%
+% If you get any error from \expkv\ while you're trying to define a key, the
+% definition will be aborted and gobbled.
+%
+% If you try to define a key with an empty set name you'll get:
+% \begin{lstlisting}
+% ! expkv Error: empty set name not allowed.
+% \end{lstlisting}
+%
+% Similarly, if you try to define a key with an empty key name:
+% \begin{lstlisting}
+% ! expkv Error: empty key name not allowed.
+% \end{lstlisting}
+%
+% Both of these messages are done in a way that doesn't throw additional errors
+% due to |\global|, |\long|, etc., not being used correctly if you prefixed one
+% of the defining macros.
+%
+% \subsubsection{Using Keys}
+%
+% This subsubsection contains the errors thrown during |\ekvset|. The errors are
+% thrown in an expandable manner by providing an undefined macro. In the
+% following messages \lstinline|<key>| gets replaced with the problematic key's
+% name, and \lstinline|<set>| with the corresponding set. If any errors during
+% \kv\ handling are encountered, the entry in the comma separated list will be
+% omitted after the error is thrown and the next \kv\ pair will be parsed.
+%
+% If you're using an undefined key you'll get:
+% \begin{lstlisting}
+% ! Undefined control sequence.
+% <argument> \! expkv Error:
+%                            unknown key (`<key>', set `<set>').
+% \end{lstlisting}
+%
+% If you're using a key for which only a normal version and no |NoVal| version
+% is defined, but don't provide a value, you'll get:
+% \begin{lstlisting}
+% ! Undefined control sequence.
+% <argument> \! expkv Error:
+%                            value required (`<key>', set `<set>').
+% \end{lstlisting}
+%
+% If you're using a key for which only a |NoVal| version and no normal version
+% is defined, but provide a value, you'll get:
+% \begin{lstlisting}
+% ! Undefined control sequence.
+% <argument> \! expkv Error:
+%                            value forbidden (`<key>', set `<set>').
+% \end{lstlisting}
+%
+% If you're using a set for which you never executed one of the defining macros
+% from \autoref{sec:define} you'll get a low level \TeX\ error, as that isn't
+% actively tested by the parser (and hence will lead to undefined behaviour and
+% not be gracefully ignored). The error will look like
+% \begin{lstlisting}
+% ! Missing \endcsname inserted.
+% <to be read again>
+%                    \ekv<set>(
+% \end{lstlisting}
+%
+% \subsection{License}
+%
+% Copyright \textcopyright\ 2020\unless\ifnum\year=2020--\the\year\fi\
+% Jonathan P. Spratte
+%
+% \medskip\noindent
+% This work may be distributed and/or modified under the conditions of the
+% \LaTeX\ Project Public License (LPPL), either version 1.3c of this license or
+% (at your option) any later version. The latest version of this license is in
+% the file:
+%
+% \url{http://www.latex-project.org/lppl.txt}
+%
+% \noindent
+% This work is ``maintained'' (as per LPPL maintenance status) by
+%
+% \mbox{Jonathan P. Spratte}.
+%
+% \end{documentation}^^A=<<
+%
+% \begin{implementation}^^A>>=
+%
+% \clearpage
+%
+% \section{Implementation}^^A>>=
+%^^A the LaTeX package >>=
+% \subsection{The \LaTeX\ Package}
+% First we set up the \LaTeX\ package. That one doesn't really do much except
+% |\input|ting the generic code and identifying itself as a package.
+% \gobbledocstriptag
+%<*pkg>
+%    \begin{macrocode}
+\def\ekv at tmp
+  {%
+    \ProvidesFile{expkv.tex}%
+      [\ekvDate\space v\ekvVersion\space an expandable key=val implementation]%
+  }
+\input{expkv.tex}
+\ProvidesPackage{expkv}%
+  [\ekvDate\space v\ekvVersion\space an expandable key=val implementation]
+%    \end{macrocode}
+% \gobbledocstriptag
+%</pkg>
+%^^A=<<
+%^^A main file >>=
+% \subsection{The Generic Code}
+% The rest of this implementation will be the generic code.
+% \gobbledocstriptag
+%<*tex>
+%
+% Check whether \eTeX\ is available -- \expkv\ requires \eTeX.
+%    \begin{macrocode}
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname numexpr\endcsname\relax
+  \errmessage{expkv requires e-TeX}
+  \expandafter\endinput
+\fi
+%    \end{macrocode}
+%
+% We make sure that it's only input once:
+%    \begin{macrocode}
+\expandafter\ifx\csname ekvVersion\endcsname\relax
+\else
+  \expandafter\endinput
+\fi
+%    \end{macrocode}
+% \begin{macro}{\ekvVersion,\ekvDate}
+% We're on our first input, so lets store the version and date in a macro.
+%    \begin{macrocode}
+\def\ekvVersion{0.3}
+\def\ekvDate{2020-01-22}
+%    \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 |\ekv at tmp| to use
+% |\ProvidesFile|, else this will expand to a |\relax| and do no harm.
+%    \begin{macrocode}
+\csname ekv at tmp\endcsname
+%    \end{macrocode}
+%
+% Store the category code of |@| to later be able to reset it and change it to
+% 11.
+%    \begin{macrocode}
+\expandafter\chardef\csname ekv at tmp\endcsname=\catcode`\@
+\catcode`\@=11
+%    \end{macrocode}
+% |\ekv at tmp| might later be reused to gobble any prefixes which might be
+% provided to |\ekvdef| and similar in case the names are invalid, we just
+% temporarily use it here as means to store the current category code of |@| to
+% restore it at the end of the file, we never care for the actual definition of
+% it.
+%
+% \begin{macro}
+%   {
+%     \@gobble,\@firstofone,\@firstoftwo,\@secondoftwo,
+%     \ekv at gobbletostop,\ekv at fi@gobble,\ekv at fi@secondoftwo
+%   }
+% Since branching tests are often more versatile than |\if...\else...\fi|
+% constructs, we define helpers that are branching pretty fast. Also here are
+% some other utility functions that just grab some tokens. The ones that are
+% also contained in \LaTeX\ don't use the |ekv| prefix.
+%    \begin{macrocode}
+\long\def\@gobble#1{}
+\long\def\@firstofone#1{#1}
+\long\def\@firstoftwo#1#2{#1}
+\long\def\@secondoftwo#1#2{#2}
+\long\def\ekv at gobbletostop#1\ekv at stop{}
+\long\def\ekv at fi@gobble\fi\@firstofone#1{\fi}
+\long\def\ekv at fi@secondoftwo\fi\@firstoftwo#1#2{\fi#2}
+%    \end{macrocode}
+% \end{macro}
+% As you can see |\ekv at gobbletostop| uses a special marker |\ekv at stop|. The
+% package will use three such markers, the one you've seen already, |\ekv at mark|
+% and |\ekv at nil|. Contrarily to how for instance \pkg{expl3} does things, we
+% don't define them, as we don't need them to have an actual meaning. This has
+% the advantage that if they somehow get expanded -- which should never happen
+% if things work out -- they'll throw an error directly.
+%
+% \begin{macro}
+%   {
+%     \ekv at ifempty,\ekv at ifempty@,\ekv at ifempty@true,\ekv at ifempty@false,
+%     \ekv at ifempty@true at F
+%   }
+% We can test for a lot of things building on an if-empty test, so lets define a
+% really fast one. Since some tests might have reversed logic (true if something
+% is not empty) we also set up macros for the reversed branches.
+%    \begin{macrocode}
+\long\def\ekv at ifempty#1%
+  {%
+    \ekv at ifempty@\ekv at ifempty@A#1\ekv at ifempty@B\ekv at ifempty@true
+      \ekv at ifempty@A\ekv at ifempty@B\@secondoftwo
+  }
+\long\def\ekv at ifempty@#1\ekv at ifempty@A\ekv at ifempty@B{}
+\long\def\ekv at ifempty@true\ekv at ifempty@A\ekv at ifempty@B\@secondoftwo#1#2{#1}
+\long\def\ekv at ifempty@false\ekv at ifempty@A\ekv at ifempty@B\@firstoftwo#1#2{#2}
+\long\def\ekv at ifempty@true at F\ekv at ifempty@A\ekv at ifempty@B\@firstofone#1{}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekv at ifblank@}
+% The obvious test that can be based on an if-empty is if-blank, meaning a test
+% checking whether the argument is empty or consists only of spaces. Our version
+% here will be tweaked a bit, as we want to check this, but with one leading
+% |\ekv at mark| token that is to be ignored.
+%    \begin{macrocode}
+\long\def\ekv at ifblank@\ekv at mark#1{\ekv at ifempty@\ekv at ifempty@A}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekv at ifdefined}
+% We'll need to check whether something is defined quite frequently, so why not
+% define a macro that does this. The following test is expandable, slower than
+% the typical expandable test for undefined control sequences, but faster for
+% defined ones. Since we want to be as fast as possible for correct input, this
+% is to be preferred.
+%    \begin{macrocode}
+\def\ekv at ifdefined#1%
+  {%
+    \expandafter
+    \ifx\csname\ifcsname #1\endcsname #1\else relax\fi\endcsname\relax
+      \ekv at fi@secondoftwo
+    \fi
+    \@firstoftwo
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}
+%   {
+%     \ekv at ifdefined@pair,\ekv at ifdefined@pair@,\ekv at ifdefined@key,
+%     \ekv at ifdefined@key@
+%   }
+% Since we can save some time if we only have to create the control sequence
+% once when we know beforehand how we want to use it, we build some other macros
+% for those cases (which we'll have quite often, once per key usage).
+%    \begin{macrocode}
+\def\ekv at ifdefined@pair#1#2%
+  {%
+    \expandafter\ekv at ifdefined@pair@
+      \csname
+        \ifcsname #1{#2}\endcsname
+          #1{#2}%
+        \else
+          relax%
+        \fi
+      \endcsname
+  }
+\def\ekv at ifdefined@pair@#1%
+  {%
+    \ifx#1\relax
+      \ekv at fi@secondoftwo
+    \fi
+    \@firstoftwo
+    {\ekv at set@pair@#1\ekv at mark}%
+  }
+\def\ekv at ifdefined@key#1#2%
+  {%
+    \expandafter\ekv at ifdefined@key@
+      \csname
+        \ifcsname #1{#2}N\endcsname
+          #1{#2}N%
+        \else
+          relax%
+        \fi
+      \endcsname
+  }
+\def\ekv at ifdefined@key@#1%
+  {%
+    \ifx#1\relax
+      \ekv at fi@secondoftwo
+    \fi
+    \@firstoftwo#1%
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekv at name,\ekv at name@set,\ekv at name@key}
+% The keys will all follow the same naming scheme, so we define it here.
+%    \begin{macrocode}
+\def\ekv at name#1#2{\ekv at name@set{#1}\ekv at name@key{#2}}
+\def\ekv at name@set#1{ekv#1(}
+\def\ekv at name@key#1{#1)}
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at checkvalid}
+% We place some restrictions on the allowed names, though, namely sets and
+% keys are not allowed to be empty -- blanks are fine (meaning \mbox{set-
+% or} key-names consisting of spaces).
+%    \begin{macrocode}
+\protected\def\ekv at checkvalid#1#2%
+  {%
+    \ekv at ifempty{#1}%
+      {%
+        \def\ekv at tmp{}%
+        \errmessage{expkv Error: empty set name not allowed}%
+      }%
+      {%
+        \ekv at ifempty{#2}%
+          {%
+            \def\ekv at tmp{}%
+            \errmessage{expkv Error: empty key name not allowed}%
+          }%
+          \@secondoftwo
+      }%
+    \@gobble
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekvifdefined,\ekvifdefinedNoVal}
+% And provide user-level macros to test whether a key is defined.
+%    \begin{macrocode}
+\def\ekvifdefined#1#2{\ekv at ifdefined{\ekv at name{#1}{#2}}}
+\def\ekvifdefinedNoVal#1#2{\ekv at ifdefined{\ekv at name{#1}{#2}N}}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+%   {
+%     \ekvdef,\ekvdefNoVal,\ekvlet,\ekvletNoVal,\ekvletkv,\ekvletkvNoVal,
+%     \ekv at defset
+%   }
+% Set up the key defining macros |\ekvdef| etc.
+%    \begin{macrocode}
+\protected\long\def\ekvdef#1#2#3%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\def\csname\ekv at name{#1}{#2}\endcsname##1{#3}%
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\long\def\ekvdefNoVal#1#2#3%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\def\csname\ekv at name{#1}{#2}N\endcsname{#3}%
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\def\ekvlet#1#2#3%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\let\csname\ekv at name{#1}{#2}\endcsname#3%
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\def\ekvletNoVal#1#2#3%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\let\csname\ekv at name{#1}{#2}N\endcsname#3%
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\def\ekvletkv#1#2#3#4%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\let\csname\ekv at name{#1}{#2}\expandafter\endcsname
+        \csname\ekv at name{#3}{#4}\endcsname
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\def\ekvletkvNoVal#1#2#3#4%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\let\csname\ekv at name{#1}{#2}N\expandafter\endcsname
+        \csname\ekv at name{#3}{#4}N\endcsname
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\def\ekv at defset#1%
+  {%
+    \expandafter\edef\csname\ekv at name@set{#1}\endcsname##1%
+      {\ekv at name@set{#1}\ekv at name@key{##1}}%
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvset}
+% Set up |\ekvset|, which should not be affected by active commas and equal
+% signs. The equal signs are a bit harder to cope with and we'll do that later,
+% but replacing the active commas with commas of category other can be done
+% beforehand. That's why we define |\ekvset| here with a temporary meaning just
+% to set up the things with two different category codes. |#1| will be a
+% \texttt{,\textsubscript{13}} and |#2| will be a \texttt{=\textsubscript{13}}.
+%    \begin{macrocode}
+\def\ekvset#1#2{%
+\endgroup
+\long\def\ekvset##1##2%
+  {%
+    \expandafter\ekv at set\csname\ekv at name@set{##1}\endcsname
+      \ekv at mark##2#1\ekv at stop#1{}%
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at set}
+% |\ekv at set| will split the \kv\ list at active commas. Then it has to check
+% whether there were unprotected other commas and resplit there.
+%    \begin{macrocode}
+\long\def\ekv at set##1##2#1%
+  {%
+%    \end{macrocode}
+% Test whether we're at the end, if so invoke |\ekv at endset|,
+%    \begin{macrocode}
+    \ekv at ifstop##2\ekv at endset\ekv at mark\ekv at stop
+%    \end{macrocode}
+% else go on with other commas,
+%    \begin{macrocode}
+    \ekv at set@other##1##2,\ekv at stop,%
+%    \end{macrocode}
+% and get the next active comma delimited \kv\ pair.
+%    \begin{macrocode}
+    \ekv at set##1\ekv at mark
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at endset}
+% |\ekv at endset| is a hungry little macro. It will eat everything that remains
+% of |\ekv at set| and unbrace the sneaked stuff.
+%    \begin{macrocode}
+\long\def\ekv at endset
+    \ekv at mark\ekv at stop\ekv at set@other##1,\ekv at stop,\ekv at set##2\ekv at mark
+    ##3%
+  {##3}
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at set@other}
+% The macro |\ekv at set@other| is guaranteed to get only single \kv\ pairs.
+% So here we need to split at equal signs. If there is no equal sign, we need to
+% test whether we're done and if not this is a |NoVal| key.
+%    \begin{macrocode}
+\long\def\ekv at set@other##1##2,%
+  {%
+    \ekv at ifblank@##2\ekv at nil\ekv at ifempty@B\ekv at ifempty@true at F
+      \ekv at ifempty@A\ekv at ifempty@B\@firstofone
+      {%
+        \ekv at ifhas@eq at other##2=\ekv at ifempty@B\ekv at ifempty@false
+          \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+          {\ekv at set@eq at other##1##2\ekv at stop}%
+          {%
+            \ekv at ifhas@eq at active##2#2\ekv at ifempty@B\ekv at ifempty@false
+              \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+              {\ekv at set@eq at active##1##2\ekv at stop}%
+              {%
+                \ekv at ifstop##2\ekv at endset@other\ekv at mark\ekv at stop
+                \ekv at strip{##2}\ekv at set@key##1%
+              }%
+          }%
+      }%
+    \ekv at set@other##1\ekv at mark%
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at set@eq at other}
+% |\ekv at set@eq at other| might not be the correct break point, there might be an
+% active equal sign in the currently parsed key-name. If so, we have to resplit.
+%    \begin{macrocode}
+\long\def\ekv at set@eq at other##1##2=%
+  {%
+    \ekv at ifhas@eq at active##2#2\ekv at ifempty@B\ekv at ifempty@false
+      \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+      {\ekv at set@eq at active##1##2=}%
+      {\ekv at strip{##2}\ekv at set@pair##1}%
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at set@eq at active}
+% We need to handle the active equal signs.
+%    \begin{macrocode}
+\long\def\ekv at set@eq at active##1##2#2%
+  {%
+    \ekv at strip{##2}\ekv at set@pair##1%
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at ifhas@eq at other,\ekv at ifhas@eq at active,\ekv at endset@other}
+% And we have to set up the testing macros for our equal signs and
+% |\ekv at endset@other|.
+%    \begin{macrocode}
+\long\def\ekv at ifhas@eq at other\ekv at mark##1={\ekv at ifempty@\ekv at ifempty@A}
+\long\def\ekv at ifhas@eq at active\ekv at mark##1#2{\ekv at ifempty@\ekv at ifempty@A}
+\long\def\ekv at endset@other
+    \ekv at mark\ekv at stop\ekv at strip##1\ekv at set@key##2%
+    \ekv at set@other##3\ekv at mark
+  {}
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak}
+% Provide macros that can completely stop the parsing of |\ekvset|, who knows
+% what it'll be useful for.
+%    \begin{macrocode}
+\long\def\ekvbreak##1##2\ekv at stop#1##3{##1}
+\long\def\ekvbreakPreSneak ##1##2\ekv at stop#1##3{##1##3}
+\long\def\ekvbreakPostSneak##1##2\ekv at stop#1##3{##3##1}
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekvsneak,\ekvsneakPre}
+% One last thing we want to do for |\ekvset| is to provide macros that just
+% smuggle stuff after |\ekvset|'s effects.
+%    \begin{macrocode}
+\long\def\ekvsneak##1##2\ekv at stop#1##3%
+  {%
+    ##2\ekv at stop#1{##3##1}%
+  }
+\long\def\ekvsneakPre##1##2\ekv at stop#1##3%
+  {%
+    ##2\ekv at stop#1{##1##3}%
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvparse}
+% Additionally to the |\ekvset| macro we also want to provide an |\ekvparse|
+% macro, that has the same scope as |\keyval_parse:NNn| from \pkg{expl3}.
+% This is pretty analogue to the |\ekvset| implementation, we just put an
+% |\unexpanded| here and there instead of other macros to stop the |\expanded|
+% on our output.
+%    \begin{macrocode}
+\long\def\ekvparse##1##2##3%
+  {%
+    \expanded{\ekv at parse##1##2\ekv at mark##3#1\ekv at stop#1}%
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at parse}
+%    \begin{macrocode}
+\long\def\ekv at parse##1##2##3#1%
+  {%
+    \ekv at ifstop##3\ekv at endparse\ekv at mark\ekv at stop
+    \ekv at parse@other##1##2##3,\ekv at stop,%
+    \ekv at parse##1##2\ekv at mark
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at endparse}
+%    \begin{macrocode}
+\long\def\ekv at endparse
+    \ekv at mark\ekv at stop\ekv at parse@other##1,\ekv at stop,\ekv at parse##2\ekv at mark
+  {}
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at parse@other}
+%    \begin{macrocode}
+\long\def\ekv at parse@other##1##2##3,%
+  {%
+    \ekv at ifblank@##3\ekv at nil\ekv at ifempty@B\ekv at ifempty@true at F
+      \ekv at ifempty@A\ekv at ifempty@B\@firstofone
+      {%
+        \ekv at ifhas@eq at other##3=\ekv at ifempty@B\ekv at ifempty@false
+          \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+          {\unexpanded{##2}\ekv at parse@eq at other##3\ekv at stop}%
+          {%
+            \ekv at ifhas@eq at active##3#2\ekv at ifempty@B\ekv at ifempty@false
+              \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+              {\unexpanded{##2}\ekv at parse@eq at active##3\ekv at stop}%
+              {%
+                \ekv at ifstop##3\ekv at endparse@other\ekv at mark\ekv at stop
+                \unexpanded{##1}{\ekv at strip{##3}\unexpanded}%
+              }%
+          }%
+      }%
+    \ekv at parse@other##1##2\ekv at mark
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at parse@eq at other}
+%    \begin{macrocode}
+\long\def\ekv at parse@eq at other##1=%
+  {%
+    \ekv at ifhas@eq at active##1#2\ekv at ifempty@B\ekv at ifempty@false
+      \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+      {\ekv at parse@eq at active##1=}%
+      {{\ekv at strip{##1}\unexpanded}\ekv at parse@pair\ekv at mark}%
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at parse@eq at active}
+%    \begin{macrocode}
+\long\def\ekv at parse@eq at active##1#2%
+  {%
+    {\ekv at strip{##1}\unexpanded}\ekv at parse@pair\ekv at mark
+  }
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at endparse@other}
+%    \begin{macrocode}
+\long\def\ekv at endparse@other
+    \ekv at mark\ekv at stop\unexpanded##1%
+    \ekv at parse@other##2\ekv at mark
+  {}
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}{\ekv at parse@pair}
+%    \begin{macrocode}
+\long\def\ekv at parse@pair##1\ekv at stop
+  {%
+    {\ekv at strip{##1}\unexpanded}%
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% Finally really setting things up with |\ekvset|'s temporary meaning:
+%    \begin{macrocode}
+}
+\begingroup
+\catcode`\,=13
+\catcode`\==13
+\ekvset,=
+%    \end{macrocode}
+%
+% \begin{macro}{\ekv at ifstop}
+% The |\ekv at ifstop| test works similar to our if-empty test, but instead of
+% using tokens which are used nowhere else (|\ekv at ifempty@A| and
+% |\ekv at ifempty@B|) we use |\ekv at mark| and |\ekv at stop|.
+%    \begin{macrocode}
+\long\def\ekv at ifstop#1\ekv at mark\ekv at stop{}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekv at set@pair,\ekv at set@pair@}
+% |\ekv at set@pair| needs to split the argument at the |=| sign and check
+% whether the key is defined.
+%    \begin{macrocode}
+\long\def\ekv at set@pair#1#2%
+  {%
+    \ekv at ifdefined@pair#2{#1}%
+      {%
+        \ekv at ifdefined{#2{#1}N}%
+          \ekv at err@noarg
+          \ekv at err@unknown
+          #2{#1}%
+        \ekv at gobbletostop
+      }%
+  }
+\long\def\ekv at set@pair@#1#2\ekv at stop
+  {%
+    \ekv at strip{#2}#1%
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekv at set@key}
+%    \begin{macrocode}
+\long\def\ekv at set@key#1#2%
+  {%
+    \ekv at ifdefined@key#2{#1}%
+      {%
+        \ekv at ifdefined{#2{#1}}%
+          \ekv at err@reqval
+          \ekv at err@unknown
+          #2{#1}%
+      }%
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekv at err,\ekv at err@}
+% Since |\ekvset| is fully expandable as long as the code of the keys is (which
+% is unlikely) we want to somehow throw expandable errors, in our case via
+% undefined control sequences.
+%    \begin{macrocode}
+\begingroup
+\edef\ekv at err
+  {%
+    \endgroup
+    \unexpanded{\long\def\ekv at err}##1%
+      {%
+        \unexpanded{\expandafter\ekv at err@\@firstofone}%
+        {\expandafter\noexpand\csname ! expkv Error:\endcsname ##1.}%
+        \unexpanded{\ekv at stop}%
+      }%
+  }
+\ekv at err
+\def\ekv at err@{\expandafter\ekv at gobbletostop}
+%    \end{macrocode}
+% \end{macro}
+% \begin{macro}
+%   {
+%     \ekv at err@common,\ekv at err@common@,
+%     \ekv at err@unknown,\ekv at err@noarg,\ekv at err@reqval
+%   }
+% Now we can use |\ekv at err| to set up some error messages so that we can later
+% use those instead of the full strings.
+%    \begin{macrocode}
+\long\def\ekv at err@common #1#2{\expandafter\ekv at err@common@\string#2{#1}}
+\long\def\ekv at err@common@#1#2#3#4#5(#6#7{\ekv at err{#6 (`#7', set `#5')}}
+\long\def\ekv at err@unknown#1#2{\ekv at err@common{unknown key}#1{#2}}
+\long\def\ekv at err@noarg  #1#2{\ekv at err@common{value forbidden}#1{#2}}
+\long\def\ekv at err@reqval #1#2{\ekv at err@common{value required}#1{#2}}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekv at strip,\ekv at strip@a,\ekv at strip@b,\ekv at strip@c}
+% Finally we borrow some ideas of \pkg{expl3}'s \pkg{l3tl} to strip spaces
+% from keys and values. This |\ekv at strip| also strips one level of outer braces
+% \emph{after} stripping spaces, so an input of | {abc} | becomes |abc| after
+% stripping. It should be used with |#1| prefixed by |\ekv at mark|.
+%    \begin{macrocode}
+\def\ekv at strip#1%
+  {%
+    \long\def\ekv at strip##1%
+      {%
+        \ekv at strip@a
+          ##1%
+          \ekv at nil
+          \ekv at mark#1%
+          #1\ekv at nil{}%
+        \ekv at stop
+      }%
+    \long\def\ekv at strip@a##1\ekv at mark#1##2\ekv at nil##3%
+      {%
+        \ekv at strip@b##3##1##2\ekv at nil
+      }%
+    \long\def\ekv at strip@b##1#1\ekv at nil
+      {%
+        \ekv at strip@c##1\ekv at nil
+      }%
+    \long\def\ekv at strip@c\ekv at mark##1\ekv at nil##2\ekv at stop##3%
+      {%
+        ##3{##1}%
+      }%
+  }
+\ekv at strip{ }
+%    \end{macrocode}
+% \end{macro}
+%
+% Now everything that's left is to reset the category code of |@|.
+%    \begin{macrocode}
+\catcode`\@=\ekv at tmp
+%    \end{macrocode}
+%
+% \gobbledocstriptag
+%</tex>
+%^^A=<<
+%
+%^^A=<<
+%
+% \end{implementation}^^A=<<
+%
+% \clearpage
+% \PrintIndex
+%
+\endinput
+%
+^^A vim: ft=tex fdm=marker fmr=>>=,=<<


Property changes on: trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex
===================================================================
--- trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex	2020-01-23 21:56:50 UTC (rev 53516)
@@ -0,0 +1,380 @@
+%%
+%% This is file `expkv.tex',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% expkv.dtx  (with options: `tex')
+%% 
+%% --------------------------------------------------------------
+%% expkv -- an expandable key=val implementation
+%% E-mail: jspratte at yahoo.de
+%% Released under the LaTeX Project Public License v1.3c or later
+%% See http://www.latex-project.org/lppl.txt
+%% --------------------------------------------------------------
+%% 
+%% Copyright (C) 2020 Jonathan P. Spratte
+%% 
+%% This  work may be  distributed and/or  modified under  the conditions  of the
+%% LaTeX Project Public License (LPPL),  either version 1.3c  of this license or
+%% (at your option) any later version.  The latest version of this license is in
+%% the file:
+%% 
+%%   http://www.latex-project.org/lppl.txt
+%% 
+%% This work is "maintained" (as per LPPL maintenance status) by
+%%   Jonathan P. Spratte.
+%% 
+%% This work consists of the file  expkv.dtx
+%% and the derived files           expkv.pdf
+%%                                 expkv.sty
+%%                                 expkv.tex
+%% 
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname numexpr\endcsname\relax
+  \errmessage{expkv requires e-TeX}
+  \expandafter\endinput
+\fi
+\expandafter\ifx\csname ekvVersion\endcsname\relax
+\else
+  \expandafter\endinput
+\fi
+\def\ekvVersion{0.3}
+\def\ekvDate{2020-01-22}
+\csname ekv at tmp\endcsname
+\expandafter\chardef\csname ekv at tmp\endcsname=\catcode`\@
+\catcode`\@=11
+\long\def\@gobble#1{}
+\long\def\@firstofone#1{#1}
+\long\def\@firstoftwo#1#2{#1}
+\long\def\@secondoftwo#1#2{#2}
+\long\def\ekv at gobbletostop#1\ekv at stop{}
+\long\def\ekv at fi@gobble\fi\@firstofone#1{\fi}
+\long\def\ekv at fi@secondoftwo\fi\@firstoftwo#1#2{\fi#2}
+\long\def\ekv at ifempty#1%
+  {%
+    \ekv at ifempty@\ekv at ifempty@A#1\ekv at ifempty@B\ekv at ifempty@true
+      \ekv at ifempty@A\ekv at ifempty@B\@secondoftwo
+  }
+\long\def\ekv at ifempty@#1\ekv at ifempty@A\ekv at ifempty@B{}
+\long\def\ekv at ifempty@true\ekv at ifempty@A\ekv at ifempty@B\@secondoftwo#1#2{#1}
+\long\def\ekv at ifempty@false\ekv at ifempty@A\ekv at ifempty@B\@firstoftwo#1#2{#2}
+\long\def\ekv at ifempty@true at F\ekv at ifempty@A\ekv at ifempty@B\@firstofone#1{}
+\long\def\ekv at ifblank@\ekv at mark#1{\ekv at ifempty@\ekv at ifempty@A}
+\def\ekv at ifdefined#1%
+  {%
+    \expandafter
+    \ifx\csname\ifcsname #1\endcsname #1\else relax\fi\endcsname\relax
+      \ekv at fi@secondoftwo
+    \fi
+    \@firstoftwo
+  }
+\def\ekv at ifdefined@pair#1#2%
+  {%
+    \expandafter\ekv at ifdefined@pair@
+      \csname
+        \ifcsname #1{#2}\endcsname
+          #1{#2}%
+        \else
+          relax%
+        \fi
+      \endcsname
+  }
+\def\ekv at ifdefined@pair@#1%
+  {%
+    \ifx#1\relax
+      \ekv at fi@secondoftwo
+    \fi
+    \@firstoftwo
+    {\ekv at set@pair@#1\ekv at mark}%
+  }
+\def\ekv at ifdefined@key#1#2%
+  {%
+    \expandafter\ekv at ifdefined@key@
+      \csname
+        \ifcsname #1{#2}N\endcsname
+          #1{#2}N%
+        \else
+          relax%
+        \fi
+      \endcsname
+  }
+\def\ekv at ifdefined@key@#1%
+  {%
+    \ifx#1\relax
+      \ekv at fi@secondoftwo
+    \fi
+    \@firstoftwo#1%
+  }
+\def\ekv at name#1#2{\ekv at name@set{#1}\ekv at name@key{#2}}
+\def\ekv at name@set#1{ekv#1(}
+\def\ekv at name@key#1{#1)}
+\protected\def\ekv at checkvalid#1#2%
+  {%
+    \ekv at ifempty{#1}%
+      {%
+        \def\ekv at tmp{}%
+        \errmessage{expkv Error: empty set name not allowed}%
+      }%
+      {%
+        \ekv at ifempty{#2}%
+          {%
+            \def\ekv at tmp{}%
+            \errmessage{expkv Error: empty key name not allowed}%
+          }%
+          \@secondoftwo
+      }%
+    \@gobble
+  }
+\def\ekvifdefined#1#2{\ekv at ifdefined{\ekv at name{#1}{#2}}}
+\def\ekvifdefinedNoVal#1#2{\ekv at ifdefined{\ekv at name{#1}{#2}N}}
+\protected\long\def\ekvdef#1#2#3%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\def\csname\ekv at name{#1}{#2}\endcsname##1{#3}%
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\long\def\ekvdefNoVal#1#2#3%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\def\csname\ekv at name{#1}{#2}N\endcsname{#3}%
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\def\ekvlet#1#2#3%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\let\csname\ekv at name{#1}{#2}\endcsname#3%
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\def\ekvletNoVal#1#2#3%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\let\csname\ekv at name{#1}{#2}N\endcsname#3%
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\def\ekvletkv#1#2#3#4%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\let\csname\ekv at name{#1}{#2}\expandafter\endcsname
+        \csname\ekv at name{#3}{#4}\endcsname
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\def\ekvletkvNoVal#1#2#3#4%
+  {%
+    \ekv at checkvalid{#1}{#2}%
+      {%
+        \expandafter\let\csname\ekv at name{#1}{#2}N\expandafter\endcsname
+        \csname\ekv at name{#3}{#4}N\endcsname
+        \ekv at defset{#1}%
+      }%
+  }
+\protected\def\ekv at defset#1%
+  {%
+    \expandafter\edef\csname\ekv at name@set{#1}\endcsname##1%
+      {\ekv at name@set{#1}\ekv at name@key{##1}}%
+  }
+\def\ekvset#1#2{%
+\endgroup
+\long\def\ekvset##1##2%
+  {%
+    \expandafter\ekv at set\csname\ekv at name@set{##1}\endcsname
+      \ekv at mark##2#1\ekv at stop#1{}%
+  }
+\long\def\ekv at set##1##2#1%
+  {%
+    \ekv at ifstop##2\ekv at endset\ekv at mark\ekv at stop
+    \ekv at set@other##1##2,\ekv at stop,%
+    \ekv at set##1\ekv at mark
+  }
+\long\def\ekv at endset
+    \ekv at mark\ekv at stop\ekv at set@other##1,\ekv at stop,\ekv at set##2\ekv at mark
+    ##3%
+  {##3}
+\long\def\ekv at set@other##1##2,%
+  {%
+    \ekv at ifblank@##2\ekv at nil\ekv at ifempty@B\ekv at ifempty@true at F
+      \ekv at ifempty@A\ekv at ifempty@B\@firstofone
+      {%
+        \ekv at ifhas@eq at other##2=\ekv at ifempty@B\ekv at ifempty@false
+          \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+          {\ekv at set@eq at other##1##2\ekv at stop}%
+          {%
+            \ekv at ifhas@eq at active##2#2\ekv at ifempty@B\ekv at ifempty@false
+              \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+              {\ekv at set@eq at active##1##2\ekv at stop}%
+              {%
+                \ekv at ifstop##2\ekv at endset@other\ekv at mark\ekv at stop
+                \ekv at strip{##2}\ekv at set@key##1%
+              }%
+          }%
+      }%
+    \ekv at set@other##1\ekv at mark%
+  }
+\long\def\ekv at set@eq at other##1##2=%
+  {%
+    \ekv at ifhas@eq at active##2#2\ekv at ifempty@B\ekv at ifempty@false
+      \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+      {\ekv at set@eq at active##1##2=}%
+      {\ekv at strip{##2}\ekv at set@pair##1}%
+  }
+\long\def\ekv at set@eq at active##1##2#2%
+  {%
+    \ekv at strip{##2}\ekv at set@pair##1%
+  }
+\long\def\ekv at ifhas@eq at other\ekv at mark##1={\ekv at ifempty@\ekv at ifempty@A}
+\long\def\ekv at ifhas@eq at active\ekv at mark##1#2{\ekv at ifempty@\ekv at ifempty@A}
+\long\def\ekv at endset@other
+    \ekv at mark\ekv at stop\ekv at strip##1\ekv at set@key##2%
+    \ekv at set@other##3\ekv at mark
+  {}
+\long\def\ekvbreak##1##2\ekv at stop#1##3{##1}
+\long\def\ekvbreakPreSneak ##1##2\ekv at stop#1##3{##1##3}
+\long\def\ekvbreakPostSneak##1##2\ekv at stop#1##3{##3##1}
+\long\def\ekvsneak##1##2\ekv at stop#1##3%
+  {%
+    ##2\ekv at stop#1{##3##1}%
+  }
+\long\def\ekvsneakPre##1##2\ekv at stop#1##3%
+  {%
+    ##2\ekv at stop#1{##1##3}%
+  }
+\long\def\ekvparse##1##2##3%
+  {%
+    \expanded{\ekv at parse##1##2\ekv at mark##3#1\ekv at stop#1}%
+  }
+\long\def\ekv at parse##1##2##3#1%
+  {%
+    \ekv at ifstop##3\ekv at endparse\ekv at mark\ekv at stop
+    \ekv at parse@other##1##2##3,\ekv at stop,%
+    \ekv at parse##1##2\ekv at mark
+  }
+\long\def\ekv at endparse
+    \ekv at mark\ekv at stop\ekv at parse@other##1,\ekv at stop,\ekv at parse##2\ekv at mark
+  {}
+\long\def\ekv at parse@other##1##2##3,%
+  {%
+    \ekv at ifblank@##3\ekv at nil\ekv at ifempty@B\ekv at ifempty@true at F
+      \ekv at ifempty@A\ekv at ifempty@B\@firstofone
+      {%
+        \ekv at ifhas@eq at other##3=\ekv at ifempty@B\ekv at ifempty@false
+          \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+          {\unexpanded{##2}\ekv at parse@eq at other##3\ekv at stop}%
+          {%
+            \ekv at ifhas@eq at active##3#2\ekv at ifempty@B\ekv at ifempty@false
+              \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+              {\unexpanded{##2}\ekv at parse@eq at active##3\ekv at stop}%
+              {%
+                \ekv at ifstop##3\ekv at endparse@other\ekv at mark\ekv at stop
+                \unexpanded{##1}{\ekv at strip{##3}\unexpanded}%
+              }%
+          }%
+      }%
+    \ekv at parse@other##1##2\ekv at mark
+  }
+\long\def\ekv at parse@eq at other##1=%
+  {%
+    \ekv at ifhas@eq at active##1#2\ekv at ifempty@B\ekv at ifempty@false
+      \ekv at ifempty@A\ekv at ifempty@B\@firstoftwo
+      {\ekv at parse@eq at active##1=}%
+      {{\ekv at strip{##1}\unexpanded}\ekv at parse@pair\ekv at mark}%
+  }
+\long\def\ekv at parse@eq at active##1#2%
+  {%
+    {\ekv at strip{##1}\unexpanded}\ekv at parse@pair\ekv at mark
+  }
+\long\def\ekv at endparse@other
+    \ekv at mark\ekv at stop\unexpanded##1%
+    \ekv at parse@other##2\ekv at mark
+  {}
+\long\def\ekv at parse@pair##1\ekv at stop
+  {%
+    {\ekv at strip{##1}\unexpanded}%
+  }
+}
+\begingroup
+\catcode`\,=13
+\catcode`\==13
+\ekvset,=
+\long\def\ekv at ifstop#1\ekv at mark\ekv at stop{}
+\long\def\ekv at set@pair#1#2%
+  {%
+    \ekv at ifdefined@pair#2{#1}%
+      {%
+        \ekv at ifdefined{#2{#1}N}%
+          \ekv at err@noarg
+          \ekv at err@unknown
+          #2{#1}%
+        \ekv at gobbletostop
+      }%
+  }
+\long\def\ekv at set@pair@#1#2\ekv at stop
+  {%
+    \ekv at strip{#2}#1%
+  }
+\long\def\ekv at set@key#1#2%
+  {%
+    \ekv at ifdefined@key#2{#1}%
+      {%
+        \ekv at ifdefined{#2{#1}}%
+          \ekv at err@reqval
+          \ekv at err@unknown
+          #2{#1}%
+      }%
+  }
+\begingroup
+\edef\ekv at err
+  {%
+    \endgroup
+    \unexpanded{\long\def\ekv at err}##1%
+      {%
+        \unexpanded{\expandafter\ekv at err@\@firstofone}%
+        {\expandafter\noexpand\csname ! expkv Error:\endcsname ##1.}%
+        \unexpanded{\ekv at stop}%
+      }%
+  }
+\ekv at err
+\def\ekv at err@{\expandafter\ekv at gobbletostop}
+\long\def\ekv at err@common #1#2{\expandafter\ekv at err@common@\string#2{#1}}
+\long\def\ekv at err@common@#1#2#3#4#5(#6#7{\ekv at err{#6 (`#7', set `#5')}}
+\long\def\ekv at err@unknown#1#2{\ekv at err@common{unknown key}#1{#2}}
+\long\def\ekv at err@noarg  #1#2{\ekv at err@common{value forbidden}#1{#2}}
+\long\def\ekv at err@reqval #1#2{\ekv at err@common{value required}#1{#2}}
+\def\ekv at strip#1%
+  {%
+    \long\def\ekv at strip##1%
+      {%
+        \ekv at strip@a
+          ##1%
+          \ekv at nil
+          \ekv at mark#1%
+          #1\ekv at nil{}%
+        \ekv at stop
+      }%
+    \long\def\ekv at strip@a##1\ekv at mark#1##2\ekv at nil##3%
+      {%
+        \ekv at strip@b##3##1##2\ekv at nil
+      }%
+    \long\def\ekv at strip@b##1#1\ekv at nil
+      {%
+        \ekv at strip@c##1\ekv at nil
+      }%
+    \long\def\ekv at strip@c\ekv at mark##1\ekv at nil##2\ekv at stop##3%
+      {%
+        ##3{##1}%
+      }%
+  }
+\ekv at strip{ }
+\catcode`\@=\ekv at tmp
+%% 
+%%
+%% End of file `expkv.tex'.


Property changes on: trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/tex/latex/expkv/expkv.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/expkv/expkv.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/expkv/expkv.sty	2020-01-23 21:56:50 UTC (rev 53516)
@@ -0,0 +1,43 @@
+%%
+%% This is file `expkv.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% expkv.dtx  (with options: `pkg')
+%% 
+%% --------------------------------------------------------------
+%% expkv -- an expandable key=val implementation
+%% E-mail: jspratte at yahoo.de
+%% Released under the LaTeX Project Public License v1.3c or later
+%% See http://www.latex-project.org/lppl.txt
+%% --------------------------------------------------------------
+%% 
+%% Copyright (C) 2020 Jonathan P. Spratte
+%% 
+%% This  work may be  distributed and/or  modified under  the conditions  of the
+%% LaTeX Project Public License (LPPL),  either version 1.3c  of this license or
+%% (at your option) any later version.  The latest version of this license is in
+%% the file:
+%% 
+%%   http://www.latex-project.org/lppl.txt
+%% 
+%% This work is "maintained" (as per LPPL maintenance status) by
+%%   Jonathan P. Spratte.
+%% 
+%% This work consists of the file  expkv.dtx
+%% and the derived files           expkv.pdf
+%%                                 expkv.sty
+%%                                 expkv.tex
+%% 
+\def\ekv at tmp
+  {%
+    \ProvidesFile{expkv.tex}%
+      [\ekvDate\space v\ekvVersion\space an expandable key=val implementation]%
+  }
+\input{expkv.tex}
+\ProvidesPackage{expkv}%
+  [\ekvDate\space v\ekvVersion\space an expandable key=val implementation]
+%% 
+%%
+%% End of file `expkv.sty'.


Property changes on: trunk/Master/texmf-dist/tex/latex/expkv/expkv.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/tlpkg/bin/tlpkg-ctan-check
===================================================================
--- trunk/Master/tlpkg/bin/tlpkg-ctan-check	2020-01-23 21:55:34 UTC (rev 53515)
+++ trunk/Master/tlpkg/bin/tlpkg-ctan-check	2020-01-23 21:56:50 UTC (rev 53516)
@@ -278,7 +278,7 @@
     exam exam-n exam-randomizechoices examdesign example examplep
     exceltex excludeonly exercise exercisebank exercisepoints exercises
     exframe exp-testopt
-    expdlist expex export expressg exsheets exsol extarrows exteps
+    expdlist expex expkv export expressg exsheets exsol extarrows exteps
     extpfeil extract extsizes
   facsimile factura facture facture-belge-simple-sans-tva faktor
     fancybox fancyhandout fancyhdr fancyhdr-it fancylabel fancynum fancypar

Modified: trunk/Master/tlpkg/tlpsrc/collection-plaingeneric.tlpsrc
===================================================================
--- trunk/Master/tlpkg/tlpsrc/collection-plaingeneric.tlpsrc	2020-01-23 21:55:34 UTC (rev 53515)
+++ trunk/Master/tlpkg/tlpsrc/collection-plaingeneric.tlpsrc	2020-01-23 21:56:50 UTC (rev 53516)
@@ -26,6 +26,7 @@
 depend epigram
 depend epsf
 depend epsf-dvipdfmx
+depend expkv
 depend fenixpar
 depend figflow
 depend fixpdfmag

Added: trunk/Master/tlpkg/tlpsrc/expkv.tlpsrc
===================================================================


More information about the tex-live-commits mailing list