From 6cf29a8f570cdc92864dd567023f539dd2667527 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Fri, 16 Jul 2021 19:56:17 +0000 Subject: expkv (16jul21) git-svn-id: svn://tug.org/texlive/trunk@59949 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/source/latex/expkv/expkv.dtx | 169 +++++++++++++++++++------ 1 file changed, 128 insertions(+), 41 deletions(-) (limited to 'Master/texmf-dist/source/latex/expkv') diff --git a/Master/texmf-dist/source/latex/expkv/expkv.dtx b/Master/texmf-dist/source/latex/expkv/expkv.dtx index 283cd0353db..da6fcf2542f 100644 --- a/Master/texmf-dist/source/latex/expkv/expkv.dtx +++ b/Master/texmf-dist/source/latex/expkv/expkv.dtx @@ -421,7 +421,7 @@ and the derived files expkv.pdf % \end{function} % \example Define |text| in |foo| to store the value inside |\foo@text|: % \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] -% \protected\long\ekvdef{foo}{text}{\def\foo@width{#1}} +% \protected\long\ekvdef{foo}{text}{\def\foo@text{#1}} % \end{lstlisting} % % \begin{function}{\ekvdefNoVal} @@ -440,7 +440,8 @@ and the derived files expkv.pdf % \cs{ekvlet}\marg{set}\marg{key}\meta{cs} % \end{syntax} % Let the value taking \key\ in \set\ to \meta{cs}, there are no checks on -% \meta{cs} enforced. +% \meta{cs} enforced, but the code should expect the value as a single braced +% argument directly following it. % \end{function} % \example Let |cmd| in |foo| do the same as |\foo@cmd|: % \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] @@ -811,7 +812,8 @@ and the derived files expkv.pdf % 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). +% the mandatory argument will be wrapped in braces after being read once -- if +% it was already wrapped it is effectively unchanged). % \end{function} % |\ekvoptarg| expands in exactly two steps, grabs all the arguments only at the % second expansion step, and is alignment safe. It has its limitations however. @@ -845,6 +847,43 @@ and the derived files expkv.pdf % \newcommand\foo@b[1]{Mandatory: #1\par No optional.} % \end{lstlisting} % +% \begin{function}{\ekvcsvloop} +% \begin{syntax} +% \cs{ekvcsvloop}\marg{code}\marg{csv-list} +% \end{syntax} +% This loops over the comma separated items in \meta{csv-list} and, after +% stripping spaces from either end of \meta{item} and removing at most one set +% of outer braces, leaves +% \texttt +% {^^A +% \cs[no-index]{unexpanded}\hskip0pt +% \{\hskip0pt +% \meta{code}\hskip0pt +% \marg{item}\hskip0pt +% \}^^A +% } +% for each list item in the input stream. Blank elements are ignored (if you +% need a blank element it should be given as |{}|). It supports both active +% commas and commas of category other. You could consider it as a watered down +% version of |\ekvparse|. However it is not alignment safe, which you could +% achieve by nesting it in |\expanded| (since the braces around the argument +% of |\expanded| will hide |&|s from \TeX's alignment parsing). +% \end{function} +% \example The following splits a comma separated list and prints it in a +% typewriter font with parentheses around each element.\\ +% \begin{minipage}{.75\linewidth} +% \begin{lstlisting} +% \newcommand*\myprocessor[1]{\texttt{(#1)}} +% \ekvcsvloop\myprocessor{abc,def,ghi}\par +% \ekvcsvloop\myprocessor{1,,2,,3,,4}\par +% \end{lstlisting} +% \end{minipage}^^A +% \begin{exresult}[nobeforeafter,box align=center]{.25\linewidth} +% \newcommand*\myprocessor[1]{\texttt{(#1)}} +% \ekvcsvloop\myprocessor{abc,def,ghi}\par +% \ekvcsvloop\myprocessor{1,,2,,3,,4}\par +% \end{exresult} +% % \begin{function}{\ekverr} % \begin{syntax} % \cs{ekverr}\marg{package}\marg{message} @@ -1554,8 +1593,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.8a} -\def\ekvDate{2021-06-03} +\def\ekvVersion{1.9} +\def\ekvDate{2021-07-15} % \end{macrocode} % \end{macro} % @@ -1762,35 +1801,73 @@ and the derived files expkv.pdf % \end{macrocode} % \end{macro} % -% \begin{macro}[internal]{\ekv@csv@loop,\ekv@csv@loop@do,\ekv@csv@loop@end} -% This is just a very simple loop over a list of comma separated values, -% leaving each element as the argument to a specified function inside of -% |\unravel|. It should be used as -% \texttt -% {^^A -% \cs[no-index]{ekv@csv@loop}\hskip0pt^^A -% \marg{function}\hskip0pt^^A -% \cs[no-index]{ekv@mark}\hskip0pt^^A -% \meta{csv-list}\hskip0pt^^A -% ,\cs[no-index]{ekv@stop},^^A -% }. -% We use some |\expandafter| chain to preexpand |\ekv@strip| here. -% \begin{macrocode} -\ekv@exparg{\long\def\ekv@csv@loop#1#2,}% +% \begin{macro}{\ekvcsvloop} +% \begin{macro}[internal]{\ekv@csv@loop@active,\ekv@csv@loop@active@end} +% An |\ekvcsvloop| will just loop over a csv list in a simple manner. First we +% split at active commas (gives better performance this way), next we have to +% check whether we're at the end of the list (checking for |\ekv@stop|). If +% not we go on splitting at commas of category other. +% \begin{macrocode} +\begingroup +\def\ekvcsvloop#1{% +\endgroup +\long\def\ekvcsvloop##1##2% + {\ekv@csv@loop@active{##1}\ekv@mark##2#1\ekv@stop#1} +% \end{macrocode} +% This does the same as |\ekv@csv@loop| but for active commas. +% \begin{macrocode} +\long\def\ekv@csv@loop@active##1##2#1% {% - \expandafter - \ekv@gobble@from@mark@to@stop - \expandafter#\expandafter2\expandafter\ekv@csv@loop@end\expandafter + \ekv@gobble@from@mark@to@stop##2\ekv@csv@loop@active@end\ekv@stop + \ekv@csv@loop{##1}##2,\ekv@stop,% + }% +\long\def\ekv@csv@loop@active@end \ekv@stop - \ekv@strip{#2}{\ekv@csv@loop@do{#1}}% - \ekv@csv@loop{#1}\ekv@mark + \ekv@csv@loop##1\ekv@mark\ekv@stop,\ekv@stop,% + {}% +} +% \end{macrocode} +% Do the definitions with the weird catcode. +% \begin{macrocode} +\catcode`\,=13 +\ekvcsvloop, +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[internal]{\ekv@csv@loop,\ekv@csv@loop@do,\ekv@csv@loop@end} +% We use temporary macros and an |\expandafter| chain to preexpand +% |\ekv@strip| here. After splitting at other commas we check again for end +% the end of the sublist, check for blank elements which should be ignored, +% and else strip spaces and execute the user code (protecting it from further +% expanding with |\unexpanded|). +% \begin{macrocode} +\def\ekv@csv@loop#1% + {% + \long\def\ekv@csv@loop##1##2,% + {% + \ekv@gobble@from@mark@to@stop##2\ekv@csv@loop@end\ekv@stop + \ekv@ifblank@##2\ekv@nil\ekv@ifempty@B\ekv@csv@loop@blank + \ekv@ifempty@A\ekv@ifempty@B + #1\ekv@csv@loop@do{##1}% + }% } -\long\def\ekv@csv@loop@do#1#2{\unexpanded{#1{#2}}} -\long\expandafter\def\expandafter\ekv@csv@loop@end - \expandafter\ekv@stop - \ekv@strip{#1}#2% - \ekv@csv@loop#3\ekv@mark - {} +\expandafter\ekv@csv@loop\expandafter{\ekv@strip{#2}} +\long\def\ekv@csv@loop@do#1#2{\unexpanded{#2{#1}}\ekv@csv@loop{#2}\ekv@mark} +\def\ekv@csv@loop@end#1% + {% + \long\def\ekv@csv@loop@end + \ekv@stop + \ekv@ifblank@\ekv@mark\ekv@stop\ekv@nil\ekv@ifempty@B\ekv@csv@loop@blank + \ekv@ifempty@A\ekv@ifempty@B + #1\ekv@csv@loop@do##1% + {\ekv@csv@loop@active{##1}\ekv@mark}% + } +\expandafter\ekv@csv@loop@end\expandafter{\ekv@strip{\ekv@mark\ekv@stop}} +\long\expandafter\def\expandafter\ekv@csv@loop@blank + \expandafter\ekv@ifempty@A\expandafter\ekv@ifempty@B + \ekv@strip{\ekv@mark#1}\ekv@csv@loop@do#2% + {\ekv@csv@loop{#2}\ekv@mark} % \end{macrocode} % \end{macro} % @@ -1969,9 +2046,9 @@ and the derived files expkv.pdf \begingroup \edef\ekv@tmp {% - \ekv@csv@loop#1\ekv@mark#6,\ekv@stop,% + \ekvcsvloop#1{#6}% \unexpanded{#2}% - {\ekv@csv@loop{}\ekv@mark#5,#6,\ekv@stop,}% + {\ekvcsvloop{}{#5,#6}}% }% \ekv@expargtwice {\endgroup#3{#5}}% @@ -2690,7 +2767,10 @@ and the derived files expkv.pdf % 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|). +% |#1|) and |\ekv@endalignsafe| (will be |#2|). As |\ekv@alignsafe| starts with +% a |\romannumeral| we use that to also control the number of steps needed +% instead of adding another |\romannumeral|. For this we have to remove the +% space token from the end of |\ekv@alignsafe|'s definition. % \begin{macrocode} \begingroup \def\ekvoptarg#1#2{% @@ -2702,24 +2782,24 @@ and the derived files expkv.pdf % bracket, if it is found grab the optional argument, else leave |#1{#2}| in the % input stream after ending the expansion context. % \begin{macrocode} -\def\ekvoptarg{\romannumeral#1\ekv@optarg@a} +\def\ekvoptarg{#1\ekv@optarg@a} \long\def\ekv@optarg@a##1##2##3% {% \ekv@optarg@if\ekv@mark##3\ekv@mark\ekv@optarg@b\ekv@mark[\ekv@mark #2% - \@firstofone{\ekv@zero##1}{##2}{##3}% + \@firstofone{ ##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} -\def\ekvoptargTF{\romannumeral#1\ekv@optargTF@a} +\def\ekvoptargTF{#1\ekv@optargTF@a} \long\def\ekv@optargTF@a##1##2##3% {% \ekv@optarg@if\ekv@mark##3\ekv@mark\ekv@optargTF@b{##1}\ekv@mark[\ekv@mark #2% - \@firstofone{\ekv@zero##2}{##3}% + \@firstofone{ ##2}{##3}% } % \end{macrocode} % The two macros to grab the optional argument have to remove the remainder of @@ -2729,12 +2809,19 @@ and the derived files expkv.pdf {#2##2{##5}} \long\def\ekv@optargTF@b ##1\ekv@mark[\ekv@mark\ifnum`##2\fi\@firstofone##3##4##5]% - {#2\ekv@zero##1{##5}} + {#2 ##1{##5}} } % \end{macrocode} -% Do the definitions and add the test macro. +% Do the definitions and add the test macro. We use |\ekv@strip| to remove the +% trailing space from the definition of |\ekv@alignsafe|. % \begin{macrocode} -\ekv@exparg{\expandafter\ekvoptarg\expandafter{\ekv@alignsafe}}\ekv@endalignsafe +\ekv@exparg + {% + \expandafter\ekv@strip\expandafter + {\expandafter\ekv@mark\ekv@alignsafe}% + \ekvoptarg + }% + \ekv@endalignsafe \long\def\ekv@optarg@if#1\ekv@mark[\ekv@mark{} % \end{macrocode} % \end{macro} -- cgit v1.2.3