From 07dc96bc3bbd691fc52aca15a172eaf41a89d873 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Tue, 25 May 2021 03:01:10 +0000 Subject: CTAN sync 202105250301 --- macros/generic/expkv-cs/README.md | 2 +- macros/generic/expkv-cs/expkv-cs.dtx | 290 ++++++++++++++--- macros/generic/expkv-cs/expkv-cs.pdf | Bin 346046 -> 363784 bytes macros/generic/expkv-def/README.md | 2 +- macros/generic/expkv-def/expkv-def.dtx | 149 +++++++-- macros/generic/expkv-def/expkv-def.pdf | Bin 390122 -> 406256 bytes macros/generic/expkv/README.md | 2 +- macros/generic/expkv/expkv.dtx | 559 ++++++++++++++++++++++++++------- macros/generic/expkv/expkv.pdf | Bin 444584 -> 481077 bytes 9 files changed, 816 insertions(+), 188 deletions(-) (limited to 'macros/generic') diff --git a/macros/generic/expkv-cs/README.md b/macros/generic/expkv-cs/README.md index 5b56601a5d..a9df8b801f 100644 --- a/macros/generic/expkv-cs/README.md +++ b/macros/generic/expkv-cs/README.md @@ -1,7 +1,7 @@ ------------------------------------------------------------------------------- # expkv-cs -- define expandable key=val macros using expkv -Version 2021-04-12 v0.8 +Version 2021-05-24 v0.9 Released under the LaTeX Project Public License v1.3c or later See http://www.latex-project.org/lppl.txt diff --git a/macros/generic/expkv-cs/expkv-cs.dtx b/macros/generic/expkv-cs/expkv-cs.dtx index 5847a4980b..38a70349d7 100644 --- a/macros/generic/expkv-cs/expkv-cs.dtx +++ b/macros/generic/expkv-cs/expkv-cs.dtx @@ -64,6 +64,19 @@ and the derived files expkv-cs.pdf \RequirePackage[oldstylenums,nott]{kpfonts} \input{glyphtounicode} \pdfgentounicode=1 +\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 + } \RequirePackage{listings} \RequirePackage{booktabs} \RequirePackage{array} @@ -73,26 +86,127 @@ and the derived files expkv-cs.pdf \RequirePackage{caption} \RequirePackage{microtype} \RequirePackage{accsupp} +\lstdefinelanguage{expkvd}[primitive]{TeX} + { + ,moretexcs=[4]^^A e-TeX + { + expanded, + numexpr, + protected, + } + ,moretexcs=[5]^^A plain/LaTeX + { + approx, + begin, + empty, + item, + LaTeX, + makeatletter,makeatother,multicolumn, + 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-cs macros + { + ekvcSplit,ekvcSplitAndForward,ekvcSplitAndUse, + ekvcHash,ekvcHashAndForward,ekvcHashAndUse, + ekvcValue,ekvcValueFast,ekvcValueSplit,ekvcValueSplitFast, + ekvcSecondaryKeys, + ekvcFlagNew, + ekvcFlagHeight, + ekvcFlagRaise, + ekvcFlagSetTrue,ekvcFlagSetFalse, + ekvcFlagIf,ekvcFlagIfRaised, + ekvcFlagReset, + ekvcFlagGetHeight,ekvcFlagGetHeights + } + ,moretexcs=[3]^^A expkv-pkg macros + { + ^^A expkv + 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, + ^^A expkv-def + ekvdefinekeys + } + } +\colorlet{codeparam}{cyan!65!black} +\def\mylstwd{.55em} \lstset { + ,language=expkvd ,flexiblecolumns=false - ,basewidth=.53em + ,basewidth=\mylstwd ,gobble=2 ,basicstyle=\fontfamily{jkp}\itshape - ,morekeywords=^^A - {^^A - \ekvcSplit,\ekvcSplitAndForward,\ekvcSplitAndUse - \ekvcHash,\ekvcHashAndForward,\ekvcHashAndUse - \ekvcValue,\ekvcValueFast, - \ekvcSecondaryKeys - } - ,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} + {}{{$\langle$}key{$\rangle$}}{5} + {}{{$\langle$}set{$\rangle$}}{5} + {}{{$\langle$}set1{$\rangle$}}{6} + {}{{$\langle$}set2{$\rangle$}}{6} ^^A,literate=*{}{\key}{4}{}{\set}{4} } -\newcommand*\CodeSymbol[1]{\kern-1pt\textbf{#1}\kern1pt} +\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}}} @@ -705,7 +819,7 @@ and the derived files expkv-cs.pdf % alias pos = possessive, % nmeta me = % { -% name=Jonathan P. Spratte, +% name=Jonathan, % age=a young, % nationality=Germany, % hobby=\TeX\ coding, @@ -738,15 +852,54 @@ and the derived files expkv-cs.pdf % As one might see, the |lady| key could actually have been an |nmeta| key as % well, as all that is done with the argument is using it as a \kv\ list. % -% Using \pkg{xparse} and forwarding arguments one can easily define \kv\ macros -% with actual optional and mandatory arguments as well. A small nonsense example -% (which should perhaps use |\ekvcSplitAndForward| instead of -% |\ekvcHashAndForward| since it only uses four keys and one other argument -- -% and isn't expandable since it uses a \env{tabular} environment): +% The result of all the first two usages would be: +% \begin{exresult}{\linewidth} +% \ekvcSplit\character +% { +% name=John Doe, +% age=any, +% nationality=the Universe, +% hobby=to exist, +% type=Mister, +% pronoun=He, +% possessive=his, +% } +% {#1 is a #5 from #3. #6 is of #2 age and #7 hobby is #4.\par} +% \ekvcSecondaryKeys\character +% { +% alias pro = pronoun, +% alias pos = possessive, +% nmeta me = +% { +% name=Jonathan, +% age=a young, +% nationality=Germany, +% hobby=\TeX\ coding, +% }, +% meta lady = +% {type=Lady, pronoun=She, possessive=her, name=Jane Doe, #1}, +% nmeta paulo = +% { +% name=Paulo, +% type=duck, +% age=a young, +% nationality=Brazil, +% hobby=to quack, +% } +% } +% \character{} +% \character{me} +% \end{exresult} +% +% Using \pkg{xparse} or \expkv's \cs{ekvoptarg} or \cs{ekvoptargTF} and +% forwarding arguments one can easily define \kv\ macros with actual optional +% and mandatory arguments as well. A small nonsense example (which should +% perhaps use |\ekvcSplitAndForward| instead of |\ekvcHashAndForward| since it +% only uses four keys and one other argument -- and isn't expandable since it +% uses a \env{tabular} environment): % \begin{lstlisting} -% \usepackage{xparse} % \makeatletter -% \NewExpandableDocumentCommand\nonsense{O{} m}{\nonsense@a{#1}{#2}} +% \newcommand*\nonsense{\ekvoptarg\nonsense@a{}} % \ekvcHashAndForward\nonsense@a\nonsense@b % { % keyA = A, @@ -762,17 +915,44 @@ and the derived files expkv-cs.pdf % & C & \ekvcValue{keyC}{#1} \\ % & D & \ekvcValue{keyD}{#1} \\ % \multicolumn{2}{l}{mandatory} & #2 \\ -% \end{tabular}\par +% \end{tabular}% % } % \makeatother % \end{lstlisting} -% And then we would be able to do some nonsense: +% And then we would be able to do some nonsense % \begin{lstlisting} % \nonsense{} % \nonsense[keyA=hihi]{haha} % \nonsense[keyA=hihi, keyB=A]{hehe} % \nonsense[keyC=huhu, keyA=hihi, keyB=A]{haha} % \end{lstlisting} +% resulting in +% \begin{exresult}{\linewidth} +% \makeatletter +% \newcommand*\nonsense{\ekvoptarg\nonsense@a{}} +% \ekvcHashAndForward\nonsense@a\nonsense@b +% { +% keyA = A, +% keyB = B, +% keyC = c, +% keyD = d, +% } +% \newcommand*\nonsense@b[2] +% {% +% \begin{tabular}{lll} +% key & A & \ekvcValue{keyA}{#1} \\ +% & B & \ekvcValue{keyB}{#1} \\ +% & C & \ekvcValue{keyC}{#1} \\ +% & D & \ekvcValue{keyD}{#1} \\ +% \multicolumn{2}{l}{mandatory} & #2 \\ +% \end{tabular}% +% } +% \makeatother +% \nonsense{} +% \nonsense[keyA=hihi]{haha} +% \nonsense[keyA=hihi, keyB=A]{hehe} +% \nonsense[keyC=huhu, keyA=hihi, keyB=A]{haha} +% \end{exresult} % % % \subsection{Speed Considerations}\label{sec:speed} @@ -945,8 +1125,8 @@ and the derived files expkv-cs.pdf % \begin{macro}{\ekvcVersion,\ekvcDate} % We're on our first input, so lets store the version and date in a macro. % \begin{macrocode} -\def\ekvcVersion{0.8} -\def\ekvcDate{2021-04-12} +\def\ekvcVersion{0.9} +\def\ekvcDate{2021-05-24} % \end{macrocode} % \end{macro} % @@ -993,8 +1173,26 @@ and the derived files expkv-cs.pdf % Some macros will have to be defined long. These two will be let to |\long| % when this should be the case. % \begin{macrocode} -\def\ekvc@long{} -\def\ekvc@any@long{} +\let\ekvc@long\ekv@empty +\let\ekvc@any@long\ekv@empty +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\ekvc@ifdefined} +% We want to test whether a macro is already defined. This test checks for a +% defined macro that isn't |\relax|. +% \begin{macrocode} +\long\def\ekvc@ifdefined#1% + {% + \ifdefined#1% + \ifx\relax#1% + \ekv@fi@gobble + \fi + \@firstofone + \ekv@fi@firstoftwo + \fi + \@secondoftwo + } % \end{macrocode} % \end{macro} % @@ -1009,7 +1207,8 @@ and the derived files expkv-cs.pdf % a little magic trick to get the macro parameter |#1| for the macro % definition this is used in, even though we're calling |\unexpanded|. We do % that by splitting the expanded |\ekvset| at some marks and place |##1| in -% between. +% between. At this spot we also add |\ekv@alignsafe| and |\ekv@endalignsafe| +% to ensure that macros created with \expkvc\ are alignment safe. % \begin{macrocode} \def\ekvc@ekvset@pre@expander#1% {% @@ -1021,7 +1220,9 @@ and the derived files expkv-cs.pdf } \def\ekvc@ekvset@pre@expander@b#1\ekvc@stop#2\ekvc@stop {% + \unexpanded\expandafter{\ekv@alignsafe}% \unexpanded{#1}##1\unexpanded{#2}% + \unexpanded\expandafter{\ekv@endalignsafe}% } % \end{macrocode} % \end{macro} @@ -1035,7 +1236,7 @@ and the derived files expkv-cs.pdf \protected\long\def\ekvcSplitAndUse#1#2% {% \let\ekvc@helpers@needed\@firstoftwo - \ekv@ifdefined{\expandafter\@gobble\string#1}% + \ekvc@ifdefined#1% {\ekvc@err@already@defined#1}% {\ekvcSplitAndUse@#1{}{#2}}% } @@ -1083,7 +1284,7 @@ and the derived files expkv-cs.pdf \protected\long\def\ekvcSplitAndForward#1#2#3% {% \let\ekvc@helpers@needed\@firstoftwo - \ekv@ifdefined{\expandafter\@gobble\string#1}% + \ekvc@ifdefined#1% {\ekvc@err@already@defined#1}% {\ekvcSplitAndUse@#1{{#2}}{#3}}% } @@ -1098,7 +1299,7 @@ and the derived files expkv-cs.pdf \protected\long\def\ekvcSplit#1#2#3% {% \let\ekvc@helpers@needed\@secondoftwo - \ekv@ifdefined{\expandafter\@gobble\string#1}% + \ekvc@ifdefined#1% {\ekvc@err@already@defined#1}% {% \expandafter @@ -1154,8 +1355,8 @@ and the derived files expkv-cs.pdf \protected\long\def\ekvc@SetupSplitKeys {% \ekvc@keycount=0 - \def\ekvc@any@long{}% - \def\ekvc@initials{}% + \let\ekvc@any@long\ekv@empty + \let\ekvc@initials\ekv@empty \ekvparse\ekvc@err@value@required\ekvc@SetupSplitKeys@a } % \end{macrocode} @@ -1165,7 +1366,7 @@ and the derived files expkv-cs.pdf \protected\def\ekvc@SetupSplitKeys@a#1% {% \advance\ekvc@keycount1 - \def\ekvc@long{}% + \let\ekvc@long\ekv@empty \ekvc@ifspace{#1}% {\ekvc@SetupSplitKeys@b#1\ekvc@stop}% {\ekvc@SetupSplitKeys@c{#1}}% @@ -1355,7 +1556,7 @@ and the derived files expkv-cs.pdf \protected\long\def\ekvcHashAndUse#1#2% {% \let\ekvc@helpers@needed\@firstoftwo - \ekv@ifdefined{\expandafter\@gobble\string#1}% + \ekvc@ifdefined#1% {\ekvc@err@already@defined#1}% {\ekvcHashAndUse@#1{}{#2}}% } @@ -1398,7 +1599,7 @@ and the derived files expkv-cs.pdf \protected\long\def\ekvcHashAndForward#1#2#3% {% \let\ekvc@helpers@needed\@firstoftwo - \ekv@ifdefined{\expandafter\@gobble\string#1}% + \ekvc@ifdefined#1% {\ekvc@err@already@defined#1}% {\ekvcHashAndUse@#1{{#2}}{#3}}% } @@ -1413,7 +1614,7 @@ and the derived files expkv-cs.pdf \protected\long\def\ekvcHash#1#2#3% {% \let\ekvc@helpers@needed\@secondoftwo - \ekv@ifdefined{\expandafter\@gobble\string#1}% + \ekvc@ifdefined#1% {\ekvc@err@already@defined#1}% {% \expandafter @@ -1441,13 +1642,13 @@ and the derived files expkv-cs.pdf % \begin{macrocode} \protected\long\def\ekvc@SetupHashKeys#1% {% - \def\ekvc@any@long{}% - \def\ekvc@initials{}% + \let\ekvc@any@long\ekv@empty + \let\ekvc@initials\ekv@empty \ekvparse\ekvc@err@value@required\ekvc@SetupHashKeys@a{#1}% } \protected\def\ekvc@SetupHashKeys@a#1% {% - \def\ekvc@long{}% + \let\ekvc@long\ekv@empty \ekvc@ifspace{#1}% {\ekvc@SetupHashKeys@b#1\ekvc@stop}% {\ekvc@SetupHashKeys@c{#1}}% @@ -1687,7 +1888,7 @@ and the derived files expkv-cs.pdf } \protected\def\ekvcSecondaryKeys@a#1% {% - \def\ekvc@long{}% + \let\ekvc@long\ekv@empty \ekvc@ifspace{#1}% {\ekvcSecondaryKeys@b#1\ekvc@stop}% {\ekvc@err@missing@type{#1}\@gobble}% @@ -2137,15 +2338,10 @@ and the derived files expkv-cs.pdf % \end{macrocode} % \end{macro} % -% \begin{macro}[internal]{\ekvc@err,\ekvc@err@} +% \begin{macro}[internal]{\ekvc@err} % We need a way to throw error messages expandably in some contexts. % \begin{macrocode} -\def\ekvc@err#1% - {% - \long\def\ekvc@err##1{\expandafter\ekv@err@\@firstofone{#1##1.}\ekv@stop}% - } -\begingroup\expandafter\endgroup -\expandafter\ekvc@err\csname ! expkv-cs Error:\endcsname +\ekv@exparg{\long\def\ekvc@err#1}{\ekverr{expkv-cs}{#1}} % \end{macrocode} % \end{macro} % diff --git a/macros/generic/expkv-cs/expkv-cs.pdf b/macros/generic/expkv-cs/expkv-cs.pdf index f498bcb298..50dd5124bf 100644 Binary files a/macros/generic/expkv-cs/expkv-cs.pdf and b/macros/generic/expkv-cs/expkv-cs.pdf differ diff --git a/macros/generic/expkv-def/README.md b/macros/generic/expkv-def/README.md index f2c9e9cfac..34a6e05db4 100644 --- a/macros/generic/expkv-def/README.md +++ b/macros/generic/expkv-def/README.md @@ -1,7 +1,7 @@ ------------------------------------------------------------------------------- # expkv-def -- a key-defining frontend for expkv -Version 2021-04-10 v0.8 +Version 2021-05-24 v0.8a Released under the LaTeX Project Public License v1.3c or later See http://www.latex-project.org/lppl.txt diff --git a/macros/generic/expkv-def/expkv-def.dtx b/macros/generic/expkv-def/expkv-def.dtx index eaec17b22c..b69c591a00 100644 --- a/macros/generic/expkv-def/expkv-def.dtx +++ b/macros/generic/expkv-def/expkv-def.dtx @@ -64,6 +64,19 @@ and the derived files expkv-def.pdf \RequirePackage[oldstylenums,nott]{kpfonts} \input{glyphtounicode} \pdfgentounicode=1 +\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 + } \RequirePackage{listings} \RequirePackage{booktabs} \RequirePackage{array} @@ -73,23 +86,117 @@ and the derived files expkv-def.pdf \RequirePackage{caption} \RequirePackage{microtype} \RequirePackage{accsupp} +\lstdefinelanguage{expkvd}[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-def macros + { + ekvdefinekeys + } + ,moretexcs=[3]^^A expkv-pkg macros + { + ^^A expkv + 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, + ^^A expkv-cs + ekvcSecondaryKeys,ekvcSplitAndForward, + } + } +\colorlet{codeparam}{cyan!65!black} +\def\mylstwd{.55em} \lstset { + ,language=expkvd ,flexiblecolumns=false - ,basewidth=.53em + ,basewidth=\mylstwd ,gobble=2 ,basicstyle=\fontfamily{jkp}\itshape - ,morekeywords=^^A - {^^A - \ekvdefinekeys - } - ,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} + {}{{$\langle$}key{$\rangle$}}{5} + {}{{$\langle$}set{$\rangle$}}{5} + {}{{$\langle$}set1{$\rangle$}}{6} + {}{{$\langle$}set2{$\rangle$}}{6} ^^A,literate=*{}{\key}{4}{}{\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}}} @@ -511,7 +618,7 @@ and the derived files expkv-def.pdf % \begin{syntax} % toks \key\ = \meta{cs} \prefixes2212 % \end{syntax} -% The \meta{cs} should be a single control sequence, such as |\foo|. +% The \meta{cs} should be a single control sequence, such as |\foo|. % Store \val\ inside of a toks-register. The |g| variants use |\global|, the % |app| variants append \val\ to the contents of that register. If \meta{cs} % is not yet defined it will be initialised with |\newtoks|. @@ -730,7 +837,7 @@ and the derived files expkv-def.pdf % in Italics. If a person has no hobbies the description item will be silently % left out. The result of the above code looks like this: % -% \begingroup +% \begin{exresult}{\linewidth} % \ekvdefinekeys{ex} % { % data name = \Pname @@ -751,7 +858,7 @@ and the derived files expkv-def.pdf % \Person{name=Jonathan P. Spratte, age=young, hobby=\TeX\ coding} % \Person{name=Some User, age=unknown, hobby=Reading Documentation} % \Person{name=Anybody, age=any} -% \endgroup +% \end{exresult} % % \subsection{License} % @@ -824,8 +931,8 @@ and the derived files expkv-def.pdf % \begin{macro}{\ekvdVersion,\ekvdDate} % We're on our first input, so lets store the version and date in a macro. % \begin{macrocode} -\def\ekvdVersion{0.8} -\def\ekvdDate{2021-04-10} +\def\ekvdVersion{0.8a} +\def\ekvdDate{2021-05-24} % \end{macrocode} % \end{macro} % @@ -2075,7 +2182,7 @@ and the derived files expkv-def.pdf }% } \protected\def\ekvd@err@not@new - {\ekvd@errm{The key for `\ekvd@cur' is already defined}} + {\ekvd@errm{The key for `\ekvd@cur' is already defined}} \protected\long\def\ekvd@err@misused@unknown {\ekvd@errm{Misuse of the unknown type found while processing `\ekvd@cur'}} % \end{macrocode} @@ -2111,7 +2218,7 @@ and the derived files expkv-def.pdf {% \ekv@ifdefined{\ekvd@unknown@choice@name{#1}{#2}}% {\csname\ekvd@unknown@choice@name{#1}{#2}\endcsname{#3}}% - {\ekvd@err{invalid choice `#3' (`#2', set `#1')}}% + {\ekvd@err{invalid choice `#3' for `#2' in set `#1'}}% } } % \end{macrocode} @@ -2119,15 +2226,9 @@ and the derived files expkv-def.pdf % % \begin{macro}[internal]{\ekvd@err} % The expandable error messages use |\ekvd@err|, which is just like |\ekv@err| -% from \expkv\ or the way \pkg{expl3} throws expandable error messages. It uses -% an undefined control sequence to start the error message. +% from \expkv. It uses a runaway argument to start the error message. % \begin{macrocode} -\def\ekvd@err#1% - {% - \long\def\ekvd@err##1{\expandafter\ekv@err@\@firstofone{#1##1.}\ekv@stop}% - } -\begingroup\expandafter\endgroup -\expandafter\ekvd@err\csname ! expkv-def Error:\endcsname +\ekv@exparg{\long\def\ekvd@err#1}{\ekverr{expkv-def}{#1}} % \end{macrocode} % \end{macro} % diff --git a/macros/generic/expkv-def/expkv-def.pdf b/macros/generic/expkv-def/expkv-def.pdf index b483ade6c1..8acb39cdb7 100644 Binary files a/macros/generic/expkv-def/expkv-def.pdf and b/macros/generic/expkv-def/expkv-def.pdf differ diff --git a/macros/generic/expkv/README.md b/macros/generic/expkv/README.md index 512cd829ca..adc545fd80 100644 --- a/macros/generic/expkv/README.md +++ b/macros/generic/expkv/README.md @@ -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 diff --git a/macros/generic/expkv/expkv.dtx b/macros/generic/expkv/expkv.dtx index 553065f32b..37a67fc781 100644 --- a/macros/generic/expkv/expkv.dtx +++ b/macros/generic/expkv/expkv.dtx @@ -76,34 +76,130 @@ and the derived files expkv.pdf \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} + {}{{$\langle$}key{$\rangle$}}{5} + {}{{$\langle$}set{$\rangle$}}{5} + {}{{$\langle$}set1{$\rangle$}}{6} + {}{{$\langle$}set2{$\rangle$}}{6} ^^A,literate=*{}{\key}{4}{}{\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 @@ and the derived files expkv.pdf % 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,11 +666,15 @@ and the derived files expkv.pdf % 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 @@ -576,14 +682,6 @@ and the derived files expkv.pdf % 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 @@ and the derived files expkv.pdf % 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 @@ and the derived files expkv.pdf % 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@secretly@sneaked|: % \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] % \ekvdefNoVal{foo}{secret}{\ekvsneak{\foo@secretly@sneaked}} @@ -696,6 +794,124 @@ and the derived files expkv.pdf % {\ekvifdefinedset{#1}{\ekvchangeset{#1}}{\ekvbreak{\foo@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@a\foo@b} +% \newcommand\foo@a[2]{Mandatory: #2\par Optional: #1} +% \newcommand\foo@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@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 \ +% 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 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 \ +% 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 at the ? prompt and I +% continue hoping recovery was complete. +% +% \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@name,\ekv@name@set,\ekv@name@key} @@ -744,17 +960,18 @@ and the derived files expkv.pdf % \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 % \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 @@ and the derived files expkv.pdf % \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 @@ and the derived files expkv.pdf % % 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@ht{1ex} @@ -794,12 +1011,14 @@ and the derived files expkv.pdf % \protected\ekvdef{myrule}{ht}{\def\myrule@ht{#1}} % \protected\ekvdef{myrule}{wd}{\def\myrule@wd{#1}} % \protected\ekvdef{myrule}{raise}{\def\myrule@raise{#1}} +% \protected\ekvdef{myrule}{lower}{\def\myrule@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@out\endgroup} +% \newcommand*\myrule[1][] +% {\begingroup\myruleset{#1}\myrule@out\endgroup} % \end{lstlisting} % And finally the output: % \begin{lstlisting} @@ -807,15 +1026,15 @@ and the derived files expkv.pdf % \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@ht{1ex} % \newcommand*\myrule@wd{0.1em} @@ -823,16 +1042,17 @@ and the derived files expkv.pdf % \protected\ekvdef{myrule}{ht}{\def\myrule@ht{#1}} % \protected\ekvdef{myrule}{wd}{\def\myrule@wd{#1}} % \protected\ekvdef{myrule}{raise}{\def\myrule@raise{#1}} +% \protected\ekvdef{myrule}{lower}{\def\myrule@raise{-#1}} % \ekvsetdef\myruleset{myrule} % \newcommand*\myrule[1][]{\begingroup\myruleset{#1}\myrule@out\endgroup} % \newcommand*\myrule@out{\rule[\myrule@raise]\myrule@wd\myrule@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 @@ and the derived files expkv.pdf % 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@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 @@ and the derived files expkv.pdf % \ekvdefNoVal{expex}{degree}{\ekvsneakPre{\deg{d}}} % \ekvdefNoVal{expex}{radian}{\ekvsneakPre{\deg{}}} % \ekvdefNoVal{expex}{internal}{\ekvsneakPre{\sine@rnd}} -% \newcommand*\sine[2] +% \newcommand*\sine{\ekvoptargTF\sine@args{\sine@final{sin}{d}{3}}} +% \newcommand*\sine@args[2] % {\ekvset{expex}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine@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,33 +1142,34 @@ and the derived files expkv.pdf % \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 % \ekvdefNoVal{expex}{degree}{\ekvsneakPre{\deg{d}}}^^A % \ekvdefNoVal{expex}{radian}{\ekvsneakPre{\deg{}}}^^A % \ekvdefNoVal{expex}{internal}{\ekvsneakPre{\sine@rnd}}^^A -% \newcommand*\sine[2] -% {\ekvset{expex}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine@stop{#2}}^^A +% \newcommand*\sine{\ekvoptargTF\sine@args{\sine@final{sin}{d}{3}}} +% \newcommand*\sine@args[2] +% {\ekvset{expex}{#1,internal}\rnd{3}\deg{d}\f{sin}\sine@stop{#2}} % \def\sine@rnd#1\rnd#2#3\sine@stop{\sine@deg#1#3\sine@stop{#2}}^^A % \def\sine@deg#1\deg#2#3\sine@stop{\sine@f#1#3\sine@stop{#2}}^^A % \def\sine@f#1\f#2#3\sine@stop{\sine@final{#2}}^^A % \newcommand*\sine@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 @@ and the derived files expkv.pdf % way easier to code for the user. % \begin{lstlisting} % \makeatletter -% \ekvcSplitAndForward\sine\sine@ +% \newcommand*\sine{\ekvoptargTF\sine@a{\sine@b{sin}{d}{3}}} +% \ekvcSplitAndForward\sine@a\sine@b % { % f=sin, % unit=d, % round=3, % } -% \ekvcSecondaryKeys\sine +% \ekvcSecondaryKeys\sine@a % { % nmeta degree={unit=d}, % nmeta radian={unit={}}, % } -% \newcommand*\sine@[4]{\fpeval{round(#1#2(#4),#3)}} +% \newcommand*\sine@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 @@ and the derived files expkv.pdf % % \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,57 +1232,52 @@ and the derived files expkv.pdf % \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|| gets replaced with the problematic key's -% name, and \lstinline|| 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|| gets replaced with the problematic key's name, and +% \lstinline|| 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. -% \! expkv Error: -% unknown key (`', set `'). +% Runaway argument? +% ! expkv Error: unknown key `' in 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. -% \! expkv Error: -% value required (`', set `'). +% Runaway argument? +% ! expkv Error: missing value for `' in 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. -% \! expkv Error: -% value forbidden (`', set `'). +% Runaway argument? +% ! expkv Error: unwanted value for `' in 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. -% \! expkv Error: -% no key `' in sets {}{}... +% Runaway argument? +% ! expkv Error: no key `' in sets {}{}... % \end{lstlisting} % % If you're using an undefined |NoVal| key in a set for which % |\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. -% \! expkv Error: -% no NoVal key `' in sets {}... +% Runaway argument? +% ! expkv Error: no NoVal key `' in sets {}{}... % \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} +% \begin{lstlisting}[language={}] % ! Missing \endcsname inserted. % % \! expkv Error: Set `' undefined. @@ -1197,8 +1414,8 @@ and the derived files expkv.pdf % 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 @@ and the derived files expkv.pdf \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 @@ and the derived files expkv.pdf % \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 @@ and the derived files expkv.pdf % 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@parse{##1}{##2}\ekv@mark##3#1\ekv@stop#1} +\long\def\ekvparse##1##2##3% + {\unexpanded\expanded{{\ekv@parse{##1}{##2}\ekv@mark##3#1\ekv@stop#1}}} % \end{macrocode} % \end{macro} % @@ -2440,18 +2665,124 @@ and the derived files expkv.pdf % \end{macrocode} % \end{macro} % -% \begin{macro}[internal]{\ekv@err,\ekv@err@} +% \begin{macro}[internal]{\ekv@alignsafe,\ekv@endalignsafe} +% These macros protect the usage of ampersands inside of alignment contexts. +% \begin{macrocode} +\begingroup +\catcode`\^^@=2 +\@firstofone{\endgroup + \def\ekv@alignsafe{\romannumeral\iffalse{\fi`^^@ } +} +\def\ekv@endalignsafe{\ifnum`{=\ekv@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@alignsafe| (will be +% |#1|) and |\ekv@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@optarg@if\ekv@mark##3\ekv@mark\ekv@optarg\ekv@mark[\ekv@mark + #2% + \@firstofone{\ekv@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@optarg@if\ekv@mark##3\ekv@mark\ekv@optargTF{##1}\ekv@mark[\ekv@mark + #2% + \@firstofone{\ekv@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@optarg\ekv@mark[\ekv@mark\ifnum`##1\fi\@firstofone##2##3##4##5]% + {#2##2{##5}} +\long\def\ekv@optargTF##1\ekv@mark[\ekv@mark\ifnum`##2\fi\@firstofone##3##4##5]% + {#2\ekv@zero##1{##5}} +} +% \end{macrocode} +% Do the definitions and add the test macro. +% \begin{macrocode} +\ekv@exparg{\expandafter\ekvoptarg\expandafter{\ekv@alignsafe}}\ekv@endalignsafe +\long\def\ekv@optarg@if#1\ekv@mark[\ekv@mark{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ekverr} +% \begin{macro}[internal]{\ekv@err@collect,\ekv@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. -% \begin{macrocode} -\def\ekv@err#1% +% 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@err@collect|. This macro has the drawback that it will only print nicely +% if the |\newlinechar| is |^^J|. +% \begin{macrocode} +\def\ekv@err@cleanup\par{} +\def\ekv@err@collect#1% {% - \long\def\ekv@err##1{\expandafter\ekv@err@\@firstofone{#1##1.}\ekv@stop}% + \def\ekv@err@collect##1\par##2% + {% + \expandafter + \ekv@err@cleanup + #1! ##2 Error: ##1\par + }% + \def#1##1\thanks@jfbu{}% } -\begingroup\expandafter\endgroup -\expandafter\ekv@err\csname ! expkv Error:\endcsname -\def\ekv@err@{\expandafter\ekv@gobbleto@stop} +\def\ekverr{ } +\expandafter\ekv@err@collect\csname ^^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 \ekverr at the ? prompt and I^^J% + continue hoping recovery\endcsname +% \end{macrocode} +% \begin{macrocode} +\long\def\ekverr#1#2{\expandafter\ekv@err@collect\detokenize{#2}\par{#1}} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@err} +% We define a shorthand to throw errors in \expkv. +% \begin{macrocode} +\ekv@exparg{\long\def\ekv@err#1}{\ekverr{expkv}{#1}} % \end{macrocode} % \end{macro} % @@ -2465,10 +2796,10 @@ and the derived files expkv.pdf % \begin{macrocode} \long\def\ekv@err@common #1#2{\expandafter\ekv@err@common@\string#2{#1}} \ekv@exparg{\long\def\ekv@err@common@#1`#2' #3.#4#5}% - {\ekv@err{#4 (`#5', set `#2')}} + {\ekv@err{#4 `#5' in set `#2'}} \ekv@exparg{\long\def\ekv@err@unknown#1}{\ekv@err@common{unknown key}{#1}} -\ekv@exparg{\long\def\ekv@err@noarg #1}{\ekv@err@common{value forbidden}{#1}} -\ekv@exparg{\long\def\ekv@err@reqval #1}{\ekv@err@common{value required}{#1}} +\ekv@exparg{\long\def\ekv@err@noarg #1}{\ekv@err@common{unwanted value for}{#1}} +\ekv@exparg{\long\def\ekv@err@reqval #1}{\ekv@err@common{missing value for}{#1}} \ekv@exparg{\long\def\ekv@err@redirect@kv@notfound#1#2#3\ekv@stop}% {\ekv@err{no key `#2' in sets #3}} \ekv@exparg{\def\ekv@err@redirect@k@notfound#1#2\ekv@stop}% diff --git a/macros/generic/expkv/expkv.pdf b/macros/generic/expkv/expkv.pdf index 95b8d60b57..1f9248c9ec 100644 Binary files a/macros/generic/expkv/expkv.pdf and b/macros/generic/expkv/expkv.pdf differ -- cgit v1.2.3