diff options
author | Manuel Pégourié-Gonnard <mpg@elzevir.fr> | 2012-10-06 17:56:48 +0000 |
---|---|---|
committer | Manuel Pégourié-Gonnard <mpg@elzevir.fr> | 2012-10-06 17:56:48 +0000 |
commit | 882042b03c776e81243fe31ccec8f5ad90895859 (patch) | |
tree | 60b60cb9237409939598288160a69ea59718d05e /Master/texmf-dist/tex/latex/loops | |
parent | 9fa32f39fa9dac70e5fa71568d71fed16f73c68d (diff) |
new latex package: loops
git-svn-id: svn://tug.org/texlive/trunk@27908 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/loops')
-rw-r--r-- | Master/texmf-dist/tex/latex/loops/loops.sty | 1211 |
1 files changed, 1211 insertions, 0 deletions
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{<parser>}{<list>}{<target.item>}{<do.on.target>} +% +% 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{<list>}<callback.macro> +% +% 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{<list>}\do{<1.parameter.callback>} +% \ayeloop*{<listcmd>}\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{<list>}{<1.parameter.callback>} +% \sifakaloop*{<listcmd>}{<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[<options>]{<list>}{<parametered.callback>} +% \foxloop*[<options>]{<listcmd>}{<parametered.callback>} +% +% 1. The <callback> 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 <arg delimiter>. +% +% 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 <callback>. It can be defined +% outside <callback> and called within <callback>. +% +% 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{}% + % <formula> #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, <formula> may have the syntax \pgfmathresult{<token>}. + \skvexpandonce\skv@provc + \let\skvexpandonce\skv@provb\noexpand\pgfmathresult + \else + % Here, <formula> 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. <condition> 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=<holder.macro> as <child.macro> initially <value> +% +\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[<depth>]{<code>} +\skvnewlet\atbeginfoxloop\atbeginforeach +% \atendfoxloop[<depth>]{<code>} +\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<parser> +\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<parser><remainder><list.type> +\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 <RETURN> 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<parser><remainder><list.type> +\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<parser><remaining.items> +\skvrobustdef*\skv@foxloop@getrm#1#2{% + \begingroup + \def\reserved@a##1\skv@foxloop@nil#1{% + \endgroup + \edef\foxlistremainder{\unexpanded{##1}}% + }% + \reserved@a#2% +} + +% \cicadaloop[<parser>]{<list>}<\if...>\fi{<1.parameter.code>} +% \cicadaloop*[<parser>]{<listcmd>}<\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[<parser>]{<list>}<\if...>\fi{<ini-code>}{<1.parameter.code>} +% \cicadiloop*[<parser>]{<listcmd>}<\if...>\fi{<ini-code>}{<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 +% {<original.parser>}{<orig.listcmd>}{<new.parser>}{<new.listcmd>} +% +% 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 |