From 882042b03c776e81243fe31ccec8f5ad90895859 Mon Sep 17 00:00:00 2001 From: Manuel Pégourié-Gonnard Date: Sat, 6 Oct 2012 17:56:48 +0000 Subject: new latex package: loops git-svn-id: svn://tug.org/texlive/trunk@27908 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/latex/loops/README | 483 +++++++++++ Master/texmf-dist/tex/latex/loops/loops.sty | 1211 +++++++++++++++++++++++++++ 2 files changed, 1694 insertions(+) create mode 100644 Master/texmf-dist/doc/latex/loops/README create mode 100644 Master/texmf-dist/tex/latex/loops/loops.sty (limited to 'Master/texmf-dist') diff --git a/Master/texmf-dist/doc/latex/loops/README b/Master/texmf-dist/doc/latex/loops/README new file mode 100644 index 00000000000..6eaa9719cbf --- /dev/null +++ b/Master/texmf-dist/doc/latex/loops/README @@ -0,0 +1,483 @@ +This is the README file for the 'loops' package. + +*** Location: latex/macros + +AUTHOR + +Ahmed Musa + +VERSION + +Version 1.0, September 2012. + +LICENSE + +Copyright (c) 2012 Ahmed Musa + +This software is author-maintained. Permission is granted to copy, +distribute and/or modify this software under the terms of the +LaTeX Project Public License, version 1.3 or higher. This software +is provided 'as it is', without warranty of any kind, either +expressed or implied, including, but not limited to, the implied +warranties of merchantability and fitness for any particular purpose. + + +SUMMARY + +The 'loops' package provides efficient looping macros for processing +both csv (separated-values) and nsv/tsv (non-separated values) lists. +csv lists having arbitrary parsers, and even active parsers, can +be processed with the tools of this package. + +All our loops can be safely nested. Breaking the inner loop doesn't +affect the continuation of the outer loop. + +The looping macros include: + +\skvrecurse: + + \skvrecurse{}\while<\if...>\fi + + A variant of D.E. Knuth's \loop macro. + + \@tempcnta\z@ + \skvrecurse{ + \advance\@tempcnta\@ne + \@tempcntb\z@ + \skvrecurse + \advance\@tempcntb\@ne + \typeout{Doing \the\@tempcnta,\the\@tempcntb} + \while + \ifnum\@tempcntb<3\relax + \fi + } + \while + \ifnum\@tempcnta<4\relax + \fi + + +\carlisleloop: + + \carlisleloop{}<\if...>\repeat + + A nesting version of D.E. Knuth's \loop macro, + adapted from the original macros of David Carlisle. + + \skvnewcounts{rowcnt,colcnt,maxrow,maxcol} + \maxrow4 \maxcol3\relax + \def\entries{} + \let\xp\skvexpandonce + \def\generate{% + \advance\colcnt\@ne + \edef\entries{\xp\entries\the\numexpr\rowcnt*\colcnt\relax}% + \ifnum\colcnt<\numexpr\maxcol+1\relax\relax + \edef\entries{\xp\entries\noexpand&}% + } + \def\generateentries{% + \carlisleloop\a + \advance\rowcnt\@ne\colcnt\z@ + \carlisleloop\b + \generate + \repeat\b + \ifnum\rowcnt<\numexpr\maxrow+1\relax\relax + \edef\entries{\xp\entries\noexpand\cr}% + \repeat\a + \edef\entries{\xp\entries\noexpand\crcr}% + } + \begin{document} + \generateentries + $$\vbox{\halign{&\ \hfil#\hfil\strut\cr\entries}}$$ + \end{document} + + +\akterloop: + + \akterloop{}<\if...>\repeat + + A generalized and naturally nesting version of D.E. Knuth's + \loop macro. No nesting brace is required, unlike in \skvrecurse. + No is required, unlike in \carlisleloop. + + \def\generateentries{% + \akterloop + \advance\rowcnt\@ne\colcnt\z@ + \akterloop + \generate + \repeat + \ifnum\rowcnt<\numexpr\maxrow+1\relax\relax + \edef\entries{\xp\entries\noexpand\cr}% + \repeat + \edef\entries{\xp\entries\noexpand\crcr}% + } + \begin{document} + \generateentries + $$\vbox{\halign{&\ \hfil#\hfil\strut\cr\entries}}$$ + \end{document} + + +\skvtfor: + + \skvtfor{}\do{} + \skvtfor*{}\do{} + + A variant of LaTeX kernel's \@tfor that has a different + approach to breaking the loop prematurely. + + \skvnewregister\count\nr + \skvtfor\x:=a \ifx {\def\x#1{#1}} \fi\do{% + \nr\z@ + \skvtfor\y:=12345\do{% + \typeout{Doing: \detokenize\expandafter{\x}--\y}% + \advance\nr\@ne + \ifnum\nr>\tw@\skvbreakloop\fi + }% + } + + +\ayeloop: + + \ayeloop{}\do{<1.parameter.callback>} + \ayeloop*{}\do{<1.parameter.callback>} + + A variant of \skvtfor that uses parameter characters + directly, instead of a holder macro. + + \ayeloop abcd\do{% + \def\nr{0}% + \ayeloop 12345\do{% + \typeout{Doing #1--##1}% + \skvpushnumber\nr + \ifnum\nr>3\relax\skvbreakloop\fi + }% + } + + +\sifakaloop: + + \sifakaloop{}{<1.parameter.callback>} + \sifakaloop*{}{<1.parameter.callback>} + + \sifakaloop is as \ayeloop but will first normalize the list + using \skvtsvnormalize before parsing. Unlike all the + other nsv/tsv loops, it preserves outer braces in the + arguments. Hence, it is costlier. You can insert \listbreaker + as a token in or to terminate the list prematurely. + + +\skvfor: + + \skvfor{}<1.parameter.callback.macro> + + \skvfor is an expandable comma loop. The list is not + normalized prior to parsing, and it is not possible to + terminate the loop prematurely (ie, before exhausting + the list). + + \skvnewregister\count\nr\nr=\tw@ + \def\do#1{% + \let\noexpand#1% + \expandafter\noexpand\csname\skvremovescape{#1}@% + \romannumeral\nr\endcsname + } + \edef\x{\skvfor{\cmda,\cmdb}\do} + \show\x -> \let\cmda\cmda@ii \let\cmdb\cmdb@ii + + +\skvcommaloop, \skvecommaloop, \skvcommaparse, \skvkvparse: + + \skvcommaloop{}{} + \skvcommaloop*{}{} + + All these have the same syntax. \skvecommaloop is as + \skvcommaloop but will execute once when + is empty. This is needed for processing empty key families. + + \skvcommaparse is as \skvcommaloop but will first normalize + using \skvcsvnormalize. \skvkvparse is as \skvcommaloop + but will first normalize using \skvkvnormalize. + \skvkvparse is meant for processing key-value lists. + + +\skvdolist and \skvparselist: + + \skvparselist{}{}{} + \skvparselist*{}{}{} + + These can process lists with arbitrary parsers/list + separators.The only difference between \skvdolist and + \skvparselist is that \skvparselist will first normalize + the list before parsing. + + +\cicadaloop: + + \cicadaloop[]{}<\if...>\fi{} + \cicadaloop*[]{}<\if...>\fi{} + + \cicadaloop will terminate prematurely on the current depth + whenever the condition specified by <\if...> is true. + is the regular code, to be executed + for every item of the list before the loop is terminated. + + Some internal macros are accessible by the user: \iflastcicada, + \currentcicada, \nextcicada, \lastcicada, \cicadacount. + \cicadacount is depth-dependent. + + You can insert \listbreaker as an item in or + to terminate the list prematurely. \listpauser isn't recognized by + \cicadaloop. + + \let\romn\romannumeral + \@tempcnta\z@ + \def\blist{{X};{Y};Z;\fi} + \def\stack{} + \cicadaloop{{a},{b},c,\if}\if01\fi{% + \advance\@tempcnta\@ne + \@tempcntb\z@ + \@tempswafalse + \cicadaloop*[;]\blist\if@tempswa\fi{% + \advance\@tempcntb\@ne + \typeout{Doing \romn\@tempcnta,\romn\@tempcntb: + \detokenize{#1,##1}}% + \edef\stack{% + \unexpanded\expandafter{\stack}\noexpand\do + {\romn\@tempcnta,\romn\@tempcntb}{\unexpanded{#1;##1}}% + }% + \ifnum\@tempcntb>\@ne + \@tempswatrue + \skvcsedef{cmd@\romn\cicadaloopdepth}{\lastcicada,\cicadacount}% + \fi + }% + } + \show\stack + + +\newforeach: + + \newforeach[]{} + {}{}{} + \newforeach[]{} + {}{}{} + + The star (*) form of \newforeach is equivalent to declaring + the option 'list is a macro' in . + + \newforeach is a more versatile and robust version of the popular + \foreach macro of the PGF bundle. The macro accepts arbitrary list + parsers and sub-parsers, including active list separators. + When needed, parameters corresponding to the holder macros can + be used directly in the callback. \newforeach does everything + that PGF's \foreach does. It doesn't automatically scope + calculations as \foreach does. Automatically creating local + groups for computations has been a problem for some users + of \foreach. Instead, \newforeach uses stacks to save and + restore the values of holder macros and some other internal + parameters/quantities. + + There is a long list of options, ie, keys in , + available for \newforeach and \foxloop. Also, there are many + internal macros that can be accessed by the user. A few of them + are \interruptforeach, \foreachcurrentitem, \foreachnextitem, + \foreachitemcount, \foreachprevitem, \prependtobeginforeach, + \appendtobeginforeach, \prependtoendforeach, \appendtoendforeach, + \ifforeachlastitem, \foreachnestdepth, \foreachlistremainder. + \foreachitemcount counts the items as the loop progresses. + It is depth dependent, ie, it can be called to access the + number of items processed on each depth of nesting of + \newforeach. On any depth, the user can say, eg, + + \ifnum\foreachitemcount>10\relax\interruptforeach\fi + + or + + \ifnum\foreachitemcount>10\relax\interruptloop\fi + + You can insert \listbreaker as an item in or + to terminate the list prematurely. \listpauser will pause + the processing for user action. + + Maybe you fancy the following syntactic sugar, which PGF's + \foreach can't presently process: + + \begingroup + \catcode`\,=13 + \gdef\alist{1,2,...,5,7,8,...,12} + \endgroup + \parindent-20pt + \begin{tikzpicture} + \draw[step=.5cm,blue!65,very thin] (0,0) grid (13,6); + \newforeach* \x [ + count in=\xc all \x satisfying \ifnum\x>5\fi + ] in \alist { + \newforeach [ + parser={;}, + subparser=:, + evaluate=\y as \ye using \numexpr\y*10 + ] + \y:\z in {1:red; 2:green; 3:blue; 4:brown; 5:purple} do { + \draw [fill=\z\ifnum\x>5!\ye\fi] (\x,\y) +(-.5,-.5) + rectangle ++(.5,.5); + \draw (\x,\y) node {\x,\y}; + } + } + \global\let\xc\xc + \end{tikzpicture} + \show\xc + + % Example credit: Tom Bombadil + \usetikzlibrary{arrows} + \parindent-40pt + \begin{tikzpicture} + % You may fix \pgfmathsetseed to get a constant output: + % \pgfmathsetseed{42} + \newforeach \y [count=\yc] in {-0.5,-1,...,-2.5} {% + \node[right] at (15.2,\y+0.1) {\scriptsize Subject \yc}; + \newforeach \x in {1,...,300}{% + \pgfmathtruncatemacro{\drawbool}{rand-0.7 > 0 ? 1 : 0}% + \ifnum\drawbool=1\relax + \fill (\x/20,\y) rectangle (\x/20+0.05,\y+0.3); + \fi + }% + }% + \draw[-stealth] (0,0) -- (15.5,0); + \node[right] at (15.5,0.2) {t in ms}; + \newforeach \x [ + pgf evaluate=\x as \xv using int(20*\x) + ] in {0,...,15} {% + \draw (\x,-0.05) -- (\x,0.05) node[above] {\xv}; + } + \end{tikzpicture} + + Here, it is possible to use #1, ##1, ##2 in the callback to + refer to \x, y, \z, respectively. This relieves the user of the + need to manually expand the holder macros \x,\y,\z in the callbacks, + as I have seen some TikZ users do. + + \newforeach found a bug in PGF's \foreach, as reported at + . However, \newforeach itself + can't be said to be bug free. + + +\foxloop: + + \foxloop[]{}{} + \foxloop*[]{}{} + + \foxloop does everything that \newforeach does, but, in addition, + it can process nsv/tsv (non-separated lists). It can auto-complete + ellipsis lists (ie, lists with '...'), but this feature is + available only for csv lists and not for nsv lists. + + 1. One important characteristic of \foxloop is that it doesn't use + holder macros (eg, \x/\y), but instead it uses parameter + characters directly. So it doesn't accept/expect holder macros. + 2. That means that if the argument isn't simple (ie, #1), then it + has to be specified as the value of the key/option 'arg' (eg, + arg=#1/#2). + 3. Note that \foxloop doesn't expect any optional 'in' or 'do'. + 4. Unlike \newforeach, \foxloop doesn't recognize the semicolon + (;) as a callback terminator. In fact, the callback for \foxloop + should ideally be always enclosed in balanced braces. PGF + often uses the semicolon as the callback terminator. \newforeach + supports that feature. + + The following macros are available to the user: + + \breakfoxloop, \interruptloop, \foxloopdepth, \foxcount, + \previousfox, \currentfox, \nextfox, \iflastfox, + \foxlistremainder, \prependtobeginfoxloop, \appendtobeginfoxloop, + \prependtoendfoxloop, \appendtoendfoxloop. + + + Examples: + + \foxloop [ + remember=#1 as \x initially A + ] {B,...,H} {% + $\overrightarrow{\x#1}$\iflastfox.\else,\space\fi + } + + This is equivalent to: + + \foxloop [ + list type=tsv, + remember=#1 as \x initially A + ] {BCDEFGH} {% + $\overrightarrow{\x#1}$\iflastfox.\else,\space\fi + } + + \usepackage{multicol} + \begin{document} + \begin{multicols}{2} + \foxloop [count=\nchar] {a,...,f} { + \foxloop [ + evaluate=##1 as \xe using \numexpr##1*20, + evaluate=##1 as \hx using \pgfmathparse{##1 mm*5}, + evaluate=##1 as \vx using \pgfmathparse{##1 mm*1.5}, + count in= \ci all ##1 satisfying \ifnum##1<4\fi initially 2, + count in= \di all ##1 satisfying \ifnum##1>2\fi, + ] {1,...,6} { + \endgraf + Doing: #1, ##1, \nchar, \ci, \di + \endgraf + \ifnum\nchar>3\relax + \vskip-\vx pt\relax + \fi + \hskip\hx pt\relax + \tikz[rounded corners,ultra thick]{ + \shade[ball color=red!\xe!blue] (0,0) circle (.25cm); + \node[white,font=\large] (0,0) {#1}; + }% + }% + } + \end{multicols} + \end{document} + + + % Example credit: Heiko Oberdiek + \makeatletter + \let\do\newdimen\do\DimWD\do\DimHT\do\DimDP + \newcommand*{\DimBox}[1]{% + \begingroup + \sbox0{\makebox[\DimWD]{#1}}% + \ht0=\DimHT\dp0=\DimDP\usebox\z@ + \endgroup + } + \newcommand*{\DimMeasure}[3]{% + \node at (0,0) {% + \let\do\global + \do\DimWD\z@\do\DimHT\z@\do\DimDP\z@ + \foxloop* [arg=##1/##2,parser={#1}] #2 {% + \sbox0{#3}% + \ifdim\wd0>\DimWD\global\DimWD=\wd0\fi + \ifdim\ht0>\DimHT\global\DimHT=\ht0\fi + \ifdim\dp0>\DimDP\global\DimDP=\dp0\fi + }% + };% + } + \makeatother + \begin{document} + \def\yellowlist{a/-1,-2;b/1,-2;c/2,-1;d/2,1;e/1,2;f/-1,2;g/-2,1;h/-2,-1} + \def\bluelist{a/b,b/c,c/d,d/e,e/f,f/g,g/h,h/a} + \begin{tikzpicture}[scale=1.2,auto=left,every node/.style={circle,thick}] + \DimMeasure{;}{\yellowlist}{#1} + \foxloop* [arg=#1/#2,parser={;},count=\xc] \yellowlist { + \node (#1) at (#2) [fill=blue!20,draw=yellow] {\DimBox{#1}}; + } + \DimMeasure{,}{\bluelist}{#1--#2} + \foxloop* [arg=#1/#2] \bluelist { + \draw [->] (#1) -- (#2) + node [midway,fill=red!20,draw=blue]{\DimBox{#1--#2}}; + } + \end{tikzpicture} + \end{document} + + +NOTE + +Non-LaTeX users can use the \newforeach macro (and some other +looping macros) by loading the file skeyval-for.tex. + + +% End of readme file of loops.sty \ No newline at end of file diff --git a/Master/texmf-dist/tex/latex/loops/loops.sty b/Master/texmf-dist/tex/latex/loops/loops.sty new file mode 100644 index 00000000000..4183be5ba27 --- /dev/null +++ b/Master/texmf-dist/tex/latex/loops/loops.sty @@ -0,0 +1,1211 @@ +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% +% This is file 'loops.sty', version 1.0, September 2012. % +% % +% This package and accompanying files may be distributed and/or % +% modified under the conditions of the LaTeX Project Public License, % +% either version 1.3 of this license or any later version. The latest % +% version of this license is in http://www.latex-project.org/lppl.txt % +% and version 1.3 or later is part of all distributions of LaTeX % +% version 2005/12/01 or later. % +% % +% The LPPL maintenance status of this software is 'author-maintained'. % +% % +% This software is provided 'as it is', without warranty of any kind, % +% either expressed or implied, including, but not limited to, the % +% implied warranties of merchantability and fitness for a particular % +% purpose. % +% % +% Copyright (c) 2010-2012 Ahmed Musa (amusa22@gmail.com). % +%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% + +\begingroup +\catcode035 06 % # +\catcode064 11 % @ +\catcode123 01 % { +\catcode125 02 % } +\catcode044 12 % , +\def\skv@prova{\endgroup + \def\do##1,{% + \ifx\do##1\else + \catcode##1\string=\the\catcode##1\relax + \expandafter\do + \fi + }% + \edef\loops@restorecodes{\do035,064,123,125,061,059,\do,}% +} +\skv@prova +\catcode035 06 % # +\catcode064 11 % @ +\catcode123 01 % { +\catcode125 02 % } +\catcode061 12 % = +\catcode044 12 % , +\def\do#1=#2,{% + \ifx#1\do\else + \edef\loops@restorecodes{% + \loops@restorecodes + \catcode#1=\the\catcode#1\relax + }% + \catcode#1=#2\relax + \expandafter\do + \fi +} +\do 032=10,033=12,036=03,038=04,040=12,041=12,042=12,043=12,% + 059=12,045=12,047=12,058=12,063=12,091=12,093=12,126=13,\do=,% + +\ProvidesPackage{loops}[2012/09/29 v1.0 Loops and list processors (AM)] +\NeedsTeXFormat{LaTeX2e}[2011/06/27] + +\@ifpackageloaded{skeyval}{}{\RequirePackage{skeyval}} +\skvnewlet\ltxkernelfor\@for + +% \skvusetwoprev{}{}{}{} +% +% Example: +% +% \skvusetwoprev{,}{a,b,c,d,e,f}{e}{} +% +\skvrobustdef*\skvusetwoprev#1#2#3#4{% + \skvifin{#1#3#1}{#1#2#1}{}{% + \skv@err{There is not '\detokenize{#3}' in the list}\@ehd + }% + \let\firstprev\@empty + \let\secondprev\@empty + \def\skv@tempa{#3}% + \def\skv@do##1#1{% + \ifx\skv@nnil##1\else + \def\current{##1}% + \ifx\current\skv@tempa + #4\relax + \def\skv@do####1\skv@nnil#1{}% + \else + \let\secondprev\firstprev + \let\firstprev\current + \fi + \expandafter\skv@do + \fi + }% + \skv@do#2#1\skv@nnil#1% +} + +% A version of D.E. Knuth's \loop: +% +\skvnewdef\dekloop#1\repeat{\skv@dekloop{#1}} +\skvnewdef\skv@dekloop#1{% + #1\relax + \expandafter\skv@dekloop + \else + \expandafter\@gobble + \fi + {#1}% +} + +% A version of D.E. Knuth's \loop that can be nested. +% Credit: David P. Carlisle. +% +% \carlisleloop\a +% \carlisleloop\b +% ... +% \if... +% \repeat\b +% ... +% \if... +% \repeat\a +% +\let\repeat\fi +\skvrobustdef*\carlisleloop#1{% + \long\def\skv@carlisleloop##1##2\repeat#1{% + \long\def##1{##2\relax\expandafter##1\fi}% + ##1\let##1\relax + }% + \expandafter\skv@carlisleloop + \csname skv@carlisleloop@\string#1\endcsname +} + +% A version of D.E.Knuth's \loop that can be nested naturally. +% +% 1. As a design constraint, this doesn't accept \par. +% +% 2. ak=Wilhelm Ackermann, ter=Rózsa Péter; in honour of these +% mathematicians. +% +% \akterloop +% \akterloop +% ... +% \if... +% \repeat +% ... +% \if... +% \repeat +% +\skvnewregister\toks\skv@aktertoks +\skvnewdef*\skv@aktercnt{0} +\skvrobustdef*\skv@akter@defloop{% + \def\skv@tempa##1##2##3{% + \def\skv@tempa{##1}% + \def\skv@tempb{##2}% + \let##2\fi + \gdef##1####1##2{% + \def##3{####1\relax\expandafter##3\fi}% + ##3\let##3\relax + }% + }% + \def\x##1{% + \skvnoexpandcs{skv@akter@##1@\skvrom\skv@aktercnt}% + }% + \edef\x{\x{loop}\x{repeat}\x{iterate}}% + \expandafter\skv@tempa\x +} +\skvrobustdef*\akterloop{% + \begingroup + \everyeof{EOF}\def\skv@aktercnt{0}% + \skv@aktertoks{\dekloop}% + \skv@akterpush +} +\skvrobustdef*\skv@akterpush{\futurelet\next\skv@akterpush@a} +% Use star variant of \skvrobustdef here, to catch \par: +\skvrobustdef*\skv@akterpush@a#1{% + \skvifntype{#1}{% + \skvifx{#1}\akterloop{% + \skvadvanceno\skv@aktercnt\@ne + \skv@akter@defloop + \skv@aktertoks\expandafter + {\the\expandafter\skv@aktertoks\skv@tempa}% + \skv@akterpush + }{% + \skvifstrcmp{#1}\repeat{% + \skvifnum\skv@aktercnt=\skvz@{% + \expandafter\endgroup\the\skv@aktertoks\repeat + }{% + \skv@aktertoks\expandafter + {\the\expandafter\skv@aktertoks\skv@tempb}% + \skvadvanceno\skv@aktercnt\m@ne + \skv@akterpush + }% + }{% + \skvifx\next\@sptoken{% + \skv@aktertoks\expandafter + {\the\expandafter\skv@aktertoks\@space#1}% + }{% + \skv@aktertoks\expandafter{\the\skv@aktertoks#1}% + }% + \skv@akterpush + }% + }% + }{% + \skvifstrcmp{#1}{EOF}{% + \skv@err{'\string\repeat' not found: endless loop}\@ehd + }{% + \skvifx\next\@sptoken{% + \skv@aktertoks\expandafter + {\the\expandafter\skv@aktertoks\space{#1}}% + }{% + \skv@aktertoks\expandafter{\the\skv@aktertoks{#1}}% + }% + \skv@akterpush + }% + }% +} + +% Expandable comma loop. List is not normalized prior to parsing, and +% it is not possible to terminate the loop prematurely. +% +% \skvfor{} +% +% Example: +% +% \skvnewregister\count\nr\nr=\tw@ +% \def\do#1{% +% \let\noexpand#1% +% \expandafter\noexpand\csname\skvremovescape{#1}@% +% \romannumeral\nr\endcsname +% } +% \edef\x{\skvfor{\cmda,\cmdb}\do} +% \show\x -> \let\cmda\cmda@ii \let\cmdb\cmdb@ii +% +\skvnewdef\skvfor#1#2{\skv@for@a#1,\skv@for@nil,\skv@for@nnil{#2}} +\skvnewdef\skv@for@a#1,#2\skv@for@nnil#3{% + \skvifblank{#1}{% + \skv@for@a#2\skv@for@nnil{#3}% + }{% + \skvifstrcmp{#1}{\skv@for@nil}% + {}{#3{#1}\skv@for@a#2\skv@for@nnil{#3}}% + }% +} + +% Process non-separated (token-wise) list: +% +% \ayeloop{}\do{<1.parameter.callback>} +% \ayeloop*{}\do{<1.parameter.callback>} +% +% \ayeloop abcd\do{% +% \def\nr{0}% +% \ayeloop 12345\do{% +% \typeout{Doing #1--##1}% +% \skvpushnumber\nr +% \ifnum\nr>3\relax\skvbreakloop\fi +% }% +% }% +% +% \ayeloop is like \skvtfor but uses parameter characters directly, +% instead of a holder macro. +% +\skvrobustdef*\ayeloop{\skv@testst\skv@ayeloop} +\skvrobustdef*\skv@ayeloop#1\do#2{% + \skv@usetempst{#1}\skv@tempa + \skvsavestate\ayeloop{\do\skv@ayeloop}\skv@csvdepth + \def\skv@ayeloop##1{% + \skvifstrcmp{##1}\skv@tfor@nil{% + \skvbreakloopfalse + }{% + #2\relax + \ifskvbreakloop + \skvbreakloopfalse + \begingroup + \def\skv@ayeloop####1\skv@tfor@nil{% + \endgroup + \edef\skvremainder{\unexpanded{####1}}% + }% + \fi + \skv@ayeloop + }% + }% + \skvbreakloopfalse + \expandafter\skv@ayeloop\skv@tempa\skv@tfor@nil + \skvrestorestate\ayeloop\skv@csvdepth +} + +% Robust, outer-brace-preserving, multi-level, general-purpose +% non-separated values list (nsv) processor. +% +% \sifakaloop{}{<1.parameter.callback>} +% \sifakaloop*{}{<1.parameter.callback>} +% +\skvnewdef*\skv@tsvnil{\skv@tsvnil} +\skvrobustdef*\sifakaloop{\skv@testst\skv@sifakaparse@a} +\skvrobustdef*\skv@sifakaparse@a#1#2{% + \skv@usetempst{#1}\skv@sifakaarg + \skvsavestate\sifakaloop{\do\skv@user@do}\skv@csvdepth + \def\skv@user@do##1{#2}% + \edef\skv@sifakaarg{% + \expandafter\skvtsvnormalize\expandafter{\skv@sifakaarg}% + }% + \skvbreakloopfalse + \skvifempty\skv@sifakaarg{% + \ifskvprocessempty\skv@user@do{}\fi + }{% + \expandafter\skv@sifakaparse@b\skv@sifakaarg\skv@tsvnil + }% + \skvrestorestate\sifakaloop\skv@csvdepth + \ifnum\skv@csvdepth=\skvz@\skvundef\skv@user@do\fi +} +\skvrobustdef*\skv@sifakaparse@b{% + \begingroup + \let\bgroup\relax + \skvifnextchar\skv@orig@bgroup{% + \let\skv@next\skv@orig@bgroup + \skv@sifakaparse@c + }{% + \let\skv@next\relax + \skv@sifakaparse@c + }% +} +\skvrobustdef*\skv@sifakaparse@c#1{% + \skvifx\skv@next\skv@orig@bgroup{% + \endgroup + \edef\skv@sifakaarg{{\unexpanded{#1}}}% + }{% + \endgroup + \edef\skv@sifakaarg{\unexpanded{#1}}% + }% + \skvifx\skv@sifakaarg\skv@tsvnil{% + \skvbreakloopfalse + }{% + \skvifx\skv@sifakaarg\skv@listbreakertoks{% + \skvbreakloop + }{% + \skvifempty\skv@sifakaarg{% + \ifskvprocessempty\skv@user@do{}\fi + }{% + \expandafter\skv@user@do\expandafter{\skv@sifakaarg}\relax + }% + \skvifdefbool{skvbreakloop}{% + \skvbreakloopfalse + \begingroup + \def\skv@prova##1\skv@tsvnil{% + \endgroup + \edef\skvremainder{\unexpanded{##1}}% + }% + \skv@prova + }{% + \skv@sifakaparse@b + }% + }% + }% +} + +% Processing both csv and tsv/nsv lists with \foxloop. +% +% \foxloop[]{}{} +% \foxloop*[]{}{} +% +% 1. The is at least a one-parameter token list. +% +% 2. \breakfoxloop can be used to break out of the loop. +% +% 3. When the argument is more than one (say, #1#2), the last one +% must be delimited by the user-suggested . +% +% 4. To permit nesting we use a stack involving the counter \foreachdepth, +% with alias \foxloopdepth. +% +% 5. \subcallback is meant for callbacks with hash characters that may be +% confused with the parameters of the main . It can be defined +% outside and called within . +% +% 6. Outer braces around the list items are preserved in the parsing. +% +% 7. 'fox' is used here to represent an item. Eg, \currentfox, \nextfox, +% \foxcount. +% +\skvnewdef*\skv@foxloop@nnil{\skv@foxloop@nil} +\skvnewbools{lastfox} +\skvnewlet\breakfoxloop\skvbreaklooptrue +\skvnewdef*\foxloopdepth{\foreachdepth} +\skvnewdef*\foxcount{\foreachitemcount} +\skvnewdef*\currentfox{\foreachcurrentitem} +\skvnewdef*\nextfox{\foreachnextitem} +\skvnewdef*\previousfox{\foreachprevitem} +\skvnewdef*\foxlistremainder{\foreachlistremainder} +\skvnewdef*\prependtobeginfoxloop{\prependtobeginforeach} +\skvnewdef*\appendtobeginfoxloop{\appendtobeginforeach} +\skvnewdef*\prependtoendfoxloop{\prependtoendforeach} +\skvnewdef*\appendtoendfoxloop{\appendtoendforeach} + +% Define \foxloop keys. Name all the macros with 'foreach' prefix so +% that we can avoid new command names and use the list auto-completion +% scheme of \newforeach. +\quickkeys.define{foxloop}{% + parser/{,}/ + \def\skv@foreach@parser{#1} + \skvstripouterbraces{2}\skv@foreach@parser + ; + list is a macro,macro list/true/ + \edef\skv@foreach@listismacro{0\skvifstrcmp{#1}{true}01} + /true,false + ; + list type/csv/ + \def\skv@foreach@listtype{#1} + /csv,kv,tsv,nsv + ; + arg,args,arg pattern/##1/ + \edef\skv@foreach@paramlist{\unexpanded{#1}} + \skvstripouterbraces{1}\skv@foreach@paramlist + ; + list breaker/\listbreaker/\def\skv@listbreakertoks{#1}; + list pauser/\listpauser/\def\skv@listpausertoks{#1}; + normalize list/true/ + \edef\skv@foreach@normalizelist{0\skvifstrcmp{#1}{true}01} + /true,false + ; + use empty,use empty items/true/ + \edef\skv@foreach@useempty{0\skvifstrcmp{#1}{true}01} + /true,false + ; + .exec/ + \let\skv@foreach@exittester\@secondoftwo + ; + loop stopper,exit when/\if01\fi/ + \ifskvindef\else + \edef\skv@tempa{\unexpanded{#1}} + \skvstripouterbraces{2}\skv@tempa + \expandafter\skv@foreach@maketester\expandafter + {\skv@tempa}\skv@foreach@exittester + \fi + ; + item counter,counter/\@nil/ + \def\skv@foreach@itemcounter{#1} + ; + % This default value of 'link' is required by \skv@foreach@filllist: + .exec/\def\skv@foreach@link{\@nil}; + link/\@nil/ + \ifskvindef\else + \edef\skv@foreach@link{\unexpanded{#1}} + \skvstripouterbraces{2}\skv@foreach@link + \ifnum0>\numexpr0\ifx\skv@foreach@link\@empty-1\else + \ifx\skv@foreach@link\@nnil-1\else+0\fi\fi\relax\relax + \skv@err{The key 'link' doesn't accept empty/blank values}\@ehd + \fi + \fi + ; + grow/\@nil/ + \ifskvindef\else + \edef\skv@prova{\unexpanded{#1}}% + \ifx\skv@prova\@nnil + \skv@err{Value required for key 'grow'}\@ehd + \else + \skvcsedef{skv@foreach@grow#1}{00}% + \edef\skv@prova{\skvifstrcmp{#1}{left}{right}{left}}% + \skvcsedef{skv@foreach@grow\skv@prova}{01}% + \fi + \fi + /left,right + ; + .exec/ + \def\skv@foreach@growleft{01}% + \def\skv@foreach@growright{01}% + ; + grow left/true/ + \ifskvindef\else + \edef\skv@foreach@growleft{0\skvifstrcmp{true}{#1}01}% + \edef\skv@foreach@growright{0\skvifstrcmp{true}{#1}10}% + \fi + /true,false + ; + grow right/true/ + \ifskvindef\else + \edef\skv@foreach@growright{0\skvifstrcmp{true}{#1}01}% + \edef\skv@foreach@growleft{0\skvifstrcmp{true}{#1}10}% + \fi + /true,false + ; + % Keep the decimals in rounded numbers or dimensions when list is + % auto-completed. The value value of this key is 'true'. + keep decimals/true/ + \edef\skv@foreach@keepdecimals{0\skvifstrcmp{true}{#1}01}% + /true,false + ; + keep high decimals,keep only high decimals/true/ + \edef\skv@foreach@keephighdecimals{0\skvifstrcmp{true}{#1}01}% + /true,false + ; + expand before fill/true/ + \edef\skv@foreach@expandbeforefill{0\skvifstrcmp{true}{#1}01}% + /true,false + ; + % Eg, 'evaluate = #1 as \x using \numexpr#1*2'. + % Since the parameters haven't assumed arguments, we defer the + % call to \skv@fox@parseevaluate until the arguments are known. + evaluate/\@nil/ + \skv@foreach@ifacceptkv{#1}{% + \skv@fox@parseevaluate{#1}% + }% + ; + % Eg, + % 'count in = \p all #1 satisfying \ifnum#1>4\fi initially 2' + % 'count in = \p all #1 satisfying \ifnum#1>4\fi' + count in,count/\@nil/ + \skv@foreach@ifacceptkv{#1}{% + \skv@fox@parsecountin{#1}% + }% + ; + % Eg, remember=#1 as \y initially A + remember/\@nil/ + \skv@foreach@ifacceptkv{#1}{% + \skv@fox@parseremember{#1}% + }% + ; +} + +% Set up \foxloop defaults: +\quickkeys.set{foxloop}{% + parser={,}, + list is a macro=false, + list type=csv, + arg=#1, + list breaker=\listbreaker, + list pauser=\listpauser, + normalize list=true, + use empty=false, + keep decimals=true, + keep high decimals=false, + expand before fill=false, +} + +% Parser for 'evaluate' key: +% +% Eg, 'evaluate = #1 as \x using \numexpr#1*2'. +% +% \skv@fox@parseevaluate differs from \skv@foreach@parseevaluate +% because #1 here is likely to be a non-macro. +% +\skvrobustdef*\skv@fox@parseevaluate#1{% + \begingroup + \def\skv@tempxifin##1##2{% + \skvxifin{\detokenize{##1}}{\detokenize{##2}}% + }% + \def\skv@tempd{}% + % #3 can be empty. So we don't test for its emptiness here. + \def\skv@tempa##1as##2using##3\skv@nil{% + \edef\skv@prova{\skvtrimspace{##1}}% + \edef\skv@provb{\skvtrimspace{##2}}% + \edef\skv@provc{\skvtrimspace{##3}}% + \ifx\skv@prova\@empty + \skv@err{Value of key 'evaluate': + \MessageBreak no quantity to evaluate}\@ehd + \fi + \ifx\skv@provb\@empty + \skv@err{Value of key 'evaluate' has no receiving macro, + \MessageBreak i.e., after 'as'}\@ehd + \else + \expandafter\skvifescaped\expandafter{\skv@provb}{}{% + \skv@err{Value of key 'evaluate': receiving macro + \MessageBreak \skvoxdetok\skv@provb not escaped}\@ehd + }% + \fi + \ifx\skv@provc\@empty + \skv@err{Value of key 'evaluate' has no formula}\@ehd + \fi + \skv@tempxifin{\pgfmathparse}{##3}{% + \def\skv@tempc####1\pgfmathparse####2\skv@nil{% + \ifx\@nnil####1\@nnil + \ifdefined\pgfmathparse + \skv@foreach@swatrue + \else + \skv@err{\noexpand\pgfmathparse isn't defined; + \MessageBreak maybe 'pgfmath' isn't loaded}\@ehd + \fi + \else + \skv@err{Invalid token \detokenize{####1} + \MessageBreak before \string\pgfmathparse}\@ehd + \fi + }% + \expandafter\skv@tempc\skv@provc\skv@nil + }{% + \skv@foreach@swafalse + \skv@tempxifin{\numexpr}{##3}{% + \def\skv@tempd{\relax}% + }{% + \skv@tempxifin{\dimexpr}{##3}{% + \def\skv@tempd{\relax}% + }{% + \def\skv@tempd{}% + }% + }% + }% + }% + \skv@tempxifin{using}{#1}{% + \skv@tempxifin{as}{#1}{% + % 'as' and 'using' are present: there is a formula. + \skv@foreach@swbtrue + \skv@tempa#1\skv@nil + }{% + \skv@err{Value of key 'evaluate': + \MessageBreak there is 'using', but no 'as'}\@ehd + }% + }{% + % No formula given: + \skv@foreach@swbfalse + \skv@tempxifin{as}{#1}{% + \def\skv@tempb##1as##2##3\skv@nil{% + \ifx\@nnil##1\@nnil + \skv@err{Value of key 'evaluate' has no + \MessageBreak quantity to evaluate}\@ehd + \else + \ifx\@nnil##2% + \skv@err{Value of key 'evaluate' has no receiving macro}\@ehd + \else + \skv@tempa##1as##2using##1\skv@nil + \fi + \fi + }% + \skv@tempb#1{\@nnil}\skv@nil + }{% + % No 'as' or 'using'. In \foxloop, #1 is not guaranteed + % to be a macro. Hence raise error here: + \skv@err{Value of key 'evaluate' has no 'as'}\@ehd + }% + }% + \skv@foreach@addtocallback{pre}{% + \ifskv@foreach@swa + % Here, may have the syntax \pgfmathresult{}. + \skvexpandonce\skv@provc + \let\skvexpandonce\skv@provb\noexpand\pgfmathresult + \else + % Here, may have the syntax \numexpr\x*2 or \dimexpr\x*2. + \edef\skvexpandonce\skv@provb{% + \ifskv@foreach@swb\noexpand\the\fi + \skvexpandonce\skv@provc\skvexpandonce\skv@tempd + }% + \fi + }% + \skvaftergroupdef\skv@foreach@precallback\endgroup +} + +% Eg, 'count in \y all #1 satisfying \ifnum#1>2\fi initially 0'. +% +% 1. must be a balanced and valid TeX or LaTeX (2-fork) +% conditional, like +% +% \ifnum\x>2\fi, \ifx\x\@empty\fi +% +% 2. The aim is to allow general conditions (not only those involving +% numerals) to be submitted. +% +% 3. PGF's \foreach doesn't have this feature; it has only a general +% counter, eg, +% +% '\foreach \x [count=\xi from 2] in {a,...,e}'. +% +% Generic counting is available in \foxloop by default, without +% user request. The macros \currentfox, \foxcount, \nextfox are +% always available on each nesting level. The boolean \iflastfox +% is also always available, to indicate when the last item of +% the list has been reached: +% +% \iflastfox\typeout{Processing last entry of the list} \fi +% +\skvrobustdef*\skv@fox@parsecountin#1{% + \begingroup + \def\skv@tempxifin##1##2{% + \skvxifin{\detokenize{##1}}{\detokenize{##2}}% + }% + \def\skv@tempa##1\skv@nil{% + \skv@tempxifin{initially}{##1}{% + \skv@tempb##1\skv@nil + }{% + \skv@tempb##1initially\skv@nil + }% + }% + \def\skv@tempb##1all##2satisfying##3initially##4\skv@nil{% + \edef\skv@prova{\skvtrimspace{##1}}% + \edef\skv@provb{\skvtrimspace{##2}}% + \edef\skv@provc{\skvtrimspace{##3}}% + \edef\skv@provd{\skvtrimspace{##4}}% + \ifx\skv@prova\@empty + \skv@err{Value of key 'count' has no counter macro}\@ehd + \else + \expandafter\skvifescaped\expandafter{\skv@prova}{}{% + \skv@err{Value of key 'count':\MessageBreak + counter macro \skvoxdetok\skv@prova not escaped}\@ehd + }% + \fi + \ifx\skv@provd\@empty + \expandafter\def\skv@prova{0}% + \else + \skvifinteger\skv@provd{% + \expandafter\let\skv@prova\skv@provd + }{% + \skv@err{Value of key 'count':\MessageBreak + initial \skvoxdetok\skv@provd not an integer}\@ehd + }% + \fi + \skv@foreach@swafalse + \ifx\skv@provc\@empty + % No tester but there may be a quantity to test. The quantity, + % on its own, isn't sufficient to conduct a test: + \let\skv@foreach@itemcounter\skv@prova + \else + % If there isn't a quantity to test, then no test is possible: + \ifx\skv@provb\@empty\else + \skv@foreach@swatrue + % \skv@tester is the second argument of \skv@foreach@maketester, + % which is used below: + \skvexpbracenext\skv@foreach@maketester\skv@provc\skv@tester + \fi + \fi + }% + \skv@tempxifin{satisfying}{#1}{% + \skv@tempxifin{all}{#1}{% + % 'all' and 'satisfying' are present: + \skv@tempa#1\skv@nil + }{% + \skv@err{Value of key 'count': + \MessageBreak there is 'satisfying', but no 'all'}\@ehd + }% + }{% + \skv@tempxifin{all}{#1}{% + \skv@tempa#1satisfying\skv@nil + }{% + % No 'all' and 'satisfying': #1 should be only the counter macro: + \ifx\@nnil#1\@nnil + \skv@err{Value of key 'count': no counter macro}\@ehd + \else + \skvifntype{#1}{% + \skv@tempa#1all satisfying\skv@nil + }{% + \skv@err{Value of key 'count in': more than 1 + \MessageBreak token '\detokenize{#1}'. + \MessageBreak I can't find counter macro}\@ehd + }% + \fi + }% + }% + \ifskv@foreach@swa + \skv@foreach@addtocallback{pre}{% + \noexpand\ifnum\noexpand\foreachitemcount=\@ne + \def\skvexpandonce\skv@prova{\number\skv@prova}% + \noexpand\fi + \skvexpandonce\skv@tester{% + \noexpand\skvpushnumber\skvexpandonce\skv@prova + }{}% + }% + \fi + \skvexpanded{\endgroup + \ifskv@foreach@swa + \skvcmdexit\skv@foreach@precallback + \else + \skvcmdexit\skv@foreach@itemcounter + \fi + }% +} + +% remember= as initially +% +\skvrobustdef*\skv@fox@parseremember#1{% + \begingroup + \def\skv@tempxifin##1##2{% + \skvxifin{\detokenize{##1}}{\detokenize{##2}}% + }% + \skv@tempxifin{(}{#1}{% + \skv@err{Value of key 'remember': '(' found. + \MessageBreak I don't accept this PGF format. + \MessageBreak remove the parenthesis '()'}\@ehd + }{}% + \def\skv@tempa##1as##2initially##3\skv@nil{% + \edef\skv@prova{\skvtrimspace{##1}}% + \edef\skv@provb{\skvtrimspace{##2}}% + \edef\skv@provc{\skvtrimspace{##3}}% + \skv@foreach@swafalse + \ifx\skv@prova\@empty + \skv@err{Value of key 'remember' has no value to remember}\@ehd + \fi + \ifx\skv@provb\@empty + \skv@err{Value of key 'remember' has no child macro; + \MessageBreak i.e., no macro after 'as'}\@ehd + \else + \expandafter\skvifescaped\expandafter{\skv@provb}{% + \skv@foreach@swatrue + }{% + \skv@err{Value of key 'remember': + \MessageBreak child \skvoxdetok\skv@provb not escaped}\@ehd + }% + \fi + }% + \skv@tempxifin{initially}{#1}{% + \skv@tempxifin{as}{#1}{% + \skv@tempa#1\skv@nil + }{% + \skv@err{Value of key 'remember': + \MessageBreak there is 'initially', but no 'as'}\@ehd + }% + }{% + \skv@tempxifin{as}{#1}{% + \skv@tempa#1initially\skv@nil + }{% + % No 'as' and 'initially': + \ifx\@nnil#1\@nnil\else + % Unlike in \newforeach, we can't create an automatic retainer + % macro here, since \foxloop uses parameters instead of + % holder macros. + \skv@err{Value of key 'remember' is incomplete: no 'as'}\@ehd + \fi + }% + }% + \ifskv@foreach@swa + % Initially: + \skv@foreach@addtocallback{pre}{% + % Don't remove this \ifnum, otherwise the initial assignment + % will be overwritten on subsequent returns: + \noexpand\ifnum\noexpand\foreachitemcount=\@ne + \edef\skvexpandonce\skv@provb{% + \noexpand\unexpanded{\skvexpandonce\skv@provc}% + }% + \noexpand\fi + }% + \skv@foreach@addtocallback{post}{% + \edef\skvexpandonce\skv@provb{% + \noexpand\unexpanded{\skvexpandonce\skv@prova}% + }% + }% + \fi + \skvexpanded{\endgroup + \ifskv@foreach@swa + \skvcmdexit\skv@foreach@precallback + \skvcmdexit\skv@foreach@postcallback + \fi + }% +} + +\skvnewlet\setupfoxloop\setupforeach + +% \atbeginfoxloop[]{} +\skvnewlet\atbeginfoxloop\atbeginforeach +% \atendfoxloop[]{} +\skvnewlet\atendfoxloop\atendforeach + +\skvrobustdef*\foxloop{\skv@teststopt\skv@foxloop{}} +\skvrobustdef*\skv@foxloop[#1]#2#3{% + \ifskv@tempst + \def\skv@foreach@st{00}% + \else + \def\skv@foreach@st{01}% + \fi + \skv@foreach@pushstate + \skv@foreach@latehookerr + \def\foreachitemcount{0}% + \def\skv@foreach@callback{}% + \def\skv@foreach@precallback{}% + \def\skv@foreach@postcallback{}% + \def\skv@foreach@onlyinitially{}% + \quickkeys.set{foxloop}{#1}% + \if\skv@foreach@st\else + \if\skv@foreach@listismacro + \def\skv@foreach@st{00}% + \else + \def\skv@foreach@st{01}% + \fi + \fi + \edef\skv@foreach@userlist{% + \if\skv@foreach@st + \expandafter\skvexpandonce + \else + \expandafter\unexpanded + \fi + {#2}% + }% + \skvcsuse{skv@foreach@atbeginhook@\skvrom\foreachdepth}% + \edef\reserved@a{% + \skvexpandonce\skv@foreach@precallback + \ifx\skv@foreach@onlyinitially\@empty\else + \noexpand\ifnum\noexpand\foreachitemcount=\@ne + \skvexpandonce\skv@foreach@onlyinitially + \noexpand\fi + \fi + \unexpanded{#3}% + \skvexpandonce\skv@foreach@postcallback + }% + % Define the user callback: + \edef\reserved@b{% + \def\noexpand\skv@foreach@callback\skvexpandonce + \skv@foreach@paramlist\noexpand\foreacheov + }% + \expandafter\reserved@b\expandafter{\reserved@a}% + \skvxifstrcmp\skv@foreach@listtype{csv}{% + \def\skv@foreach@lookahead@a##1{% + \def\skv@foreach@continue{##1.}% + \futurelet\skv@foreach@next\skv@foreach@lookahead@b + }% + \def\skv@foreach@lookahead@c[##1]{\skv@foreach@continue[{##1}]}% + \def\skv@foreach@lookahead@d(##1){\skv@foreach@continue({##1})}% + }{% + \def\skv@foreach@lookahead@a##1{% + \let\skv@foreach@continue##1% + \futurelet\skv@foreach@next\skv@foreach@lookahead@b + }% + \def\skv@foreach@lookahead@c[##1]{\skv@foreach@continue{[##1]}}% + \def\skv@foreach@lookahead@d(##1){\skv@foreach@continue{(##1)}}% + }% + \def\skv@foreach@lookahead@b{% + \skvifx\skv@foreach@next[{% + \skv@foreach@parenthtrue\skv@foreach@lookahead@c + }{% + \skvifx\skv@foreach@next({% + \skv@foreach@parenthtrue\skv@foreach@lookahead@d + }{% + \skv@foreach@parenthfalse\skv@foreach@continue + }% + }% + }% + \skvbreakloopfalse\lastfoxfalse + \def\reserved@a##1{% + \skv@setupgetnextfox{##1}% + \skvxifstrcmp\skv@foreach@listtype{csv}{% + \if\skv@foreach@normalizelist + \skv@foreach@normalize[##1]\skv@foreach@userlist + \fi + \def\skv@foreach@do####1##1####2\skv@foxloop@stop{% + \edef\currentfox{\skvexpandonce{\@gobble####1}}% + \skv@foxloop@a{##1}{####2}{csv}% + }% + \skvifempty\skv@foreach@userlist{}{% + \expandafter\skv@foreach@filllist\expandafter + {\skv@foreach@parser}\skv@foreach@userlist + }% + % If \skv@foreach@userlist is empty, \currentfox in + % \skv@foxloop@b will terminate the loop. + \expandafter\skv@foreach@lookahead@a\expandafter\skv@foreach@do + \skv@foreach@userlist##1\skv@foxloop@nil##1\skv@foxloop@stop + }{% + \if\skv@foreach@normalizelist + \edef\skv@foreach@userlist{% + \skvexpbracenext\skvtsvnormalize\skv@foreach@userlist + }% + \fi + \def\skv@foreach@do{% + \let\bgroup\relax + \futurelet\skv@foreach@next\skv@foreach@do@a + }% + \def\skv@foreach@do@a####1####2\skv@foxloop@stop{% + \let\bgroup\skv@orig@bgroup + \ifskv@foreach@parenth + \edef\currentfox{\unexpanded{####1}}% + \else + \skvifx\skv@foreach@next\bgroup{% + \edef\currentfox{{\unexpanded{####1}}}% + }{% + \edef\currentfox{\unexpanded{####1}}% + }% + \fi + \skv@foxloop@a{}{####2}{tsv}% + }% + \expandafter\skv@foreach@lookahead@a\expandafter\skv@foreach@do + \skv@foreach@userlist\skv@foxloop@nil\skv@foxloop@stop + }% + }% + \expandafter\reserved@a\expandafter{\skv@foreach@parser}% + \skvcsuse{skv@foreach@atendhook@\romannumeral\foreachdepth}% + \skv@foreach@popstate +} +% \skv@setupgetnextfox +\skvrobustdef*\skv@setupgetnextfox#1{% + \skvxifstrcmp\skv@foreach@listtype{csv}{% + \def\skv@foreach@getnext##1#1##2\skv@foxloop@stop{% + \edef\nextfox{\skvexpandonce{\@gobble##1}}% + % See \skv@foxloop@b for \lastfoxtrue. + }% + }{% + \def\skv@foreach@getnext{% + \begingroup + \let\bgroup\relax + \futurelet\skv@foreach@next\skv@foreach@g@tnext + }% + \def\skv@foreach@g@tnext##1##2\skv@foxloop@stop{% + \let\bgroup\skv@orig@bgroup + \ifskv@foreach@parenth + \edef\nextfox{\unexpanded{##1}}% + \else + \skvifx\skv@foreach@next\bgroup{% + \edef\nextfox{{\unexpanded{##1}}}% + }{% + \edef\nextfox{\unexpanded{##1}}% + }% + \fi + }% + }% +} +% \skv@foxloop@a +\skvrobustdef*\skv@foxloop@a#1#2#3{% + \skvifx\currentfox\skv@foxloop@nnil{% + \skvbreakloopfalse\lastfoxfalse + }{% + \skvifx\currentfox\skv@listbreakertoks{% + \skv@foxloop@getrm{#1}{#2}% + }{% + \skvifx\currentfox\skv@listpausertoks{% + \message{^^J! List pause: + ^^JType x or X to quit, or to proceed^^J}% + \bgroup\endlinechar\m@ne\global\read-1 to\@gtempa\egroup + \skvexpandarg\lowercase{\def\noexpand\@gtempa{\@gtempa}}% + \skvxifstrcmp\@gtempa{x}{% + \skv@foxloop@getrm{#1}{#2}% + }{% + \skv@foxloop@b{#1}{#2}{#3}% + }% + }{% + \skv@foxloop@b{#1}{#2}{#3}% + }% + }% + }% +} +% \skv@foxloop@b +\skvrobustdef*\skv@foxloop@b#1#2#3{% + \skv@foreach@lookahead@a\skv@foreach@getnext#2\skv@foxloop@stop + \ifx\nextfox\skv@foxloop@nnil + \lastfoxtrue\def\nextfox{}% + \fi + \edef\foreachitemcount{\the\numexpr\foreachitemcount+1}% + \ifnum0=\numexpr + \ifx\skv@foreach@itemcounter\@undefined-1\else+0\fi + \ifx\skv@foreach@itemcounter\@empty-1\else+0\fi + \ifx\skv@foreach@itemcounter\@nnil-1\else+0\fi\relax\relax + \expandafter\let\skv@foreach@itemcounter\foreachitemcount + \fi + \skvifempty\currentfox{% + \if\skv@foreach@useempty + \expandafter\skv@foreach@callback\expandafter\foreacheov + \fi + }{% + % Tes for \listpauser, in case the user decides to continue + % after \listpauser has been picked: + \skvifx\currentfox\skv@listpausertoks{}{% + \skvifx\skv@foreach@paramlist\skv@simplearg{% + \expandafter\skv@foreach@callback\expandafter + {\currentfox}\foreacheov + }{% + \expandafter\skv@foreach@callback\currentfox\foreacheov + }% + }% + }% + \skv@foreach@exittester{\skvbreakloop}{}% + \skvifdefbool{skvbreakloop}{% + \skvbreakloopfalse\lastfoxfalse + \skv@foxloop@getrm{#1}{#2}% + }{% + \skv@foreach@lookahead@a\skv@foreach@do#2\skv@foxloop@stop + }% +} +% \skv@foxloop@getrm +\skvrobustdef*\skv@foxloop@getrm#1#2{% + \begingroup + \def\reserved@a##1\skv@foxloop@nil#1{% + \endgroup + \edef\foxlistremainder{\unexpanded{##1}}% + }% + \reserved@a#2% +} + +% \cicadaloop[]{}<\if...>\fi{<1.parameter.code>} +% \cicadaloop*[]{}<\if...>\fi{<1.parameter.code>} +% +% 1. When the conditional <\if...> isn't true, the regular code +% will be executed for every member of the list. +% 3. \ifskvbreakloop may be used as the conditional in <\if...>. +% 5. The boolean \iflastcicada is available for accessing the last item +% of the list. There are also the macros \currentcicada, \lastcicada, +% \cicadacount, and the counter \cicadaloopdepth. +% 6. \cicadaloop can be nested. +% +% Example: +% +% \let\romn\romannumeral +% \@tempcnta\z@ +% \def\blist{{X};{Y};Z;\fi} +% \def\stack{} +% \cicadaloop{{a},{b},c,\if}\if01\fi{% +% \advance\@tempcnta\@ne +% \@tempcntb\z@ +% \@tempswafalse +% \cicadaloop*[;]\blist\if@tempswa\fi{% +% \advance\@tempcntb\@ne +% \typeout{Doing \romn\@tempcnta,\romn\@tempcntb: \detokenize{#1,##1}}% +% \edef\stack{% +% \unexpanded\expandafter{\stack}\noexpand\do +% {\romn\@tempcnta,\romn\@tempcntb}{\unexpanded{#1;##1}}% +% }% +% \ifnum\@tempcntb>\@ne +% \@tempswatrue +% \skvcsedef{cmd@\romn\cicadaloopdepth}{\lastcicada,\cicadacount}% +% \fi +% }% +% } +% \show\stack +% +\skvnewregisters\bool{\iflastcicada} +\skvbuildmacrostack\skv@cicada@state{% + \do\skv@cicada@do\do\skv@cicada@userlist\do\currentcicada + \do\nextcicada\do\cicadacount\do\lastcicada\do\skv@cicada@callback + \do\skv@cicada@getnext\do\ifskvprocessempty +}\skv@stackdepthlimit + +\skvnewnumbers{cicadaloopdepth} +\skvnewlet\skv@cicada@nil\relax +\skvnewdef*\skv@cicada@nnil{\skv@cicada@nil} +\skvnewlet\breakcicada\skvbreakloop + +\skvrobustdef*\cicadaloop{\skv@teststopt\skv@cicadaloop,} +\skvrobustdef*\skv@cicadaloop[#1]#2#3\fi#4{% + \let\if@cicada@st\ifskv@tempst + \skvpushstate\skv@cicada@state\cicadaloopdepth + \let\ifskv@tempst\if@cicada@st + \skv@usetempst{#2}\skv@cicada@userlist + \skv@foreach@normalize[#1]\skv@cicada@userlist + \def\skv@cicada@callback##1{#4}% + \def\lastcicada{}% + \def\skv@cicada@getnext##1#1##2\skv@cicada@stop{% + \edef\nextcicada{\skvexpandonce{\@gobble##1}}% + \ifx\nextcicada\skv@cicada@nnil + \lastcicadatrue + \def\nextcicada{}% + \fi + }% + \chardef\cicadacount\skvz@ + \def\skv@cicada@do##1#1##2\skv@cicada@stop{% + \edef\currentcicada{\skvexpandonce{\@gobble##1}}% + \skvifx\currentcicada\skv@cicada@nnil{% + \skvbreakloopfalse\lastcicadafalse + }{% + \skvifx\currentcicada\skv@listbreakertoks{% + \skvbreakloopfalse\lastcicadafalse + }{% + \edef\cicadacount{\the\numexpr\cicadacount+1}% + \skv@cicada@lookahead\skv@cicada@getnext##2\skv@cicada@stop + \skvexpandbracenext\skv@cicada@callback\currentcicada\relax + \skvifnotcond#3\fi{}\skvbreakloop + \skvifdefbool{skvbreakloop}{% + \skvbreakloopfalse\lastcicadafalse + \begingroup + \def\skv@prova####1\skv@cicada@nil#1{% + \endgroup + \edef\skvremainder{\unexpanded{####1}}% + }% + \skv@prova##2% + }{% + \let\lastcicada\currentcicada + \skv@cicada@lookahead\skv@cicada@do##2\skv@cicada@stop + }% + }% + }% + }% + \def\skv@cicada@lookahead##1{% + \def\skv@cicada@continue{##1.}% + \futurelet\skv@next\skv@cicada@lookahead@a + }% + \def\skv@cicada@lookahead@a{% + \ifx\skv@next[% + \expandafter\skv@cicada@lookahead@b + \else + \ifx\skv@next(% + \expandafter\expandafter\expandafter\skv@cicada@lookahead@c + \else + \expandafter\expandafter\expandafter\skv@cicada@continue + \fi + \fi + }% + \def\skv@cicada@lookahead@b[##1]{\skv@cicada@continue[{##1}]}% + \def\skv@cicada@lookahead@c(##1){\skv@cicada@continue({##1})}% + \skvbreakloopfalse\lastcicadafalse + \skvifx\skv@cicada@userlist\@empty{% + \ifskvprocessempty\skv@cicada@callback{}\fi + }{% + \skvexpandnext{\skv@cicada@lookahead\skv@cicada@do}% + \skv@cicada@userlist#1\skv@cicada@nil#1\skv@cicada@stop + }% + \skvpopstate\skv@cicada@state\cicadaloopdepth +} + +% \cicadiloop[]{}<\if...>\fi{}{<1.parameter.code>} +% \cicadiloop*[]{}<\if...>\fi{}{<1.parameter.code>} +% +% It is easy to forget to turn conditional #3 off before commencing +% the loop, especially if it is a boolean. The conditional might have +% been set true before calling \cicadaloop, and this may invalidate +% later tests to terminate the loop prematurely. #4 can be used to turn +% the condtional off before commencing the loop. +% +\skvrobustdef*\cicadiloop{\skv@teststopt\skv@cicadiloop,} +\skvrobustdef*\skv@cicadiloop[#1]#2#3\fi#4{% + \skvexpandsecond + {#4\relax\skv@cicadiloop}{\ifskv@tempst*\fi}% + [#1]{#2}#3\fi +} + +% \lpchangelistseparator +% {}{}{}{} +% +% Example: +% \def\alist{{a/1,a/2};b;c} +% \lpchangelistseparator{;}\alist{,}\blist +% +\skvrobustdef*\lpchangelistseparator#1#2#3#4{% + \def#4{}% + \cicadaloop*[#1]{#2}\if01\fi{}{}{% + \edef#4{% + \skvexpandonce#4{\unexpanded{##1}}% + \iflastcicada\else#3\fi + }% + }% +} + +\loops@restorecodes +\endinput -- cgit v1.2.3