summaryrefslogtreecommitdiff
path: root/macros/generic/eijkhout/repeat.tex
blob: 0bbb0785f6c72f45e7d19743991eeb39b77d4664 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 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 <http://www.latex-project.org/lppl/> for further information.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 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.
%
% 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