summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex')
-rw-r--r--Master/texmf-dist/tex/latex/loops/loops.sty1211
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