<< . .

. 21
( : 45)

. . >>

| known primary | unknown primary ¡=
| type primary | cycle primary ¿
| odd numeric primary =
| not boolean primary ¡¿
boolean secondary ’’ boolean primary primary
future pen primary
| boolean secondary and boolean primary pen
boolean tertiary ’’ boolean secondary transform
| boolean tertiary or boolean secondary
boolean expression ’’ boolean tertiary
| numeric expression relation numeric tertiary
| pair expression relation pair tertiary
| transform expression relation transform tertiary
| boolean expression relation boolean tertiary
| string expression relation string tertiary
relation ’’ < | <= | > | >= | = | <>

Most of these operations were already explained in Chapter 8, so it™s only necessary
to mention the more subtle points now. A primary of any type can be tested to see
whether it has a speci¬c type, and whether it has a known or unknown value based on
the equations so far. In these tests, a future pen primary is considered to be of type
pen. The test ˜cycle p™ is true if and only if p is a cyclic path. The ˜odd™ function ¬rst
rounds its argument to an integer, then tests to see if the integer is odd. The ˜not™
function changes true to false and vice versa. The ˜and™ function yields true only if
both arguments are true; the ˜or™ function yields true unless both arguments are false.
Relations on pairs, transforms, or strings are decided by the ¬rst unequal component
from left to right. (A transform is considered to be a 6-tuple as in Chapter 15.)

What do you think: Is false > true?

Could ˜(odd n) and not (odd ’n)™ possibly be true?
Chapter 19: Conditions and Loops 171

EXERCISE 19.4 type
Could ˜(cycle p) and not (known p)™ possibly be true?
EXERCISE 19.5 statement
De¬ne an ˜even™ macro such that ˜even n™ is true if and only if round(n) is an right-hand side
path join
even integer. [Hint: There™s a slick answer.]
Boolean expressions beginning with a type should not come at the very pair expression
beginning of a statement, because will think that a declaration loop
is coming up instead of an expression . Thus, for example, if b is a boolean variable, the loop header
equation ˜path p = b™ should be rewritten either as ˜b = path p™ or as ˜(path p) = b™. for
A boolean expression like ˜x = y™ that involves the equality relation looks very forsu¬xes
much like an equation. will consider ˜=™ to be a relation unless is
the expression to its left occurs at the very beginning of a statement or the very =
beginning of a right-hand side . If you want to change an equation into a relation,
for list
just insert parentheses, as in ˜(x = y) = b™ or ˜b = (x = y)™. ,
After a path join , the token ˜cycle™ is not considered to be the beginning of su¬x list
a boolean primary . (Cf. Chapter 14.)
The boolean expression ˜path ((0, 0))™ is false, even though ˜((0, 0))™ meets until
Chapter 14™s syntax rules for path primary , via ( path expression ) and initial value
step size
( path tertiary ) and ( pair tertiary ). A pair expression is not considered to be of
limit value
type path unless the path interpretation is mandatory. exit clause
Evaluate ˜length ((3, 4))™ and ˜length ((3, 4){0, 0})™ and ˜length reverse (3, 4)™.

OK, that covers all there is to be said about conditions. What about
loops? It™s easiest to explain loops by giving the syntax ¬rst:
loop ’’ loop header : loop text endfor
loop header ’’ for symbolic token is for list
| for symbolic token is progression
| forsuffixes symbolic token is su¬x list
| forever
is ’’ = | :=
for list ’’ expression | empty
| for list , expression | for list , empty
su¬x list ’’ su¬x
| su¬x list , su¬x
progression ’’ initial value step step size until limit value
initial value ’’ numeric expression
step size ’’ numeric expression
limit value ’’ numeric expression
exit clause ’’ exitif boolean expression ;
As in macro de¬nitions, ˜=™ and ˜:=™ are interchangeable here.
172 Chapter 19: Conditions and Loops

This syntax shows that loops can be of four kinds, which we might capsules
indicate schematically as follows: semicolons
for x = 1 , 2 , 3 : text(x) endfor downto
for x = ν1 step ν2 until ν3 : text(x) endfor
forsu¬xes s = σ1 , σ2 , σ3 : text(s) endfor
forever: text endfor
The ¬rst case expands to ˜text( 1 ) text( 2 ) text( 3 )™; the ™s here are expres-
sions of any type, not necessarily “known,” and they are evaluated and put into
capsules before being substituted for x. The ™s might also be empty, in which
case text( ) is omitted. The second case is more complicated, and it will be
explained carefully below; simple cases like ˜1 step 2 until 7™ are equivalent to
short lists like ˜1, 3, 5, 7™. The third case expands to ˜text(σ1 ) text(σ2 ) text(σ3 )™;
the σ™s here are arbitrary su¬xes (possibly empty), in which subscripts will have
been evaluated and changed to numeric tokens before being substituted for s.
The ¬nal case expands into the sequence ˜text text text . . .™, ad in¬nitum; there™s
an escape from this (and from the other three kinds of loop) if an exit clause
appears in the text, as explained below.
Notice that if the loop text is a single statement that™s supposed to be
repeated several times, you should put a ˜;™ just before the endfor, not just after
it; ™s loops do not insert semicolons automatically, because they are
intended to be used in the midst of expressions as well as with statements that
are being iterated.
Plain de¬nes ˜upto™ as an abbreviation for ˜step 1 until™,
and ˜downto™ as an abbreviation for ˜step ’1 until™. Therefore you can say,
e.g., ˜ for x = 1 upto 9: ™ instead of ˜ for x = 1, 2, 3, 4, 5, 6, 7, 8, 9: ™.
When you say ˜for x = ν1 step ν2 until ν3 ™, evaluates the three
numeric expressions, which must have known values. Then it reads the loop
text. If ν2 > 0 and ν1 > ν3 , or if ν2 < 0 and ν1 < ν3 , the loop is not performed at
all. Otherwise text(ν1 ) is performed, ν1 is replaced by ν1 + ν2 , and the same process is
repeated with the new value of ν1 .
Read the rules in the previous paragraph carefully, then explain for what values
of x the loop is performed if you say (a) ˜ for x = 1 step 2 until 0™ . (b) ˜ for x = 1
step ’2 until 0 ™. (c) ˜ for x = 1 step 0 until 0 ™. (d) ˜ for x = 0 step .1 until 1 ™.
A loop text is rather like the replacement text of a macro. It is any se-
quence of tokens that is balanced with respect to unquoted appearances of
for/forsu¬xes/forever and endfor delimiters. reads the entire loop
text quickly and stores it away before trying to perform it or to expand macros within
it. All occurrences of the controlled symbolic token in the loop text are changed to
special internal parameter tokens that mean “insert an argument here,” where the ar-
gument is of type expr in the case of for, of type su¬x in the case of forsu¬xes. This
rule implies, in particular, that the symbolic token has no connection with similarly
named variables elsewhere in the program.
Chapter 19: Conditions and Loops 173

EXERCISE 19.8 ¬‚ex
What values are shown by the following program?
n=0; for n=1: m=n; endfor show m,n; end. exitif
The ¬‚ex routine described in Chapter 14 provides an interesting example of prime number
how loops can be used inside of macros inside of expressions: forever
pair z [ ], dz ; numeric n ; % private variables Matthew
def ¬‚ex (text t) = % t is a list of pairs
hide ( n := 0;
for z = t: z [incr n ] := z; endfor
dz := z [n ] ’ z [1] )
z [1] for k = 2 upto n ’ 1: . . . z [k]{dz } endfor
. . . z [n ] enddef ;
The ¬rst loop stores the given pairs temporarily in an array, and it also counts how
many there are; this calculation is “hidden.” Then the actual ¬‚ex-path is contributed
to the program with the help of a second loop. (Appendix B uses the convention that
symbolic tokens ending in ˜ ™ should not appear in a user™s program; this often makes
it unnecessary to ˜save™ tokens.)
When encounters the construction ˜exitif boolean expression ;™,
it evaluates the boolean expression. If the expression is true, the (innermost)
loop being iterated is terminated abruptly. Otherwise, nothing special happens.
De¬ne an ˜exitunless™ macro such that ˜exitunless boolean expression ;™
will exit the current loop if the boolean expression is false.
Write a program that sets p[k] to the kth prime number, for
1 ¤ k ¤ 30. Thus, p[1] should be 2, p[2] = 3, etc.
When you run on the ¬le ˜expr.mf™ of Chapter 8, you get into a
˜forever™ loop that can be stopped if you type, e.g., ˜0 end™. But what can you type to
get out of the loop without ending the run? (The goal is to make type ˜*™,
without incurring any error messages.)

If? thou Protector of this damned Strumpet,
Talk™st thou to me of Ifs: thou art a Traytor,
O¬ with his Head.
” WILLIAM SHAKESPEARE, Richard the Third (1593)

Use not vain repetitions.
” Matthew 6 : 7 (c. 70 A.D.)
(page 174)

Chapter 20: More About Macros 175

Chapter 18 gave the basic facts about macro de¬nitions, but it didn™t tell the vardef
declared variable
whole story. It™s time now for the Ultimate Truth to be revealed. SIMULA67
But this whole chapter consists of “dangerous bend” paragraphs, since the tag
subject matter will be appreciated best by people who have worked with begingroup
for a little while. We shall discuss the following topics:
De¬nitions that begin with ˜vardef ™; these embed macros into the variables
of a program and extend the unary operators of expressions.
De¬nitions that begin with ˜primarydef ™, ˜secondarydef ™, or ˜tertiarydef ™;
these extend the binary operators of expressions.
Other primitives of that expand into sequences of tokens in a
macro-like way, including ˜input™ and ˜scantokens™.
Rules that explain when tokens are subject to expansion and when they aren™t.
First let™s consider the vardef heading that was left unde¬ned in Chapter 18.
The ordinary macros discussed in that chapter begin with
def symbolic token parameter heading
and then comes ˜=™, etc. You can also begin a de¬nition by saying
vardef declared variable parameter heading
instead; in this case the declared variable might consist of several tokens, and you are
essentially de¬ning a variable whose “value” is of type “macro.” For example, suppose
you decide to say
pair a.p; pen a.q; path a.r; vardef a.s = . . . enddef ;
then a.p, a.q, and a.r will be variables of types pair, pen, and path, but a.s will
expand into a sequence of tokens. (The language SIMULA67 demonstrated that it is
advantageous to include procedures as parts of variable data structures;
does an analogous thing with macros.)
After a de¬nition like ˜def t = . . .™, the token t becomes a “spark”; i.e., you
can™t use it in a su¬x. But after ˜vardef t = . . .™, the token t remains a “tag,”
because macro expansion will take place only when t is the ¬rst token in a variable
name. Some of the de¬nitions in Appendix B are vardefs instead of defs for just that
reason; for example,
vardef dir primary d = right rotated d enddef
allows a user to have variable names like ˜p5dir™.
A variable is syntactically a primary expression, and would get
unnecessarily confused if the replacement texts of vardef macros were very dif-
ferent from primary expressions. Therefore, the tokens ˜begingroup™ and ˜endgroup™
are automatically inserted at the beginning and end of every vardef replacement text.
If you say ˜showvariable a™ just after making the declarations and de¬nition above,
the machine will reply as follows:
a.q=unknown pen
a.r=unknown path
176 Chapter 20: More About Macros

The ˜incr™ macro of Appendix B increases its argument by 1 and produces incr
the increased value as its result. The inserted ˜begingroup™ and ˜endgroup™
come in handy here: expr
vardef incr su¬x $ = $ := $ + 1; $ enddef . undelimited su¬x parameters
at sharp
Notice that the argument is a su¬x, not an expr, because every variable name is a solve
binary search
special case of a su¬x , and because an expr parameter should never appear to the nonlinear equations
left of ˜:=™. Incidentally, according to the rules for undelimited su¬x parameters in equations, nonlinear
Chapter 18, you™re allowed to say either ˜incr v™ or ˜incr(v)™ when applying incr to v.
There™s another kind of vardef, in which the variable name being de¬ned can Southall
have any additional su¬x when it is used; this su¬x is treated as an argument
to the macro. In this case you write
vardef declared variable @# parameter heading
and you can use @# in the replacement text (where it behaves like any other su¬x
parameter). For example, Appendix B says
vardef z@# = (x@#, y@#) enddef ;
this is the magic de¬nition that makes ˜z3r ™ equivalent to ˜(x3r , y3r )™, etc. In fact, we
now know that ˜z3r™ actually expands into eleven tokens:
begingroup (x3r, y3r) endgroup
True or false: After ˜vardef a@# suffix b = . . . enddef™, the su¬x argument b
will always be empty.
includes a solve macro that uses binary search to ¬nd nu-
merical solutions to nonlinear equations, which are too di¬cult to resolve in
the ordinary way. To use solve , you ¬rst de¬ne a macro f such that f (x) is either true
or false; then you say
solve f (true x , false x )
where true x and false x are values such that f (true x ) = true and f (false x ) = false.
The resulting value x will be at the cutting edge between truth and falsity, in the sense
that x will be within a given tolerance of values for which f yields both outcomes.
vardef solve @#(expr true x , false x ) =
tx := true x ; fx := false x ;
forever: x := .5[tx , fx ]; exitif abs(tx ’ fx ) ¤ tolerance ;
if @#(x ) : tx else : fx ¬:=x ; endfor;
x enddef ;
For example, the solve routine makes it possible to solve the following inter-
esting problem posed by Richard Southall: Given points z1 , z2 , z3 , z4 such
that x1 < x2 < x3 < x4 and y1 < y2 = y3 > y4 , ¬nd the point z between z2 and z3
will choose to travel right at z in the path
such that
z1 {z2 ’ z1 } . . z . . {z4 ’ z3 } z4 .
Chapter 20: More About Macros 177

If we try z = z2 , will choose a direction at z that has a positive (upward) nice
cube root
y-component; but at z = z3 , ™s chosen direction will have a negative (down-
collective subscripts
ward) y-component. Somewhere in between is a “nice” value of z for which the curve at
will not rise above the line y = y2 . What is this z? sharp at

(Figure 20a will be inserted here; too bad you can™t see it now.)

Chapter 14 gives equations from which z could be computed, in principle, but those
equations involve trigonometry in a complicated fashion. It™s nice to know that we can
¬nd z rather easily in spite of those complexities:
vardef upward (expr x) =
ypart direction 1 of (z1 {z2 ’ z1 } . . (x, y2 ) . . {z4 ’ z3 }z4 ) > 0 enddef ;
z = (solve upward (x2 , x3 ), y2 ).
It might happen in unusual cases that upward (x) is false for all x2 ¤ x ¤ x3 ,
hence solve is being invoked under invalid assumptions. What result does it give then?
Use solve to ¬nd 10, and compare the answer to the cube root obtained in
the normal way.
The syntax for declared variable in Chapter 7 allows for collective subscripts
as well as tags in the name of the variable being declared. Thus, you can say
vardef a[ ]b[ ] = . . . enddef ;
what does this mean? Well, it means that all variables like a1b2 are macros with
a common replacement text. Every vardef has two implicit su¬x parameters, ˜#@™
and ˜@™, which can be used in the replacement text to discover what subscripts have
actually been used. Parameter ˜@™ is the ¬nal token of the variable name (˜2™ in this
example); parameter ˜#@™ is everything preceding the ¬nal token (in this case ˜a1b™).
These notations are supposed to be memorable because ˜@™ is where you™re “at,” while
˜#@™ is everything before and ˜@#™ is everything after.
After ˜vardef p[]dir=(#@dx,#@dy) enddef™, what™s the expansion of ˜p5dir™ ?
Explain how it™s possible to retrieve the ¬rst subscript in the replacement text
of vardef a[]b[] (thereby obtaining, for example, ˜1™ instead of ˜a1b™).
Say ˜showvariable incr,z™ to and explain the machine™s reply.
A vardef wipes out all type declarations and macro de¬nitions for variables
whose name begins with the newly de¬ned macro variable name. For example,
˜vardef a™ causes variables like a.p and a1b2 to disappear silently; ˜vardef a.s™ wipes
178 Chapter 20: More About Macros

out a.s.p, etc. Moreover, after ˜vardef a™ is in e¬ect, you are not allowed to say vardef heading
˜pair a.p™ or ˜vardef a[]™, since such variables would be inaccessible.
The syntax for de¬nition in Chapter 18 was incomplete, because vardef leveldef heading
heading and leveldef heading were omitted. Here are the missing rules:
vardef heading ’’ vardef declared variable parameter heading tertiarydef
| vardef declared variable @# parameter heading parameter
numeric secondary
leveldef heading ’’ leveldef parameter symbolic token parameter dotprod
leveldef ’’ primarydef | secondarydef | tertiarydef intersectionpoint
parameter ’’ symbolic token save
The new things here are primarydef , secondarydef , and tertiarydef , which permit begingroup
you to extend ™s repertoire of binary operators. For example, the ˜dotprod™ transum
operator is de¬ned as follows in Appendix B: sum
primarydef w dotprod z =
(xpart w — xpart z + ypart w — ypart z) enddef .

™s syntax for expressions has e¬ectively gained a new rule

numeric secondary ’’ pair secondary dotprod pair primary

in addition to the other forms of numeric secondary , because of this primarydef.

The names ˜primarydef ™, ˜secondarydef ™, and ˜tertiarydef ™ may seem o¬
by one, because they de¬ne operators at one level higher up: A primarydef
de¬nes a binary operator that forms a secondary expression from a secondary and a pri-
mary; such operators are at the same level as ˜—™ and ˜rotated™. A secondarydef de¬nes
a binary operator that forms a tertiary expression from a tertiary and a secondary;
such operators are at the same level as ˜+™ and ˜or™. A tertiarydef de¬nes a binary
operator that forms an expression from an expression and a tertiary; such operators
are at the same level as ˜<™ and ˜&™.

Plain ™s ˜intersectionpoint™ macro is de¬ned by a secondarydef
because it is analogous to ˜intersectiontimes™, which occurs at the same level
(namely the secondary ’ tertiary level).

secondarydef p intersectionpoint q =
begingroup save x , y ; (x , y ) = p intersectiontimes q;
if x < 0: errmessage("The paths don™t intersect"); (0, 0)
else: .5[point x of p, point y of q] ¬ endgroup enddef ;

Notice that begingroup and endgroup are necessary here; they aren™t inserted au-
tomatically as they would have been in a vardef .

<< . .

. 21
( : 45)

. . >>