texlive[59342] Master/texmf-dist: expkv (25may21)

commits+karl at tug.org commits+karl at tug.org
Tue May 25 22:47:02 CEST 2021


Revision: 59342
          http://tug.org/svn/texlive?view=revision&revision=59342
Author:   karl
Date:     2021-05-25 22:47:02 +0200 (Tue, 25 May 2021)
Log Message:
-----------
expkv (25may21)

Modified Paths:
--------------
    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/expkv.dtx
    trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex

Modified: trunk/Master/texmf-dist/doc/latex/expkv/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/expkv/README.md	2021-05-25 20:46:48 UTC (rev 59341)
+++ trunk/Master/texmf-dist/doc/latex/expkv/README.md	2021-05-25 20:47:02 UTC (rev 59342)
@@ -1,7 +1,7 @@
 -------------------------------------------------------------------------------
 # expkv -- an expandable key=val implementation
 
-Version 2021-04-11 v1.7a
+Version 2021-05-24 v1.8
 
 Released under the LaTeX Project Public License v1.3c or later
 See http://www.latex-project.org/lppl.txt

Modified: trunk/Master/texmf-dist/doc/latex/expkv/expkv.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx	2021-05-25 20:46:48 UTC (rev 59341)
+++ trunk/Master/texmf-dist/source/latex/expkv/expkv.dtx	2021-05-25 20:47:02 UTC (rev 59342)
@@ -76,34 +76,130 @@
 \RequirePackage{microtype}
 \RequirePackage{accsupp}
 \RequirePackage{enumitem}
+\RequirePackage{tcolorbox}
+\newtcolorbox{exresult}[2][]
+  {colback=expkvgrey!10!white,colframe=expkvgrey,fontupper=\small,width={#2},#1}
+\newtcbox\exres[1][]
+  {
+     colback=expkvgrey!10!white
+    ,colframe=expkvgrey
+    ,size=small
+    ,nobeforeafter
+    ,tcbox raise base
+    ,fontupper=\small
+    ,#1
+  }
+\def\mylstwd{.55em}
+\lstdefinelanguage{expkv}[primitive]{TeX}
+  {
+    ,moretexcs=[4]^^A e-TeX
+      {
+        expanded,
+        numexpr,
+        protected,
+      }
+    ,moretexcs=[5]^^A plain/LaTeX
+      {
+        approx,
+        begin,
+        empty,
+        item,
+        LaTeX,
+        makeatletter,makeatother,
+        newcommand,newdimen,
+        RequirePackage,
+        rule,
+        TeX,
+        textit,texttt,
+        usepackage,
+      }
+    ,moretexcs=[6]^^A used packages
+      {
+        ^^A xfp
+        fpeval,
+        ^^A keyval
+        setkeys,
+        ^^A yax
+        defactiveparameter,storevalue,setparameterlist
+      }
+    ,moretexcs=[2]^^A expkv macros
+      {
+        ekvdef,ekvdefNoVal,^^A
+        ekvlet,ekvletNoVal,ekvletkv,ekvletkvNoVal,^^A
+        ekvdefunknown,ekvdefunknownNoVal,^^A
+        ekvredirectunknown,ekvredirectunknownNoVal,^^A
+        ekvset,ekvsetSneaked,^^A
+        ekvsetdef,ekvsetSneakeddef,ekvsetdefSneaked,^^A
+        ekvparse,^^A
+        ekvVersion,ekvDate,^^A
+        ekvifdefined,ekvifdefinedNoVal,ekvifdefinedset,^^A
+        ekvbreak,ekvbreakPreSneak,ekvbreakPostSneak,^^A
+        ekvsneak,ekvsneakPre,^^A
+        ekvchangeset,^^A
+        ekvoptarg,ekvoptargTF,^^A
+        ekverr
+      }
+    ,moretexcs=[3]^^A expkv-pkg macros
+      {
+        ^^A expkv-cs
+        ekvcSecondaryKeys,ekvcSplitAndForward,
+        ^^A expkv-def
+        ekvdefinekeys
+      }
+  }
+\colorlet{codeparam}{cyan!65!black}
 \lstset
   {
+    ,language=expkv
     ,flexiblecolumns=false
-    ,basewidth=.53em
+    ,basewidth=\mylstwd
     ,gobble=2
     ,basicstyle=\fontfamily{jkp}\itshape
-    ,morekeywords=^^A
-      {^^A
-        \ekvdef,\ekvdefNoVal,^^A
-        \ekvlet,\ekvletNoVal,\ekvletkv,\ekvletkvNoVal,^^A
-        \ekvdefunknown,\ekvdefunknownNoVal,^^A
-        \ekvredirectunknown,\ekvredirectunknownNoVal,^^A
-        \ekvset,\ekvsetSneaked,^^A
-        \ekvsetdef,\ekvsetSneakeddef,\ekvsetdefSneaked,^^A
-        \ekvparse,^^A
-        \ekvVersion,\ekvDate,^^A
-        \ekvifdefined,\ekvifdefinedNoVal,\ekvifdefinedset,^^A
-        \ekvbreak,\ekvbreakPreSneak,\ekvbreakPostSneak,^^A
-        \ekvsneak,\ekvsneakPre,^^A
-        \ekvchangeset
-      }
-    ,morecomment=[l]\%
+    ,texcsstyle=*[1]{\bfseries\color{expkvgrey}} ^^A primitives
+    ,texcsstyle=*[2]{\bfseries\color{expkvred}}  ^^A expkv
+    ,texcsstyle=*[3]{\color{expkvred}}           ^^A expkv-pkg
+    ,texcsstyle=*[4]{\bfseries\color{expkvgrey}} ^^A e-TeX
+    ,texcsstyle=*[5]{\bfseries\color{expkvgrey}} ^^A plain/LaTeX macros
+    ,texcsstyle=*[6]{}                           ^^A macros of other packages
     ,commentstyle=\color[gray]{0.4}
-    ,literate={\{}{{\CodeSymbol\{}}{1}
-              {\}}{{\CodeSymbol\}}}{1}
+    ,literate=
+              {\{} {{\CodeUpBf\{}}{1}
+              {\}} {{\CodeUpBf\}}}{1}
+              {$}  {{\CodeUpBf\$}}{1}
+              {[}  {{\CodeUp[}}{1}
+              {]}  {{\CodeUp]}}{1}
+              {(}  {{\CodeUp(}}{1}
+              {)}  {{\CodeUp)}}{1}
+              {*}  {{$*$}}{1}
+              {1}  {{\CodeUp{1}}}{1}
+              {2}  {{\CodeUp{2}}}{1}
+              {3}  {{\CodeUp{3}}}{1}
+              {4}  {{\CodeUp{4}}}{1}
+              {5}  {{\CodeUp{5}}}{1}
+              {6}  {{\CodeUp{6}}}{1}
+              {7}  {{\CodeUp{7}}}{1}
+              {8}  {{\CodeUp{8}}}{1}
+              {9}  {{\CodeUp{9}}}{1}
+              {0}  {{\CodeUp{0}}}{1}
+              {##} {{\CodeColored{codeparam}{1}{\#}}}{1}
+              {##1}{{\CodeColored{codeparam}{2}{\#1}}}{2}
+              {##2}{{\CodeColored{codeparam}{2}{\#2}}}{2}
+              {##3}{{\CodeColored{codeparam}{2}{\#3}}}{2}
+              {##4}{{\CodeColored{codeparam}{2}{\#4}}}{2}
+              {##5}{{\CodeColored{codeparam}{2}{\#5}}}{2}
+              {##6}{{\CodeColored{codeparam}{2}{\#6}}}{2}
+              {##7}{{\CodeColored{codeparam}{2}{\#7}}}{2}
+              {##8}{{\CodeColored{codeparam}{2}{\#8}}}{2}
+              {##9}{{\CodeColored{codeparam}{2}{\#9}}}{2}
+              {<key>}{{$\langle$}key{$\rangle$}}{5}
+              {<set>}{{$\langle$}set{$\rangle$}}{5}
+              {<set1>}{{$\langle$}set1{$\rangle$}}{6}
+              {<set2>}{{$\langle$}set2{$\rangle$}}{6}
     ^^A,literate=*{<key>}{\key}{4}{<set>}{\set}{4}
   }
-\newcommand*\CodeSymbol[1]{\textbf{#1}}
+\newcommand*\CodeColored[3]{\textcolor{#1}{\makebox[\dimexpr\mylstwd*#2]{#3}}}
+\newcommand*\CodeUpBf[1]{\makebox[\mylstwd]{\textup{\textbf{#1}}}}
+\newcommand*\CodeUp[1]{\makebox[\mylstwd]{\textup{#1}}}
 \RequirePackage{randtext}
 \let\metaORIG\meta
 \protected\def\meta #1{\texttt{\metaORIG{#1}}}
@@ -474,7 +570,13 @@
 %   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.
+%
+%   |\ekvset| is currently \emph{not} alignment safe.\footnotemark\ As a result,
+%   key names and values that contain an |&| must be wrapped in braces when
+%   |\ekvset| is used inside an alignment (like \LaTeXe's |tabular| environment)
+%   or you have to create a wrapper that ensures an alignment safe context.
 % \end{function}
+% \footnotetext{This might change in the future, I've not decided yet.}
 % \example Parse |key=arg, key| in the set |foo|:
 % \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
 % \ekvset{foo}{key=arg, key}
@@ -564,12 +666,16 @@
 %   only keys as an argument to \meta{code1}, and those which are a \kv\ pair
 %   to \meta{code2} as two arguments. It is fully expandable as well and returns
 %   each element of the parsed list in |\unexpanded|, which has no effect
-%   outside of an |\expanded| or |\edef| context\footnotemark.
-%   If you need control over the necessary steps of expansion you
-%   can use |\expanded| around it. You can use multiple tokens in \meta{code1}
-%   and \meta{code2} or just a single control sequence name. In both cases the
-%   found \key\ and \val\ are provided as a brace group following them.
+%   outside of an |\expanded| or |\edef| context.
+%   Also |\ekvparse| expands in exactly two steps of expansion.
+%   You can use multiple tokens in \meta{code1} and \meta{code2} or just a
+%   single control sequence name. In both cases the found \key\ and \val\ are
+%   provided as a brace group following them.
 %
+%   |\ekvparse| is alignment safe, meaning that you don't have to take any
+%   precautions if it is used inside an alignment context (like \LaTeXe's
+%   |tabular| environment) and any key or value can contain an |&|.
+%
 %   |\ekvbreak|, |\ekvsneak|, and |\ekvchangeset| and their relatives don't work
 %   in |\ekvparse|.  It is analogue to \pkg{expl3}'s |\keyval_parse:NNn|, but
 %   not with the same parsing rules -- |\keyval_parse:NNn| throws an error on
@@ -576,14 +682,6 @@
 %   multiple equal signs per \kv\ pair and on empty \key\ names in a \kv\ pair,
 %   both of which |\ekvparse| doesn't deal with.
 % \end{function}
-% \footnotetext 
-%   {^^A
-%     This is a change in behaviour, previously (v0.3 and before) \cs{ekvparse}
-%     would expand in exactly two steps. This isn't always necessary, but makes
-%     the parsing considerably slower. If this is necessary for your application
-%     you can put an \cs[no-index]{expanded} around it and will still be faster
-%     since you need only a single \cs[no-index]{expandafter} this way.^^A
-%   }
 % \example
 % \begin{lstlisting}
 % \ekvparse{\handlekey{S}}{\handlekeyval{S}}{foo = bar, key, baz={zzz}}
@@ -599,11 +697,11 @@
 %   another macro, you should use |\expanded| as only then the input stream will
 %   contain the output above:
 % \begin{lstlisting}
-% \expandafter\handle\expanded{\ekvparse\k\kv{foo = bar, key, baz={zzz}}}
+% \expandafter\parse\expanded{\ekvparse\k\kv{foo = bar, key, baz={zzz}}}
 % \end{lstlisting}
 %   would expand to
 % \begin{lstlisting}
-% \handle\kv{foo}{bar}\k{key}\kv{baz}{zzz}
+% \parse\kv{foo}{bar}\k{key}\kv{baz}{zzz}
 % \end{lstlisting}
 %
 % \subsection{Other Macros}
@@ -673,7 +771,7 @@
 %   list everything that has been |\ekvsneak|ed will be left in the input
 %   stream. A small usage example is shown in \autoref{sec:sneakex}.
 % \end{function}
-% \example Define a key |secret| in the set |foo| that will sneak out a macro
+% \example Define a key |secret| in the set |foo| that will sneak out
 % |\foo at secretly@sneaked|:
 % \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
 % \ekvdefNoVal{foo}{secret}{\ekvsneak{\foo at secretly@sneaked}}
@@ -696,6 +794,124 @@
 %   {\ekvifdefinedset{#1}{\ekvchangeset{#1}}{\ekvbreak{\foo at cd@error}}}
 % \end{lstlisting}
 %
+% \begin{function}{\ekvoptarg}
+%   \begin{syntax}
+%     \cs{ekvoptarg}\marg{next}\marg{default}
+%   \end{syntax}
+%   This macro will check for a following optional argument in brackets (|[]|)
+%   expandably. After the optional argument there has to be a mandatory one. The
+%   code in \meta{next} should expect two arguments (the processed optional
+%   argument and the mandatory one). If there was an optional argument the
+%   result will be \meta{next}\marg{optional}\meta{mandatory} (so the optional
+%   argument will be wrapped in braces, the mandatory argument will be
+%   untouched). If there was no optional argument the result will be
+%   \meta{next}\marg{default}\marg{mandatory} (so the default will be used and
+%   the mandatory argument will be wrapped in braces).
+% \end{function}
+% |\ekvoptarg| expands in exactly two steps and is alignment safe. It has its
+% limitations however. It can't tell the difference between |[| and |{[}|, so it
+% doesn't work if the mandatory argument is a single bracket. Also if the
+% optional argument should contain a nested closing bracket, the optional
+% argument has to use nested braces like so: |[{arg]ument}]|.
+% \example Say we have a macro that should take an optional argument defaulting
+% to 1:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \newcommand\foo{\ekvoptarg\@foo{1}}
+% \newcommand\@foo[2]{Mandatory: #2\par Optional: #1}
+% \end{lstlisting}
+%
+% \begin{function}{\ekvoptargTF}
+%   \begin{syntax}
+%     \cs{ekvoptargTF}\marg{true}\marg{false}
+%   \end{syntax}
+%   This macro is similar to |\ekvoptarg|, but will result in
+%   \meta{true}\marg{optional}\meta{mandatory} or \meta{false}\marg{mandatory}
+%   instead of placing a default value.
+% \end{function}
+% |\ekvoptargTF| expands in exactly two steps and is alignment safe. It has the
+% same limitations as |\ekvoptarg|.
+% \example Say we have a macro that should behave differently depending on
+% whether there was an optional argument or not. This could be done with:
+% \begin{lstlisting}[aboveskip=0pt,belowskip=0pt]
+% \newcommand\foo{\ekvoptargTF\foo at a\foo at b}
+% \newcommand\foo at a[2]{Mandatory: #2\par Optional: #1}
+% \newcommand\foo at b[1]{Mandatory: #1\par No optional.}
+% \end{lstlisting}
+%
+% \begin{function}{\ekverr}
+%   \begin{syntax}
+%     \cs{ekverr}\marg{package}\marg{message}
+%   \end{syntax}
+%   This macro will throw an error fully expandably.\footnotemark\ The error
+%   length is limited to a total length of 69~characters, and since ten
+%   characters will be added for the formatting (\verb*|! | and
+%   \verb*| Error: |) that leaves us with a total length for \meta{package} plus
+%   \meta{message} of 59~characters. If the message gets longer \TeX\ will only
+%   display the first 69~characters and append |\ETC.| to the end.
+%
+%   Neither \meta{package} nor \meta{message} expand any further. Also
+%   \meta{package} must not contain an explicit |\par| token or the token
+%   |\thanks at jfbu|. No such restriction applies to \meta{message}.
+%
+%   If |^^J| is set up as the |\newlinechar| (which is the case in \LaTeXe\ but
+%   not in plain \TeX\ by default) you can use that to introduce line breaks in
+%   your error message. However that doesn't change the message length limit.
+% \end{function}
+% \footnotetext{The used mechanism was to the best of my knowledge first
+% implemented by Jean-François Burnol.}
+% After your own error message some further text will be placed. The
+% formatting of that text will look good if |^^J| is the |\newlinechar|, else
+% not so much. That text will read:
+% \begin{verbatim}
+% ! Paragraph ended before \<an-expandable-macro>
+% completed due to above exception.  If the error
+% summary is  not comprehensible  see the package
+% documentation.
+% I will try to recover now.  If you're in inter-
+% active mode hit <return>  at the ? prompt and I
+% continue hoping recovery was complete.
+% \end{verbatim}
+% Any clean up has to be done by you, |\ekverr| will expand to nothing after
+% throwing the error message.
+% \example Say we set up a small calculation which works with user input. In our
+% calculation we need a division, so have to watch out for division by zero. If
+% we detect such a case we throw an error and do the recovery by using the
+% biggest integer allowed in \TeX\ as the result.
+% \begin{lstlisting}
+% \newcommand*\mydivision[2]
+%   {%
+%     \number\numexpr
+%       \ifnum\numexpr#2=0 % space here on purpose
+%         \ekverr{my}{division by 0. Setting result to 2147483647.}%
+%         2147483647%
+%       \else
+%         (#1)/(#2)%
+%       \fi
+%     \relax
+%   }
+% $(10+5)/(3-3)\approx\mydivision{10+5}{3-3}$
+% \end{lstlisting}
+% If that code gets executed the following will be the terminal output
+% \begin{verbatim}
+% Runaway argument?
+% ! my Error: division by 0. Setting result to 2147483647.
+% ! Paragraph ended before \<an-expandable-macro>
+% completed due to above exception.  If the error
+% summary is  not comprehensible  see the package
+% documentation.
+% I will try to recover now.  If you're in inter-
+% active mode hit <return>  at the ? prompt and I
+% continue hoping recovery was complete.
+% <to be read again>
+%                    \par
+% l.15 $(10+5)/(3-3)\approx\mydivision{10+5}{3-3}
+%                                                $
+% ?
+% \end{verbatim}
+% and the output would contain
+% \exres{$(10+5)/(3-3)\approx2147483647$}
+% if we continued the \TeX\ run at the prompt.
+%
 % \bigskip
 %
 % \begin{function}{\ekv at name,\ekv at name@set,\ekv at name@key}
@@ -744,10 +960,11 @@
 % \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}
+% \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{minipage}{.5\linewidth}
 % \begin{lstlisting}
 % \ourmacro{}\par
 % \ourmacro{width}\par
@@ -754,7 +971,7 @@
 % \ourmacro{width=5pt}\par
 % \end{lstlisting}
 % \end{minipage}^^A
-% \begin{minipage}{\dimexpr\linewidth-6cm\relax}
+% \begin{exresult}[nobeforeafter,box align=center]{.5\linewidth}
 %   \newdimen\ourdim\ourdim=150pt
 %   \protected\ekvdef{our}{width}{\ourdim=#1\relax}^^A
 %   \protected\ekvdefNoVal{our}{width}{\ourdim=.9\hsize}^^A
@@ -763,7 +980,7 @@
 %   \ourmacro{}\par
 %   \ourmacro{width}\par
 %   \ourmacro{width=5pt}\par
-% \end{minipage}
+% \end{exresult}
 %
 % \paragraph{The same keys using \protect\expkvd}
 % Using \expkvd\ we can set up the equivalent key using a \kv\ interface, after
@@ -785,7 +1002,7 @@
 %
 % Another small example could be a \kv\ driven |\rule| alternative, because I
 % keep forgetting the correct order of its arguments. First we define the keys
-% (and initialize the used macros to store the keys):
+% (and initialize the macros used to store the keys):
 % \begin{lstlisting}
 % \makeatletter
 % \newcommand*\myrule at ht{1ex}
@@ -794,12 +1011,14 @@
 % \protected\ekvdef{myrule}{ht}{\def\myrule at ht{#1}}
 % \protected\ekvdef{myrule}{wd}{\def\myrule at wd{#1}}
 % \protected\ekvdef{myrule}{raise}{\def\myrule at raise{#1}}
+% \protected\ekvdef{myrule}{lower}{\def\myrule at raise{-#1}}
 % \end{lstlisting}
 % Then we define a macro to change the defaults outside of |\myrule| and
 % |\myrule| itself:
 % \begin{lstlisting}
 % \ekvsetdef\myruleset{myrule}
-% \newcommand*\myrule[1][]{\begingroup\myruleset{#1}\myrule at out\endgroup}
+% \newcommand*\myrule[1][]
+%   {\begingroup\myruleset{#1}\myrule at out\endgroup}
 % \end{lstlisting}
 % And finally the output:
 % \begin{lstlisting}
@@ -807,15 +1026,15 @@
 % \makeatother
 % \end{lstlisting}
 % And we can use it:\\
-% \begin{minipage}{7cm}
+% \begin{minipage}{.5\linewidth}
 % \begin{lstlisting}
 % a\myrule\par
-% a\myrule[ht=2ex,raise=-.5ex]\par
+% a\myrule[ht=2ex,lower=.5ex]\par
 % \myruleset{wd=5pt}
 % a\myrule
 % \end{lstlisting}
 % \end{minipage}^^A
-% \begin{minipage}{\dimexpr\linewidth-7cm\relax}
+% \begin{exresult}[nobeforeafter,box align=center]{.5\linewidth}
 %   \makeatletter
 %   \newcommand*\myrule at ht{1ex}
 %   \newcommand*\myrule at wd{0.1em}
@@ -823,17 +1042,18 @@
 %   \protected\ekvdef{myrule}{ht}{\def\myrule at ht{#1}}
 %   \protected\ekvdef{myrule}{wd}{\def\myrule at wd{#1}}
 %   \protected\ekvdef{myrule}{raise}{\def\myrule at raise{#1}}
+%   \protected\ekvdef{myrule}{lower}{\def\myrule at raise{-#1}}
 %   \ekvsetdef\myruleset{myrule}
 %   \newcommand*\myrule[1][]{\begingroup\myruleset{#1}\myrule at out\endgroup}
 %   \newcommand*\myrule at out{\rule[\myrule at raise]\myrule at wd\myrule at ht}
 %   \makeatother
 %   a\myrule\par
-%   a\myrule[ht=2ex,raise=-.5ex]\par
+%   a\myrule[ht=2ex,lower=.5ex]\par
 %   \myruleset{wd=5pt}
 %   a\myrule
-% \end{minipage}
-% 
+% \end{exresult}
 %
+%
 % \subsubsection{An Expandable \kv\ Macro Using \cs[no-index]{ekvsneak}}
 % \label{sec:sneakex}
 %
@@ -877,7 +1097,10 @@
 % 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.
+% right before it. The user macro |\sine| uses |\ekvoptargTF| to check for the
+% optional argument short cutting to the final step if no optional argument was
+% found. This way we safe some time in this case, though we have to specify the
+% default values twice.
 %
 % \begin{lstlisting}
 % \RequirePackage{xfp}
@@ -887,13 +1110,10 @@
 % \ekvdefNoVal{expex}{degree}{\ekvsneakPre{\deg{d}}}
 % \ekvdefNoVal{expex}{radian}{\ekvsneakPre{\deg{}}}
 % \ekvdefNoVal{expex}{internal}{\ekvsneakPre{\sine at rnd}}
-% \newcommand*\sine[2]
+% \newcommand*\sine{\ekvoptargTF\sine at args{\sine at final{sin}{d}{3}}}
+% \newcommand*\sine at args[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
@@ -922,15 +1142,15 @@
 % \makeatother
 % \end{lstlisting}
 % Let's test our macro:\\
-% \begin{minipage}{.7\linewidth}
+% \begin{minipage}{.75\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}
+%   \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}
+% \end{minipage}^^A
+% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth}
 %   \makeatletter
 %   \ekvdef{expex}{f}{\ekvsneakPre{\f{#1}}}^^A
 %   \ekvdef{expex}{round}{\ekvsneakPre{\rnd{#1}}}^^A
@@ -937,18 +1157,19 @@
 %   \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
+%   \newcommand*\sine{\ekvoptargTF\sine at args{\sine at final{sin}{d}{3}}}
+%   \newcommand*\sine at args[2]
+%     {\ekvset{expex}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine at stop{#2}}
 %   \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}
+%   \sine{60}\par
+%   \sine[round=10]{60}\par
+%   \sine[f=cos,radian]{pi}\par
+%   \edef\myval{\sine[f=tan]{1}}\texttt{\meaning\myval}
+% \end{exresult}
 %
 % \paragraph{The same macro using \protect\expkvc}
 % Using \expkvc\ we can set up something equivalent with a bit less code. The
@@ -956,18 +1177,19 @@
 % way easier to code for the user.
 % \begin{lstlisting}
 % \makeatletter
-% \ekvcSplitAndForward\sine\sine@
+% \newcommand*\sine{\ekvoptargTF\sine at a{\sine at b{sin}{d}{3}}}
+% \ekvcSplitAndForward\sine at a\sine at b
 %   {
 %     f=sin,
 %     unit=d,
 %     round=3,
 %   }
-% \ekvcSecondaryKeys\sine
+% \ekvcSecondaryKeys\sine at a
 %   {
 %     nmeta degree={unit=d},
 %     nmeta radian={unit={}},
 %   }
-% \newcommand*\sine@[4]{\fpeval{round(#1#2(#4),#3)}}
+% \newcommand*\sine at b[4]{\fpeval{round(#1#2(#4),#3)}}
 % \makeatother
 % \end{lstlisting}
 % The resulting macro will behave just like the one previously defined, but will
@@ -982,8 +1204,8 @@
 %
 % \subsubsection{Load Time}
 %
-% \file{expkv.tex} checks whether \eTeX\ is available. If it isn't, an error
-% will be thrown using |\errmessage|:
+% \file{expkv.tex} checks whether \eTeX\ and the |\expanded| primitive are
+% available. If it isn't, an error will be thrown using |\errmessage|:
 % \begin{lstlisting}
 % ! expkv Error: e-TeX required.
 % \end{lstlisting}
@@ -1010,41 +1232,37 @@
 % \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
+% thrown in an expandable manner using |\ekverr|. 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>').
+% Runaway argument?
+% ! expkv Error: unknown key `<key>' in 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>').
+% Runaway argument?
+% ! expkv Error: missing value for `<key>' in 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>').
+% Runaway argument?
+% ! expkv Error: unwanted value for `<key>' in set `<set>'
 % \end{lstlisting}
 %
 % If you're using an undefined key in a set for which |\ekvredirectunknown| was
 % used, and the key isn't found in any of the other sets as well, you'll get:
 % \begin{lstlisting}
-% ! Undefined control sequence.
-% <argument> \! expkv Error:
-%                            no key `<key>' in sets {<set1>}{<set2>}...
+% Runaway argument?
+% ! expkv Error: no key `<key>' in sets {<set1>}{<set2>}...
 % \end{lstlisting}
 %
 % If you're using an undefined |NoVal| key in a set for which
@@ -1051,9 +1269,8 @@
 % |\ekvredirectunknownNoVal| was used, and the key isn't found in any of the
 % other sets as well, you'll get:
 % \begin{lstlisting}
-% ! Undefined control sequence.
-% <argument> \! expkv Error:
-%                            no NoVal key `<key>' in sets {<set1>}...
+% Runaway argument?
+% ! expkv Error: no NoVal key `<key>' in sets {<set1>}{<set2>}...
 % \end{lstlisting}
 %
 % If you're using a set for which you never executed one of the defining macros
@@ -1060,7 +1277,7 @@
 % 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}
+% \begin{lstlisting}[language={}]
 % ! Missing \endcsname inserted.
 % <to be read again>
 %                    \! expkv Error: Set `<set>' undefined.
@@ -1197,8 +1414,8 @@
 % loading \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
+% \defactiveparameter yax {\storevalue\myheight yax:height } % setup
+% \setparameterlist{yax}{ height = 6 }                       % benchmark
 % \end{lstlisting}
 %
 % \begin{table}
@@ -1313,11 +1530,16 @@
 \fi
 %    \end{macrocode}
 %
-% Check whether \eTeX\ is available -- \expkv\ requires \eTeX.
+% Check whether \eTeX\ and |\expanded| are available -- \expkv\ requires \eTeX.
 %    \begin{macrocode}
-\begingroup\expandafter\expandafter\expandafter\endgroup
-\expandafter\ifx\csname numexpr\endcsname\relax
-  \errmessage{expkv requires e-TeX}
+\begingroup
+  \edef\ekvtmpa{\string\expanded}
+  \edef\ekvtmpb{\meaning\expanded}
+  \expandafter
+\endgroup
+\ifx\ekvtmpa\ekvtmpb
+\else
+  \errmessage{expkv Error: e-TeX and \noexpand\expanded required}
   \expandafter\endinput
 \fi
 %    \end{macrocode}
@@ -1325,8 +1547,8 @@
 % \begin{macro}{\ekvVersion,\ekvDate}
 % We're on our first input, so lets store the version and date in a macro.
 %    \begin{macrocode}
-\def\ekvVersion{1.7a}
-\def\ekvDate{2021-04-11}
+\def\ekvVersion{1.8}
+\def\ekvDate{2021-05-24}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -2092,9 +2314,12 @@
 % 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.
+% on our output. The |\unexpanded\expanded{{...}}| ensures that the material is
+% in an alignment safe group at all time, and that it doesn't expand any further
+% in an |\edef| or |\expanded| context.
 %    \begin{macrocode}
-\long\def\ekvparse##1##2##3{\ekv at parse{##1}{##2}\ekv at mark##3#1\ekv at stop#1}
+\long\def\ekvparse##1##2##3%
+  {\unexpanded\expanded{{\ekv at parse{##1}{##2}\ekv at mark##3#1\ekv at stop#1}}}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -2440,21 +2665,127 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[internal]{\ekv at err,\ekv at err@}
+% \begin{macro}[internal]{\ekv at alignsafe,\ekv at endalignsafe}
+%   These macros protect the usage of ampersands inside of alignment contexts.
+%    \begin{macrocode}
+\begingroup
+\catcode`\^^@=2
+\@firstofone{\endgroup
+  \def\ekv at alignsafe{\romannumeral\iffalse{\fi`^^@ }
+}
+\def\ekv at endalignsafe{\ifnum`{=\ekv at zero}\fi}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekvoptarg,\ekvoptargTF}
+% Provide macros to expandably collect an optional argument in brackets. The
+% macros here are pretty simple in nature compared to \pkg{xparse}'s
+% possibilities (they don't care for nested bracket levels).
+%
+% We start with a temporary definition to pre-expand |\ekv at alignsafe| (will be
+% |#1|) and |\ekv at endalignsafe| (will be |#2|).
+%    \begin{macrocode}
+\begingroup
+\def\ekvoptarg#1#2{%
+\endgroup
+%    \end{macrocode}
+% The real definition starts an expansion context after grabbing the arguments.
+% |#1| will be the next step, |#2| the default value, and |#3| might be an
+% opening bracket, or the mandatory argument. We check for the opening bracket,
+% if it is found grab the optional argument, else leave |#1{#2}| in the input
+% stream after ending the expansion context.
+%    \begin{macrocode}
+\long\def\ekvoptarg##1##2##3%
+  {%
+    \romannumeral
+    #1%
+    \ekv at optarg@if\ekv at mark##3\ekv at mark\ekv at optarg\ekv at mark[\ekv at mark
+    #2%
+    \@firstofone{\ekv at zero##1}{##2}{##3}%
+  }
+%    \end{macrocode}
+% The other variant of this will do roughly the same. Here, |#1| will be the
+% next step if an optional argument is found, |#2| the next step else, and |#3|
+% might be the opening bracket or mandatory argument.
+%    \begin{macrocode}
+\long\def\ekvoptargTF##1##2##3%
+  {%
+    \romannumeral
+    #1%
+    \ekv at optarg@if\ekv at mark##3\ekv at mark\ekv at optargTF{##1}\ekv at mark[\ekv at mark
+    #2%
+    \@firstofone{\ekv at zero##2}{##3}%
+  }
+%    \end{macrocode}
+% The two macros to grab the optional argument have to remove the remainder of
+% the test and the wrong next step as well as grabbing the argument.
+%    \begin{macrocode}
+\long\def\ekv at optarg\ekv at mark[\ekv at mark\ifnum`##1\fi\@firstofone##2##3##4##5]%
+  {#2##2{##5}}
+\long\def\ekv at optargTF##1\ekv at mark[\ekv at mark\ifnum`##2\fi\@firstofone##3##4##5]%
+  {#2\ekv at zero##1{##5}}
+}
+%    \end{macrocode}
+% Do the definitions and add the test macro.
+%    \begin{macrocode}
+\ekv at exparg{\expandafter\ekvoptarg\expandafter{\ekv at alignsafe}}\ekv at endalignsafe
+\long\def\ekv at optarg@if#1\ekv at mark[\ekv at mark{}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ekverr}
+% \begin{macro}[internal]{\ekv at err@collect,\ekv at err@cleanup}
 % 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.
+% a runaway argument (to my knowledge the first version of this method was
+% implemented by Jean-François Burnol, many thanks to him). The first step is to
+% ensure that the second argument (which might contain user input) doesn't
+% contain tokens we use as delimiters (in this case |\par|), this will be done
+% by the front facing macro |\ekverr|. But first we set some other things up.
+%
+% We use a temporary definition for |\ekverr| to get multiple consecutive
+% spaces. Then we set up the macro that will collect the error and the macro
+% that will throw the error. The latter will have an unreasonable long name.
+% This way we can convey more information. Though the information in the macro
+% name is static and has to be somewhat general to fit every occurence. The
+% important bit is that the long named macro has a delimited argument and is
+% short which will throw the error at the |\par| at the end of
+% |\ekv at err@collect|. This macro has the drawback that it will only print nicely
+% if the |\newlinechar| is |^^J|.
 %    \begin{macrocode}
-\def\ekv at err#1%
+\def\ekv at err@cleanup\par{}
+\def\ekv at err@collect#1%
   {%
-    \long\def\ekv at err##1{\expandafter\ekv at err@\@firstofone{#1##1.}\ekv at stop}%
+    \def\ekv at err@collect##1\par##2%
+      {%
+        \expandafter
+        \ekv at err@cleanup
+        #1! ##2 Error: ##1\par
+      }%
+    \def#1##1\thanks at jfbu{}%
   }
-\begingroup\expandafter\endgroup
-\expandafter\ekv at err\csname ! expkv Error:\endcsname
-\def\ekv at err@{\expandafter\ekv at gobbleto@stop}
+\def\ekverr{ }
+\expandafter\ekv at err@collect\csname <an-expandable-macro>^^J%
+  completed due to above exception. \ekverr If the error^^J%
+  summary is \ekverr not comprehensible \ekverr see the package^^J%
+  documentation.^^J%
+  I will try to recover now. \ekverr If you're in inter-^^J%
+  active mode hit <return> \ekverr at the ? prompt and I^^J%
+  continue hoping recovery\endcsname
 %    \end{macrocode}
+%    \begin{macrocode}
+\long\def\ekverr#1#2{\expandafter\ekv at err@collect\detokenize{#2}\par{#1}}
+%    \end{macrocode}
 % \end{macro}
+% \end{macro}
 %
+% \begin{macro}[internal]{\ekv at err}
+% We define a shorthand to throw errors in \expkv.
+%    \begin{macrocode}
+\ekv at exparg{\long\def\ekv at err#1}{\ekverr{expkv}{#1}}
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}[internal]
 %   {
 %     \ekv at err@common,\ekv at err@common@,
@@ -2465,10 +2796,10 @@
 %    \begin{macrocode}
 \long\def\ekv at err@common #1#2{\expandafter\ekv at err@common@\string#2{#1}}
 \ekv at exparg{\long\def\ekv at err@common@#1`#2' #3.#4#5}%
-  {\ekv at err{#4 (`#5', set `#2')}}
+  {\ekv at err{#4 `#5' in set `#2'}}
 \ekv at exparg{\long\def\ekv at err@unknown#1}{\ekv at err@common{unknown key}{#1}}
-\ekv at exparg{\long\def\ekv at err@noarg  #1}{\ekv at err@common{value forbidden}{#1}}
-\ekv at exparg{\long\def\ekv at err@reqval #1}{\ekv at err@common{value required}{#1}}
+\ekv at exparg{\long\def\ekv at err@noarg  #1}{\ekv at err@common{unwanted value for}{#1}}
+\ekv at exparg{\long\def\ekv at err@reqval #1}{\ekv at err@common{missing value for}{#1}}
 \ekv at exparg{\long\def\ekv at err@redirect at kv@notfound#1#2#3\ekv at stop}%
   {\ekv at err{no key `#2' in sets #3}}
 \ekv at exparg{\def\ekv at err@redirect at k@notfound#1#2\ekv at stop}%

Modified: trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex
===================================================================
--- trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex	2021-05-25 20:46:48 UTC (rev 59341)
+++ trunk/Master/texmf-dist/tex/generic/expkv/expkv.tex	2021-05-25 20:47:02 UTC (rev 59342)
@@ -34,13 +34,18 @@
 \else
   \expandafter\endinput
 \fi
-\begingroup\expandafter\expandafter\expandafter\endgroup
-\expandafter\ifx\csname numexpr\endcsname\relax
-  \errmessage{expkv requires e-TeX}
+\begingroup
+  \edef\ekvtmpa{\string\expanded}
+  \edef\ekvtmpb{\meaning\expanded}
+  \expandafter
+\endgroup
+\ifx\ekvtmpa\ekvtmpb
+\else
+  \errmessage{expkv Error: e-TeX and \noexpand\expanded required}
   \expandafter\endinput
 \fi
-\def\ekvVersion{1.7a}
-\def\ekvDate{2021-04-11}
+\def\ekvVersion{1.8}
+\def\ekvDate{2021-05-24}
 \csname ekv at tmp\endcsname
 \expandafter\chardef\csname ekv at tmp\endcsname=\catcode`\@
 \catcode`\@=11
@@ -427,7 +432,8 @@
 \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{\ekv at parse{##1}{##2}\ekv at mark##3#1\ekv at stop#1}
+\long\def\ekvparse##1##2##3%
+  {\unexpanded\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 gobble@from at mark@to at stop##3\ekv at endparse\ekv at stop
@@ -643,19 +649,65 @@
     \ekv at exparg{\ekv at zero\ekv at exparg{\long\def#1##1}}%
       {\ekvsetSneaked{#2}{#3}{##1}}%
   }
-\def\ekv at err#1%
+\begingroup
+\catcode`\^^@=2
+\@firstofone{\endgroup
+  \def\ekv at alignsafe{\romannumeral\iffalse{\fi`^^@ }
+}
+\def\ekv at endalignsafe{\ifnum`{=\ekv at zero}\fi}
+\begingroup
+\def\ekvoptarg#1#2{%
+\endgroup
+\long\def\ekvoptarg##1##2##3%
   {%
-    \long\def\ekv at err##1{\expandafter\ekv at err@\@firstofone{#1##1.}\ekv at stop}%
+    \romannumeral
+    #1%
+    \ekv at optarg@if\ekv at mark##3\ekv at mark\ekv at optarg\ekv at mark[\ekv at mark
+    #2%
+    \@firstofone{\ekv at zero##1}{##2}{##3}%
   }
-\begingroup\expandafter\endgroup
-\expandafter\ekv at err\csname ! expkv Error:\endcsname
-\def\ekv at err@{\expandafter\ekv at gobbleto@stop}
+\long\def\ekvoptargTF##1##2##3%
+  {%
+    \romannumeral
+    #1%
+    \ekv at optarg@if\ekv at mark##3\ekv at mark\ekv at optargTF{##1}\ekv at mark[\ekv at mark
+    #2%
+    \@firstofone{\ekv at zero##2}{##3}%
+  }
+\long\def\ekv at optarg\ekv at mark[\ekv at mark\ifnum`##1\fi\@firstofone##2##3##4##5]%
+  {#2##2{##5}}
+\long\def\ekv at optargTF##1\ekv at mark[\ekv at mark\ifnum`##2\fi\@firstofone##3##4##5]%
+  {#2\ekv at zero##1{##5}}
+}
+\ekv at exparg{\expandafter\ekvoptarg\expandafter{\ekv at alignsafe}}\ekv at endalignsafe
+\long\def\ekv at optarg@if#1\ekv at mark[\ekv at mark{}
+\def\ekv at err@cleanup\par{}
+\def\ekv at err@collect#1%
+  {%
+    \def\ekv at err@collect##1\par##2%
+      {%
+        \expandafter
+        \ekv at err@cleanup
+        #1! ##2 Error: ##1\par
+      }%
+    \def#1##1\thanks at jfbu{}%
+  }
+\def\ekverr{ }
+\expandafter\ekv at err@collect\csname <an-expandable-macro>^^J%
+  completed due to above exception. \ekverr If the error^^J%
+  summary is \ekverr not comprehensible \ekverr see the package^^J%
+  documentation.^^J%
+  I will try to recover now. \ekverr If you're in inter-^^J%
+  active mode hit <return> \ekverr at the ? prompt and I^^J%
+  continue hoping recovery\endcsname
+\long\def\ekverr#1#2{\expandafter\ekv at err@collect\detokenize{#2}\par{#1}}
+\ekv at exparg{\long\def\ekv at err#1}{\ekverr{expkv}{#1}}
 \long\def\ekv at err@common #1#2{\expandafter\ekv at err@common@\string#2{#1}}
 \ekv at exparg{\long\def\ekv at err@common@#1`#2' #3.#4#5}%
-  {\ekv at err{#4 (`#5', set `#2')}}
+  {\ekv at err{#4 `#5' in set `#2'}}
 \ekv at exparg{\long\def\ekv at err@unknown#1}{\ekv at err@common{unknown key}{#1}}
-\ekv at exparg{\long\def\ekv at err@noarg  #1}{\ekv at err@common{value forbidden}{#1}}
-\ekv at exparg{\long\def\ekv at err@reqval #1}{\ekv at err@common{value required}{#1}}
+\ekv at exparg{\long\def\ekv at err@noarg  #1}{\ekv at err@common{unwanted value for}{#1}}
+\ekv at exparg{\long\def\ekv at err@reqval #1}{\ekv at err@common{missing value for}{#1}}
 \ekv at exparg{\long\def\ekv at err@redirect at kv@notfound#1#2#3\ekv at stop}%
   {\ekv at err{no key `#2' in sets #3}}
 \ekv at exparg{\def\ekv at err@redirect at k@notfound#1#2\ekv at stop}%



More information about the tex-live-commits mailing list.