From 705b4d8f092a56d9ae900940d265fb6f55b3033d Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Tue, 6 Nov 2012 22:35:43 +0000 Subject: loops (6nov12) git-svn-id: svn://tug.org/texlive/trunk@28196 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/latex/loops/README | 83 ++++-- Master/texmf-dist/tex/latex/loops/loops.sty | 414 +++++++++++++++++++++------- 2 files changed, 383 insertions(+), 114 deletions(-) (limited to 'Master/texmf-dist') diff --git a/Master/texmf-dist/doc/latex/loops/README b/Master/texmf-dist/doc/latex/loops/README index ca4a68a07e6..955e90f4994 100644 --- a/Master/texmf-dist/doc/latex/loops/README +++ b/Master/texmf-dist/doc/latex/loops/README @@ -6,7 +6,7 @@ Ahmed Musa VERSION -Version 1.1, October 2012. +Version 1.1, 2012/10/16. LOCATION ON CTAN @@ -124,8 +124,8 @@ The looping macros include: \skvtfor: - \skvtfor{}\do{} - \skvtfor*{}\do{} + \skvtfor:={}\do{} + \skvtfor*:={}\do{} A variant of LaTeX kernel's \@tfor that has a different approach to breaking the loop prematurely. @@ -307,18 +307,19 @@ The looping macros include: restore the values of holder macros and some other internal parameters/quantities. - There is a long list of options/keys in , available for - \newforeach and \foxloop. For example, key 'reverse list' allows users - to automatically reverse their lists prior to processing. - - Also, there are many internal macros that can be accessed by the user. + There is a long list of options/keys in , available for + \newforeach and \foxloop or \foreachfox. For example, key 'reverse list' + allows users to automatically reverse their lists prior to processing. + + 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. - + \ifforeachlastitem, \foreachnestdepth, \foreachlistremainder, + \skvforeachonlyinitially. + \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 @@ -334,6 +335,11 @@ The looping macros include: to terminate the list prematurely. \listpauser will pause the processing for user action. + \newforeach found a bug in PGF's \foreach, as reported at + . However, \newforeach itself + can't be said to be bug free. + Maybe you fancy the following syntactic sugar, which PGF's \foreach can't presently process: @@ -344,7 +350,8 @@ The looping macros include: \parindent-20pt \begin{tikzpicture} \draw[step=.5cm,blue!65,very thin] (0,0) grid (13,6); - \newforeach* \x [ + \newforeach \x [ + expand list once, count in=\xc all \x satisfying \ifnum\x>5\fi ] in \alist { \newforeach [ @@ -411,13 +418,55 @@ The looping macros include: \end{document} - \newforeach found a bug in PGF's \foreach, as reported at - . However, \newforeach itself - can't be said to be bug free. + \begin{document} + \begin{tikzpicture}[scale=2] + \let\pgftrun\pgfmathtruncatemacro + \newforeach [count=\ic] \x/\y in {1/.5,1/1,0/1,0/0,1/0,1/-1,0/-1,0/-.5,0/0} + \coordinate (p\ic) at (\x,\y); + \draw[line width=1pt,blue] (p1) \newforeach \p in {2,...,8} {-- (p\p)}; + \newforeach \q in {1,...,7} {% + \pgftrun{\qa}{\q+1}\pgftrun{\qb}{\q+2}\pgftrun{\ind}{2*\q-1}% + \pgftrun{\next}{2*\q}% + \coordinate (n\ind) at (barycentric cs:p\q=0.5,p\qa=0.5); + \coordinate (n\next) at (barycentric cs:p\q=0.125,p\qa=0.75,p\qb=0.125); + } + \draw[line width=2pt,red] (n1)\newforeach \q in {2,...,13}{--(n\q)}; + \end{tikzpicture} + \end{document} + + + % A complete graph + % Example credit: + % Quintin Jean-Noël + % + \usepackage[active,tightpage]{preview} + \PreviewEnvironment{tikzpicture} + \setlength\PreviewBorder{5pt}% + \usetikzlibrary[topaths] + \newcount\mycount + \begin{document} + \begin{tikzpicture}[transform shape] + \newforeach \nr in {1,...,8}{ + \mycount=\numexpr(\nr-1)*45\relax + \node[draw,circle,inner sep=0.25cm] (N-\nr) at + (\the\mycount:5.4cm) {}; + } + \newforeach \nr in {9,...,16}{ + \mycount=\numexpr(\nr-1)*45+22\relax + \node[draw,circle,inner sep=0.25cm] (N-\nr) at + (\the\mycount:5.4cm) {}; + } + \newforeach \nr in {1,...,15}{ + \mycount=\numexpr\nr+1\relax + \newforeach \nra in {\the\mycount,...,16}{ + \path (N-\nr) edge[->,bend right=3] (N-\nra) + edge[<-,bend left=3](N-\nra); + } + } + \end{tikzpicture} -\foxloop: +\foxloop or \foreachfox: \foxloop[]{}{} \foxloop*[]{}{} diff --git a/Master/texmf-dist/tex/latex/loops/loops.sty b/Master/texmf-dist/tex/latex/loops/loops.sty index f05a61d6c10..351205fdd4e 100644 --- a/Master/texmf-dist/tex/latex/loops/loops.sty +++ b/Master/texmf-dist/tex/latex/loops/loops.sty @@ -1,5 +1,5 @@ %++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++% -% This is file 'loops.sty', version 1.1, October 2012. % +% This is file 'loops.sty', version 1.1, 2012/10/16. % % % % This package and accompanying files may be distributed and/or % % modified under the conditions of the LaTeX Project Public License, % @@ -53,7 +53,7 @@ \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/10/14 v1.1 Loops and list processors (AM)] +\ProvidesPackage{loops}[2012/10/16 v1.1 Loops and list processors (AM)] \NeedsTeXFormat{LaTeX2e}[2011/06/27] % This is the way to load 'skeyval-for-core' here, otherwise we @@ -64,6 +64,20 @@ \skvnewlet\ltxkernelfor\@for \skvnewlet\ltxkerneltfor\@tfor +\directkeys*{ + .every unknown option={ + \PackageWarning{loops}{Unknown option '\CurrentOption' ignored}% + }, + .prefix=SKV, + .family=loops, + .holder prefix=lps@, + .initialize keys after define, + .new options={ + .bool/verbose/true/\iflps@verbose\skv@verbosetrue\fi, + }, + .copy class options and process options, +} + % \skvusetwoprev{}{}{}{} % % Example: @@ -71,7 +85,7 @@ % \skvusetwoprev{,}{a,b,c,d,e,f}{e}{} % \skvrobustdef*\skvusetwoprev#1#2#3#4{% - \skvifin{#1#3#1}{#1#2#1}{}{% + \skvifinTF{#1#3#1}{#1#2#1}{}{% \skv@err{There is not '\detokenize{#3}' in the list}\@ehd }% \let\firstprev\@empty @@ -93,11 +107,29 @@ \skv@do#2#1\skv@nnil#1% } -% A version of D.E. Knuth's \loop: +% Remove one element from a list. Only the first occurence of the +% element will be removed. This is fast. +% +% \lpsremovefromlist{} +% +% Eg, \def\x{1,2,3,4} \lpsremovefromlist{,}7\x +% +\skvrobustdef\lpsremovefromlist#1#2#3{% + \begingroup + \def\lps@remove@a##1\lps@nil{##1}% + \def\lps@remove@b##1\lps@nil{}% + \def\lps@remove@c##1#1#2##2\lps@nil##3\lps@nil##4\lps@nnil{% + \expandafter\endgroup##3\def#3{##1##2}\lps@nil + }% + \expandafter\lps@remove@c\expandafter#1#3\lps@nil + \lps@remove@a\lps@nil#1#2\lps@nil\lps@remove@b\lps@nil\lps@nnil +} + +% Expandable version of D.E. Knuth's \loop: % \skvnewdef\dekloop#1\repeat{\skv@dekloop{#1}} \skvnewdef\skv@dekloop#1{% - #1\relax + #1% \expandafter\skv@dekloop \else \expandafter\@gobble @@ -105,6 +137,11 @@ {#1}% } +\skvnewdef\lpswhilecondition#1\do#2{% + \csname @#1firstofone\else gobble\fi\endcsname + {#2\lpswhilecondition{#1}\do{#2}}% +} + % A version of D.E. Knuth's \loop that can be nested. % Credit: David P. Carlisle. % @@ -170,16 +207,16 @@ \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{% + \skvifntypeTF{#1}{% + \skvifxTF{#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@{% + \skvifstrcmpTF{#1}\repeat{% + \skvifnumTF\skv@aktercnt=\skvz@{% \expandafter\endgroup\the\skv@aktertoks\repeat }{% \skv@aktertoks\expandafter @@ -188,7 +225,7 @@ \skv@akterpush }% }{% - \skvifx\next\@sptoken{% + \skvifxTF\next\@sptoken{% \skv@aktertoks\expandafter {\the\expandafter\skv@aktertoks\@space#1}% }{% @@ -198,10 +235,10 @@ }% }% }{% - \skvifstrcmp{#1}{EOF}{% + \skvifstrcmpTF{#1}{EOF}{% \skv@err{'\string\repeat' not found: endless loop}\@ehd }{% - \skvifx\next\@sptoken{% + \skvifxTF\next\@sptoken{% \skv@aktertoks\expandafter {\the\expandafter\skv@aktertoks\space{#1}}% }{% @@ -230,10 +267,10 @@ % \skvnewdef\skvfor#1#2{\skv@for@a{#2}#1,\skv@for@nil,} \skvnewdef\skv@for@a#1#2,{% - \skvifblank{#2}{% + \skvifblankTF{#2}{% \skv@for@a{#1}% }{% - \skvifstrcmp{#2}\skv@for@nil{}{#1{#2}\skv@for@a{#1}}% + \skvifstrcmpTF{#2}\skv@for@nil{}{#1{#2}\skv@for@a{#1}}% }% } @@ -259,7 +296,7 @@ \skv@usetempst{#1}\skv@tempa \skvsavestate\ayeloop{\do\skv@ayeloop}\skv@csvdepth \def\skv@ayeloop##1{% - \skvifstrcmp{##1}\skv@tfor@nil{% + \skvifstrcmpTF{##1}\skv@tfor@nil{% \skvbreakloopfalse }{% #2\relax @@ -295,7 +332,7 @@ \expandafter\skvtsvnormalize\expandafter{\skv@sifakaarg}% }% \skvbreakloopfalse - \skvifempty\skv@sifakaarg{% + \skvifemptyTF\skv@sifakaarg{% \ifskvprocessempty\skv@user@do{}\fi }{% \expandafter\skv@sifakaparse@b\skv@sifakaarg\skv@tsvnil @@ -315,25 +352,25 @@ }% } \skvrobustdef*\skv@sifakaparse@c#1{% - \skvifx\skv@next\skv@orig@bgroup{% + \skvifxTF\skv@next\skv@orig@bgroup{% \endgroup \edef\skv@sifakaarg{{\unexpanded{#1}}}% }{% \endgroup \edef\skv@sifakaarg{\unexpanded{#1}}% }% - \skvifx\skv@sifakaarg\skv@tsvnil{% + \skvifxTF\skv@sifakaarg\skv@tsvnil{% \skvbreakloopfalse }{% - \skvifx\skv@sifakaarg\skv@listbreakertoks{% + \skvifxTF\skv@sifakaarg\skv@listbreakertoks{% \skvbreakloop }{% - \skvifempty\skv@sifakaarg{% + \skvifemptyTF\skv@sifakaarg{% \ifskvprocessempty\skv@user@do{}\fi }{% \expandafter\skv@user@do\expandafter{\skv@sifakaarg}\relax }% - \skvifdefbool{skvbreakloop}{% + \skvifdefboolTF{skvbreakloop}{% \skvbreakloopfalse \begingroup \def\skv@prova##1\skv@tsvnil{% @@ -389,15 +426,11 @@ % 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{skvfoxloop}{% +\quickkeys.define{loops/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 @@ -409,11 +442,11 @@ 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} + \edef\skv@foreach@normalizelist{0\skvifstrcmpTF{#1}{true}01} /true,false ; use empty,use empty items/true/ - \edef\skv@foreach@useempty{0\skvifstrcmp{#1}{true}01} + \edef\skv@foreach@useempty{0\skvifstrcmpTF{#1}{true}01} /true,false ; .exec/ @@ -449,7 +482,7 @@ \skv@err{Value required for key 'grow'}\@ehd \else \skvcsedef{skv@foreach@grow#1}{00}% - \edef\skv@prova{\skvifstrcmp{#1}{left}{right}{left}}% + \edef\skv@prova{\skvifstrcmpTF{#1}{left}{right}{left}}% \skvcsedef{skv@foreach@grow\skv@prova}{01}% \fi \fi @@ -461,38 +494,51 @@ ; grow left/true/ \ifskvindef\else - \edef\skv@foreach@growleft{0\skvifstrcmp{true}{#1}01}% - \edef\skv@foreach@growright{0\skvifstrcmp{true}{#1}10}% + \edef\skv@foreach@growleft{0\skvifstrcmpTF{true}{#1}01}% + \edef\skv@foreach@growright{0\skvifstrcmpTF{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}% + \edef\skv@foreach@growright{0\skvifstrcmpTF{true}{#1}01}% + \edef\skv@foreach@growleft{0\skvifstrcmpTF{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}% + \edef\skv@foreach@keepdecimals{0\skvifstrcmpTF{true}{#1}01}% /true,false ; keep high decimals,keep only high decimals/true/ - \edef\skv@foreach@keephighdecimals{0\skvifstrcmp{true}{#1}01}% + \edef\skv@foreach@keephighdecimals{0\skvifstrcmpTF{true}{#1}01}% + /true,false + ; + expand before fill,expand list before fill/true/ + \edef\skv@foreach@expandlistbeforefill{0\skvifstrcmpTF{true}{#1}01}% /true,false ; - expand before fill/true/ - \edef\skv@foreach@expandbeforefill{0\skvifstrcmp{true}{#1}01}% + expand list once,*,list is a macro,macro list/true/ + \edef\skv@foreach@expandlistonce{0\skvifstrcmpTF{true}{#1}01}% + /true,false + ; + expand list twice/true/ + \edef\skv@foreach@expandlisttwice{0\skvifstrcmpTF{true}{#1}01}% + /true,false + ; + % Don't use 'expand' for the key 'expand list': + expand list,fully expand list,expand list fully/true/ + \edef\skv@foreach@expandlistfully{0\skvifstrcmpTF{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. + % call to \skv@foxloop@parseevaluate until the arguments are known. evaluate/\@nil/ \skv@foreach@ifacceptkv{#1}{% - \skv@fox@parseevaluate{#1}% + \skv@foxloop@parseevaluate{#1}% }% ; % Eg, @@ -500,25 +546,24 @@ % 'count in = \p all #1 satisfying \ifnum#1>4\fi' count in,count/\@nil/ \skv@foreach@ifacceptkv{#1}{% - \skv@fox@parsecountin{#1}% + \skv@foxloop@parsecountin{#1}% }% ; % Eg, remember=#1 as \y initially A remember/\@nil/ \skv@foreach@ifacceptkv{#1}{% - \skv@fox@parseremember{#1}% + \skv@foxloop@parseremember{#1}% }% ; - reverse list,reverse/true/ - \edef\skv@foreach@reverselist{0\skvifstrcmp{true}{#1}01}% + reverse list,reverse,reflect/true/ + \edef\skv@foreach@reverselist{0\skvifstrcmpTF{true}{#1}01}% /true,false ; } % Set up \foxloop defaults: -\quickkeys.set{skvfoxloop}{% +\quickkeys.set{loops/foxloop}{% parser={,}, - list is a macro=false, list type=csv, arg=#1, list breaker=\listbreaker, @@ -529,19 +574,22 @@ keep high decimals=false, expand before fill=false, reverse list=false, + expand list once=false, + expand list twice=false, + expand list fully=false, } % Parser for 'evaluate' key: % % Eg, 'evaluate = #1 as \x using \numexpr#1*2'. % -% \skv@fox@parseevaluate differs from \skv@foreach@parseevaluate +% \skv@foxloop@parseevaluate differs from \skv@foreach@parseevaluate % because #1 here is likely to be a non-macro. % -\skvrobustdef*\skv@fox@parseevaluate#1{% +\skvrobustdef*\skv@foxloop@parseevaluate#1{% \begingroup \def\skv@tempxifin##1##2{% - \skvxifin{\detokenize{##1}}{\detokenize{##2}}% + \skvxifinTF{\detokenize{##1}}{\detokenize{##2}}% }% \def\skv@tempd{}% % #3 can be empty. So we don't test for its emptiness here. @@ -557,7 +605,7 @@ \skv@err{Value of key 'evaluate' has no receiving macro, \MessageBreak i.e., after 'as'}\@ehd \else - \expandafter\skvifescaped\expandafter{\skv@provb}{}{% + \expandafter\skvifescapedTF\expandafter{\skv@provb}{}{% \skv@err{Value of key 'evaluate': receiving macro \MessageBreak \skvoxdetok\skv@provb not escaped}\@ehd }% @@ -664,10 +712,10 @@ % % \iflastfox\typeout{Processing last entry of the list} \fi % -\skvrobustdef*\skv@fox@parsecountin#1{% +\skvrobustdef*\skv@foxloop@parsecountin#1{% \begingroup \def\skv@tempxifin##1##2{% - \skvxifin{\detokenize{##1}}{\detokenize{##2}}% + \skvxifinTF{\detokenize{##1}}{\detokenize{##2}}% }% \def\skv@tempa##1\skv@nil{% \skv@tempxifin{initially}{##1}{% @@ -684,7 +732,7 @@ \ifx\skv@prova\@empty \skv@err{Value of key 'count' has no counter macro}\@ehd \else - \expandafter\skvifescaped\expandafter{\skv@prova}{}{% + \expandafter\skvifescapedTF\expandafter{\skv@prova}{}{% \skv@err{Value of key 'count':\MessageBreak counter macro \skvoxdetok\skv@prova not escaped}\@ehd }% @@ -692,7 +740,7 @@ \ifx\skv@provd\@empty \expandafter\def\skv@prova{0}% \else - \skvifinteger\skv@provd{% + \skvifintegerTF\skv@provd{% \expandafter\let\skv@prova\skv@provd }{% \skv@err{Value of key 'count':\MessageBreak @@ -730,7 +778,7 @@ \ifx\@nnil#1\@nnil \skv@err{Value of key 'count': no counter macro}\@ehd \else - \skvifntype{#1}{% + \skvifntypeTF{#1}{% \skv@tempa#1all satisfying\skv@nil }{% \skv@err{Value of key 'count in': more than 1 @@ -761,10 +809,10 @@ % remember= as initially % -\skvrobustdef*\skv@fox@parseremember#1{% +\skvrobustdef*\skv@foxloop@parseremember#1{% \begingroup \def\skv@tempxifin##1##2{% - \skvxifin{\detokenize{##1}}{\detokenize{##2}}% + \skvxifinTF{\detokenize{##1}}{\detokenize{##2}}% }% \skv@tempxifin{(}{#1}{% \skv@err{Value of key 'remember': '(' found. @@ -783,7 +831,7 @@ \skv@err{Value of key 'remember' has no child macro; \MessageBreak i.e., no macro after 'as'}\@ehd \else - \expandafter\skvifescaped\expandafter{\skv@provb}{% + \expandafter\skvifescapedTF\expandafter{\skv@provb}{% \skv@foreach@swatrue }{% \skv@err{Value of key 'remember': @@ -839,12 +887,12 @@ \skvnewdef*\skv@foxloop@kvlist{} % \setupfoxloop{} \skvrobustdef*\setupfoxloop#1{% - \skvifblank{#1}{}{% + \skvifblankTF{#1}{}{% \skvaddtolist\skv@foxloop@kvlist{#1}% }% } \skvrobustdef*\skv@foxloop@setkeys#1{% - \def\reserved@a{\quickkeys.set{skvfoxloop}}% + \def\reserved@a{\quickkeys.set{loops/foxloop}}% \expandafter\reserved@a\expandafter{\skv@foxloop@kvlist,#1}% } @@ -856,31 +904,52 @@ \skvrobustdef*\foxloop{\skv@teststopt\skv@foxloop{}} \skvnewlet\foreachfox\foxloop \skvrobustdef*\skv@foxloop[#1]#2#3{% - \ifskv@tempst - \def\skv@foreach@st{00}% - \else - \def\skv@foreach@st{01}% - \fi + \edef\skv@foreach@seenstar{0\ifskv@tempst0\else1\fi}% \skv@foreach@pushstate \skv@foreach@latehookerr \def\foreachitemcount{0}% \def\skv@foreach@callback{}% + \def\skv@foreach@afterprecallback{}% \def\skv@foreach@precallback{}% \def\skv@foreach@postcallback{}% \def\skv@foreach@onlyinitially{}% \skv@foxloop@setkeys{#1}% - \if\skv@foreach@st\else - \if\skv@foreach@listismacro - \def\skv@foreach@st{00}% - \else - \def\skv@foreach@st{01}% - \fi + \if\skv@foreach@seenstar + \def\skv@foreach@expandlistonce{00}% + \fi + \ifcase0% + \if\skv@foreach@expandlistonce\else + \if\skv@foreach@expandlisttwice\else + \if\skv@foreach@expandlistfully\else 1\fi\fi\fi\relax + \def\skv@foreach@listisamacro{00}% + \else + \def\skv@foreach@listisamacro{01}% + \skvifmacroFT{#2}{}{% + \ifskv@verbose + \skv@warn{One-item list '\detokenize{#2}' is a macro. + \MessageBreak If your list is indeed a macro, you can + \MessageBreak either put star (*) after + \detokenize{\foxloop or \foreachfox} + \MessageBreak or use the key 'expand list once' + \MessageBreak or 'expand list twice' or 'list is a macro', etc}% + \fi + }% \fi \edef\skv@foreach@userlist{% - \if\skv@foreach@st + \if\skv@foreach@expandlistonce \expandafter\skvexpandonce \else - \expandafter\unexpanded + \if\skv@foreach@expandlisttwice + \expandafter\expandafter\expandafter\skvexpandtwice + \else + \if\skv@foreach@expandlistfully + \expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter\@iden + \else + \expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter\unexpanded + \fi + \fi \fi {#2}% }% @@ -901,7 +970,7 @@ \skv@foreach@paramlist\noexpand\foreacheov }% \expandafter\reserved@b\expandafter{\reserved@a}% - \skvxifstrcmp\skv@foreach@listtype{csv}{% + \skvxifstrcmpTF\skv@foreach@listtype{csv}{% \def\skv@foreach@lookahead@a##1{% \def\skv@foreach@continue{##1.}% \futurelet\skv@foreach@next\skv@foreach@lookahead@b @@ -917,10 +986,10 @@ \def\skv@foreach@lookahead@d(##1){\skv@foreach@continue{(##1)}}% }% \def\skv@foreach@lookahead@b{% - \skvifx\skv@foreach@next[{% + \skvifxTF\skv@foreach@next[{% \skv@foreach@parenthtrue\skv@foreach@lookahead@c }{% - \skvifx\skv@foreach@next({% + \skvifxTF\skv@foreach@next({% \skv@foreach@parenthtrue\skv@foreach@lookahead@d }{% \skv@foreach@parenthfalse\skv@foreach@continue @@ -930,7 +999,7 @@ \skvbreakloopfalse\lastfoxfalse \def\reserved@a##1{% \skv@setupgetnextfox{##1}% - \skvxifstrcmp\skv@foreach@listtype{csv}{% + \skvxifstrcmpTF\skv@foreach@listtype{csv}{% \if\skv@foreach@normalizelist \skv@foreach@normalize[##1]\skv@foreach@userlist \fi @@ -938,7 +1007,7 @@ \edef\currentfox{\skvexpandonce{\@gobble####1}}% \skv@foxloop@a{##1}{####2}{csv}% }% - \skvifempty\skv@foreach@userlist{}{% + \skvifemptyTF\skv@foreach@userlist{}{% \expandafter\skv@foreach@filllist\expandafter {\skv@foreach@parser}\skv@foreach@userlist \if\skv@foreach@reverselist\skvafterfi @@ -965,7 +1034,7 @@ \ifskv@foreach@parenth \edef\currentfox{\unexpanded{####1}}% \else - \skvifx\skv@foreach@next\bgroup{% + \skvifxTF\skv@foreach@next\bgroup{% \edef\currentfox{{\unexpanded{####1}}}% }{% \edef\currentfox{\unexpanded{####1}}% @@ -983,7 +1052,7 @@ } % \skv@setupgetnextfox \skvrobustdef*\skv@setupgetnextfox#1{% - \skvxifstrcmp\skv@foreach@listtype{csv}{% + \skvxifstrcmpTF\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. @@ -999,7 +1068,7 @@ \ifskv@foreach@parenth \edef\nextfox{\unexpanded{##1}}% \else - \skvifx\skv@foreach@next\bgroup{% + \skvifxTF\skv@foreach@next\bgroup{% \edef\nextfox{{\unexpanded{##1}}}% }{% \edef\nextfox{\unexpanded{##1}}% @@ -1010,18 +1079,18 @@ } % \skv@foxloop@a \skvrobustdef*\skv@foxloop@a#1#2#3{% - \skvifx\currentfox\skv@foxloop@nnil{% + \skvifxTF\currentfox\skv@foxloop@nnil{% \skvbreakloopfalse\lastfoxfalse }{% - \skvifx\currentfox\skv@listbreakertoks{% + \skvifxTF\currentfox\skv@listbreakertoks{% \skv@foxloop@getrm{#1}{#2}% }{% - \skvifx\currentfox\skv@listpausertoks{% + \skvifxTF\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}{% + \skvxifstrcmpTF\@gtempa{x}{% \skv@foxloop@getrm{#1}{#2}% }{% \skv@foxloop@b{#1}{#2}{#3}% @@ -1045,15 +1114,15 @@ \ifx\skv@foreach@itemcounter\@nnil-1\else+0\fi\relax\relax \expandafter\let\skv@foreach@itemcounter\foreachitemcount \fi - \skvifempty\currentfox{% + \skvifemptyTF\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{% + \skvifxTF\currentfox\skv@listpausertoks{}{% + \skvifxTF\skv@foreach@paramlist\skv@simplearg{% \expandafter\skv@foreach@callback\expandafter {\currentfox}\foreacheov }{% @@ -1062,7 +1131,7 @@ }% }% \skv@foreach@exittester{\skvbreakloop}{}% - \skvifdefbool{skvbreakloop}{% + \skvifdefboolTF{skvbreakloop}{% \skvbreakloopfalse\lastfoxfalse \skv@foxloop@getrm{#1}{#2}% }{% @@ -1146,17 +1215,17 @@ \chardef\cicadacount\skvz@ \def\skv@cicada@do##1#1##2\skv@cicada@stop{% \edef\currentcicada{\skvexpandonce{\@gobble##1}}% - \skvifx\currentcicada\skv@cicada@nnil{% + \skvifxTF\currentcicada\skv@cicada@nnil{% \skvbreakloopfalse\lastcicadafalse }{% - \skvifx\currentcicada\skv@listbreakertoks{% + \skvifxTF\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}{% + \skvifcondFT#3\fi{}\skvbreakloop + \skvifdefboolTF{skvbreakloop}{% \skvbreakloopfalse\lastcicadafalse \begingroup \def\skv@prova####1\skv@cicada@nil#1{% @@ -1189,7 +1258,7 @@ \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{% + \skvifxTF\skv@cicada@userlist\@empty{% \ifskvprocessempty\skv@cicada@callback{}\fi }{% \skvexpandnext{\skv@cicada@lookahead\skv@cicada@do}% @@ -1214,16 +1283,18 @@ [#1]{#2}#3\fi } -% \lpchangelistseparator +% Change the list separator of a given list. +% +% \lpschangelistseparator % {}{}{}{} % % Example: % \def\alist{{a/1,a/2};b;c} -% \lpchangelistseparator{;}\alist{,}\blist +% \lpschangelistseparator{;}\alist{,}\blist % -\skvrobustdef*\lpchangelistseparator#1#2#3#4{% +\skvrobustdef*\lpschangelistseparator#1#2#3#4{% \def#4{}% - \cicadaloop*[#1]{#2}\if01\fi{}{}{% + \cicadaloop*[#1]{#2}\if01\fi{% \edef#4{% \skvexpandonce#4{\unexpanded{##1}}% \iflastcicada\else#3\fi @@ -1231,5 +1302,154 @@ }% } +% Declare a list normalizer command for an arbitrary parser. +% +% \lpsdeclarelistnormalizer{} +% +% Eg, +% +% \lpsdeclarelistnormalizer\lpscommanormalize{,} +% +% \lpscommanormalize is defined later. +% +\skvrobustdef*\lpsdeclarelistnormalizer#1#2{% + \skvifescapedTF{#1}{}{% + \skv@err{Token \detokenize{#1} isn't escaped}\@ehd + }% + \def\reserved@a{\lps@declarelistnormalizer{#1}}% + \expandafter\reserved@a\expandafter{\string#2}% +} +\skvrobustdef*\lps@declarelistnormalizer#1#2{% + \begingroup + \lccode`\~=`#2 % + \lowercase{\endgroup + \skvnewdef#1##1{% + \unexpanded\expandafter{\romannumeral-`\q + \skvcsuse{\skvremovescape#1@activeparser}#2##1#2~\normal@nil}% + }% + \long\skvcsdef{\skvremovescape#1@activeparser}##1~##2\normal@nil{% + \skvifblankTF{##2} + {\skvcsuse{\skvremovescape#1@spaceparser}##1 #2\normal@nil} + {\skvcsuse{\skvremovescape#1@activeparser}##1#2##2\normal@nil}% + }% + }% + \long\skvcsdef{\skvremovescape#1@spaceparser}##1 #2##2\normal@nil{% + \skvifblankTF{##2} + {\skvcsuse{\skvremovescape#1@parserspace}##1#2 \normal@nil} + {\skvcsuse{\skvremovescape#1@spaceparser}##1#2##2\normal@nil}% + }% + \long\skvcsdef{\skvremovescape#1@parserspace}##1#2 ##2\normal@nil{% + \skvifblankTF{##2} + {\skvcsuse{\skvremovescape#1@doubleparser}##1#2#2\normal@nil} + {\skvcsuse{\skvremovescape#1@parserspace}##1#2##2\normal@nil}% + }% + \long\skvcsdef{\skvremovescape#1@doubleparser}##1#2#2##2\normal@nil{% + \skvifblankTF{##2} + {\skvifblankTF{##1}{}{\expandafter\expandafter\expandafter + \space\expandafter\noexpand\@gobble##1}}% + {\skvcsuse{\skvremovescape#1@doubleparser}##1#2##2\normal@nil}% + }% +} + +\lpsdeclarelistnormalizer\lpscommanormalize{,} +\lpsdeclarelistnormalizer\lpsslashnormalize{/} + +% Declare an expandable csv list parser. No premature stopping of the loop +% is possible by this scheme. To use, first do +% +% \lpsdeclareexpandablelistparser{} +% \def{<1-parameter callback>} +% +% and then call +% +% or *. +% +% Example: +% +% \lpsdeclareexpandablelistparser\myloop\do{,} +% +% \def\x{a,\if,b} +% \def\do#1{\unexpanded{{#1}}} +% \edef\x{\myloop*\x} +% +\skvrobustdef*\lpsdeclareexpandablelistparser#1#2#3{% + % No need to save the processor.cmd and processor.function in a list. + \skvaftercs\lpsdeclarelistnormalizer{\skvremovescape#1@normalizer}{#3}% + \begingroup + \let\x\skvnoexpandcs + \skvexpanded{\endgroup + \skvnewdef*\noexpand#1{% + \noexpand\skvxifnextchar*% + {\x{lps@parse@\skvremovescape#1@a}{\noexpand\@firstoftwo}} + {\x{lps@parse@\skvremovescape#1@a}{\noexpand\@secondoftwo}}% + }% + \def\x{lps@parse@\skvremovescape#1@a}####1####2{% + \noexpand\expandafter\x{lps@parse@\skvremovescape#1@b}% + \unexpanded{\romannumeral-`\q}####1{% + \noexpand\expandafter\x{\skvremovescape#1@normalizer}% + \noexpand\expandafter{####2}% + }{% + \x{\skvremovescape#1@normalizer}{####2}% + }% + #3\noexpand#1#3% + }% + \def\x{lps@parse@\skvremovescape#1@b}####1#3{% + \noexpand\skvifxTF{\noexpand#1}{####1}% + {}{\noexpand#2{####1}\x{lps@parse@\skvremovescape#1@b}}% + }% + }% +} + +\skvrobustdef*\lpsAtBeginEnvironment#1{% + \skvaftercs\skvappendtomacro{lps@atbegin@#1@hook}% +} +\skvrobustdef*\lpsAtEndEnvironment#1{% + \skvaftercs\skvappendtomacro{lps@atend@#1@hook}% +} + +\skvpatchcmd\begin + {\csname#1\endcsname} + {\skvcsuse{lps@atbegin@#1@hook}\csname#1\endcsname} + {} + {\skv@err{Patching '\string\begin' failed!\MessageBreak + '\string\lpsAtBeginEnvironment' will not work\@gobble}\@ehd + } + +\skvpatchcmd\end + {\csname end#1\endcsname} + {\skvcsuse{lps@atend@#1@hook}\csname end#1\endcsname} + {} + {\skv@err{Patching '\string\end' failed!\MessageBreak + '\string\lpsAtEndEnvironment' will not work\@gobble}\@ehd + } + +% Without the following patch, drawing intructions like +% +% \draw[blue] (p) \newforeach \p in {1,...,8}{...}; +% +% will not work, because of the way tikZ hardwires \foreach +% for this type of task. +% +\lpsAtBeginEnvironment{tikzpicture}{% + \def\tikz@handle@more{% + \ifx\@let@token a\let\@next=\tikz@arcA\else + \ifx\@let@token e\let\@next=\tikz@e@char\else + \ifx\@let@token g\let\@next=\tikz@g@char\else + \ifx\@let@token s\let\@next=\tikz@schar\else + \ifx\@let@token |\let\@next=\tikz@vh@lineto\else + \ifx\@let@token p\let\@next=\tikz@pchar\pgfsetmovetofirstplotpoint + \else\ifx\@let@token t\let\@next=\tikz@to\else + \ifx\@let@token\pgfextra\let\@next=\tikz@extra\else + \ifx\@let@token\foreach\let\@next=\tikz@foreach\else + \ifx\@let@token\newforeach\let\@next=\tikz@foreach\else + \ifx\@let@token\pgf@stop\let\@next=\relax\else + \ifx\@let@token\par\let\@next=\tikz@scan@next@command\else + \ifx\@let@token d\let\@next=\tikz@decoration\else + \ifx\@let@token l\let\@next=\tikz@l@char\else + \let\@next=\tikz@expand\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \fi\fi\fi\fi\@next + }% +} + \loops@restorecodes \endinput -- cgit v1.2.3