ńņš. 21 |

| 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.)

EXERCISE 19.2

What do you think: Is false > true?

EXERCISE 19.3

Could ā˜(odd n) and not (odd ā’n)ā™ possibly be true?

Chapter 19: Conditions and Loops 171

EXERCISE 19.4 type

declaration

Could ā˜(cycle p) and not (known p)ā™ possibly be true?

equality

equation

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.]

cycle

path

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

endfor

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

for

A boolean expression like ā˜x = yā™ that involves the equality relation looks very forsuļ¬xes

forever

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.)

progression

step

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

exitif

EXERCISE 19.6 ;

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

upto

for x = 1 , 2 , 3 : text(x) endfor downto

quote

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 .

EXERCISE 19.7

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

hide

What values are shown by the following program?

save

n=0; for n=1: m=n; endfor show m,n; end. exitif

exitunless

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

SHAKESPEARE

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.

EXERCISE 19.9

Deļ¬ne an ā˜exitunlessā™ macro such that ā˜exitunless boolean expression ;ā™

will exit the current loop if the boolean expression is false.

EXERCISE 19.10

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.

EXERCISE 19.11

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)

20

More

About

Macros

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

spark

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

endgroup

for a little while. We shall discuss the following topics:

showvariable

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.p=pair

a.q=unknown pen

a.r=unknown path

a.s=macro:->begingroup...endgroup

176 Chapter 20: More About Macros

The ā˜incrā™ macro of Appendix B increases its argument by 1 and produces incr

suļ¬x

the increased value as its result. The inserted ā˜begingroupā™ and ā˜endgroupā™

suļ¬x

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

tolerance

Chapter 18, youā™re allowed to say either ā˜incr vā™ or ā˜incr(v)ā™ when applying incr to v.

forever

exitif

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

EXERCISE 20.1

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-

Plain

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

showvariable

incr

z

(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 ).

EXERCISE 20.2

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?

EXERCISE 20.3 ā

3

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.

EXERCISE 20.4

After ā˜vardef p[]dir=(#@dx,#@dy) enddefā™, whatā™s the expansion of ā˜p5dirā™ ?

EXERCISE 20.5

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ā™).

EXERCISE 20.6

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

vardef

ā˜pair a.pā™ or ā˜vardef a[]ā™, since such variables would be inaccessible.

vardef

@#

The syntax for deļ¬nition in Chapter 18 was incomplete, because vardef leveldef heading

leveldef

heading and leveldef heading were omitted. Here are the missing rules:

primarydef

secondarydef

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

intersectiontimes

parameter ā’ā’ symbolic token save

errmessage

The new things here are primarydef , secondarydef , and tertiarydef , which permit begingroup

endgroup

you to extend ā™s repertoire of binary operators. For example, the ā˜dotprodā™ transum

operator is deļ¬ned as follows in Appendix B: sum

transforms

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 |