%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Repeat loop macro, version 0.92, December 2000
% Copyright Victor Eijkhout 2000
% file name: repeat.tex
%
% Author:
% Victor Eijkhout
% Department of Computer Science
% University of Tennessee, Knoxville TN 37996
%
% victor@eijkhout.net
%
% This program is free software; you can redistribute it and/or
% modify it under the terms of the GNU General Public License
% as published by the Free Software Foundation; either version 2
% of the License, or (at your option) any later version.
% 
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% For a copy of the GNU General Public License, write to the 
% Free Software Foundation, Inc.,
% 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA,
% or find it on the net, for instance at
% http://www.gnu.org/copyleft/gpl.html
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% General loop macro:
% \repeat
%   \for{<var>} \from{<start>} \by{<step>} \to{<end>} \downto{<end>}
%               \until{<cond>} \while{<cond>}
%   \do { <loop body> }
% where all control sequences in between \repeat and \do are optional.
% The space after each argument is mandatory!
% (This implies that you will have to write "\from\i" as "\from{\i}".)
%
% 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.
%
% 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, eliminated unwanted space caused by
%      "\for{ijk} \do{...}"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%

%%
%% Prevent multiple loading of this file
%%
\expandafter\ifx\csname REPdepth\endcsname\relax
  \message{Loading loop macro, version 0.92}%
\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}
%\def\cscsarg#1#2#3{\expandafter#1\expandafter#2\csname#3\endcsname}
%\def\REPcsREPcsargrom#1#2#3{\expandafter#1\expandafter#2%
%                      \csname#3\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}}
%%
%% 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}}\REPtmp}%
    \def\from##1 {\REPcsargrom\global{REPcount}##1\relax}%
    \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}}\REPtmp}%
    \def\downto##1 {\gdef\REPsign{-}\gdef\REPcomp{<}\to{##1}}%
    \def\by##1 {\ifnum##1<0 \message{REPEAT: increment has to be >0}%
               \REPcsargrom\gdef{REPinc}{-##1}\else
               \REPcsargrom\gdef{REPinc}{##1}\fi}
    \def\until##1 {\edef\REPtmp{\global\REPcsargrom\noexpand{REPutest}=
       {\noexpand##1\relax
        \noexpand\expandafter \noexpand\breakrepeat \noexpand\fi}}\REPtmp}%
    \def\while##1 {\edef\REPtmp{\global\REPcsargrom\noexpand{REPwtest}=
       {\noexpand##1\relax \noexpand\else
        \noexpand\expandafter \noexpand\breakrepeat \noexpand\fi}}\REPtmp}%
    \from{1} \by{1} #1%
  \endgroup}
\def\REPzero
   {\REPcsrom{REPtoks}{}\REPcsrom{REPutest}{}\REPcsrom{REPwtest}{}%
    \REPcsargrom\def{REPbody}{}}

\endinput

%\tracingmacros2
\repeat \for{i} \by{2} \do {
  \ifnum\i=13 \expandafter\breakrepeat \fi
  \message{doing \number\i}
}

\repeat \for{iii} \to{8} \do {}
\message{After loop: \number\iii}

\repeat \for{i} \from{10} \by{2} \downto{0} \do {
  \message{countdown \number\i}
}

\repeat \for{x} \while{\ifnum\x<7} \do {
  \message{going \number\x}
}

\repeat \to{3} \do {
  \message{hello there!}
}

\newcount\tmpcount
\repeat \for{j}
        \until{\tmpcount\j \divide\tmpcount by 37 \noexpand\ifnum\tmpcount=1}
  \do {
    \message{testing \number\j}
}

\repeat \for{i} \by{2} \to{10} \do
 {\repeat \for{j} \from{\i} \by{3} \to{18} \do
  {\message{(\number\i.\number\j)}
 }}

% infinite loop
%\repeat \do {}

\bye
