%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Repeat loop macro, version 0.93a, September 2006
% Copyright Victor Eijkhout 1999-2006
% file name: repeat.tex
%
% Author:
% Victor Eijkhout
% Texas Advanced Computing Center
% The University of Texas at Austin, Austin TX 78758, USA
%
% victor@eijkhout.net
%
% This program is released under the LaTeX Project Public License
% see for further information.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% General loop macro:
% \repeat
% \for{} \from{} \by{} \to{} \downto{}
% \until{} \while{}
% \do { }
% where all control sequences in between \repeat and \do are optional.
%
% var: characters to form a control sequence;
% after \for{index} you can access the loop counter as \index.
% This is a count register; to print it use \number\index.
% start,step,end: integers with obvious relations to the loop counter;
% start and step have a default value of 1
% cond: (sequence of commands ending in) any TeX \if... test.
%
% Count down instead of up with \downto; the increment given in \by
% is always positive, and is added or subtracted accordingly.
%
% Tests: \until is evaluated at the end of the loop body; \while
% at the beginning of the loop body.
%
% Exit from middle of loop: \breakrepeat
% use this at any place in the loop; in case of a conditional use
% \ifsomething ... \expandafter \breakrepeat \fi
%
% There are some examples at the end of this file, after the \endinput line;
% remove the \endinput statement to see the examples executed.
% Note that the line ends do not cause spaces in the output.
%
% Technical details:
% The loop body is not executed in a group: the braces are those
% of a token list.
% The `for' variable is \let to a \count register.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% revision history:
% 0.9 first release, January 1999
% 0.91 documentation update,
% csarg-like control sequences made REP...
% counter update made global in case the body issues grouping,
% copyright notice, February 1999
% 0.92 installed trace switches
% 0.93 spaces are now ignored after each argument group. Author info updated.
% 0.93a removed \bye from \endinput line.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%%
%% Prevent multiple loading of this file
%%
\expandafter\ifx\csname REPdepth\endcsname\relax
\message{Loading loop macro, version 0.93a}%
\else \endinput \fi
%%
%% Auxiliary stuff
%%
\def\REPcsarg#1#2{\expandafter#1\csname#2\endcsname}
\def\REPcsrom#1{\csname #1\romannumeral\REPdepth\endcsname}
\def\REPcsargrom#1#2{\expandafter#1\csname#2\romannumeral\REPdepth\endcsname}
\newcount\REPdepth
\let\endrepeat\relax \def\csprotect{}
% Trace switches may later be defined by PAC_utils
\let\REPtraceinit\relax \let\REPtraceexit\relax
%%
%% Main repeat macro
%% - go to next level and allocate unique counter/toks if this is the
%% first time we visit this level
%% - setup: gather bounds and termination conditions
%% - scoop up body in token list; after the assignment define and
%% execute body
%%
\def\repeat#1\do{\REPtraceinit % exit in \breakrepeat
\advance\REPdepth by 1\relax
\REPcsargrom\ifx{REPcount}\relax
\REPcsargrom{\csname newcount\expandafter\endcsname}{REPcount}%
\REPcsargrom{\csname newtoks\expandafter\endcsname}{REPtoks}%
\REPcsargrom{\csname newtoks\expandafter\endcsname}{REPwtest}%
\REPcsargrom{\csname newtoks\expandafter\endcsname}{REPutest}%
\fi \REPzero \def\REPsign{}\def\REPcomp{>}\REPsetup{#1}%
\edef\REPtmp
{\def\REPcsargrom\noexpand{REPrepeat}{\REPcsargrom\noexpand{REPbody}}}\REPtmp
\afterassignment\REPdxbody\REPcsrom{REPtoks}}
%%
%% Define and execute loop body
%% This is done with an \edef to construct the actual sequence
%%
\def\REPdxbody{\REPcsargrom\edef{REPbody}{%
\REPcsargrom\the{REPwtest}%
\noexpand\the\REPcsargrom\noexpand{REPtoks}%
\REPcsargrom\the{REPutest}%
\global\REPcsargrom\advance{REPcount} by \REPsign\REPcsrom{REPinc}\relax
\noexpand\endrepeat
\REPcsargrom\noexpand{REPrepeat}}%
\REPcsrom{REPbody}\ignorespaces}
%%
%% Stop test
%% In order to stop, issue a
%% \breakrepeat which scoops up the rest of the body and exits
%%
\def\breakrepeat#1\endrepeat{\REPzero\REPcsargrom\let{REPrepeat}\relax
\advance\REPdepth by -1 \REPtraceexit
}
%%
%% Setup
%% gather bounds and termination conditions
%%
\def\REPsetup#1{%
\begingroup
\def\for##1{%
\edef\REPtmp{%
\global\let\REPcsarg\noexpand{##1}\REPcsrom{REPcount}\ignorespaces}%
\REPtmp}%
\def\from##1{\REPcsargrom\global{REPcount}##1\ignorespaces}%
\def\to##1{%
\edef\REPtmp{\global\REPcsargrom\noexpand{REPwtest}=
{\REPcsargrom\the{REPwtest}%
\noexpand\ifnum\REPcsargrom\noexpand{REPcount}\REPcomp##1\relax
\noexpand\expandafter \noexpand\breakrepeat
\noexpand\fi}\ignorespaces}%
\REPtmp}%
\def\downto##1{%
\gdef\REPsign{-}\gdef\REPcomp{<}\to{##1}\ignorespaces}%
\def\by##1{\ifnum##1<0 \message{REPEAT: increment has to be >0}%
\REPcsargrom\gdef{REPinc}{-##1}\else
\REPcsargrom\gdef{REPinc}{##1}\fi\ignorespaces}%
\def\until##1{%
\edef\REPtmp{\global\REPcsargrom\noexpand{REPutest}=
{\noexpand##1\relax
\noexpand\expandafter \noexpand\breakrepeat \noexpand\fi}\ignorespaces}%
\REPtmp}%
\def\while##1{%
\edef\REPtmp{\global\REPcsargrom\noexpand{REPwtest}=
{\noexpand##1\relax \noexpand\else
\noexpand\expandafter \noexpand\breakrepeat \noexpand\fi}\ignorespaces}%
\REPtmp}%
\from{1}\by{1}#1%
\endgroup}
\def\REPzero
{\REPcsrom{REPtoks}{}\REPcsrom{REPutest}{}\REPcsrom{REPwtest}{}%
\REPcsargrom\def{REPbody}{}}
\endinput
%\tracingmacros2
\newcount\tmpcount % used in some examples below
1
\repeat \for{i}
\by{2}
\do {%
\ifnum\i=13 \expandafter\breakrepeat \fi
\message{doing \number\i}%
}%
2
\repeat \for{iii}
\to{8}
\do {}
\message{After loop: \number\iii}%
3
\repeat \for{i}
\from{10}
\by{2}
\downto{0}
\do {\message{countdown \number\i}}
4
\repeat \for{x} \while{\ifnum\x<7}
\do {\message{going \number\x}}
5
\repeat \to{3} \do {\message{hello there!}}
6
\repeat \for{j}
\until{\tmpcount\j \divide\tmpcount by 37 \noexpand\ifnum\tmpcount=1}
\do {\message{testing \number\j}}
7
\repeat \for{i} \by{2} \to{10} \do
{\repeat \for{j} \from{\i} \by{3} \to{18}
\do {\message{(\number\i.\number\j)}}
}
8
1
2
3
4
5
6
7
8
% infinite loop
%\repeat \do {}
\bye