<<

. 6
( 13)



>>

If it takes a month to get it,
then be happy that it takes only a month.
” LLOYD REYNOLDS, Italic Calligraphy & Handwriting (1969)
(page 154)




17
Grouping
Chapter 17: Grouping 155


We have now covered all the visual, graphic aspects of ”its points, end
program
paths, pens, and pictures; but we still don™t know everything about ™s end
organizational, administrative aspects”its programs. The next few chapters of statement list
;
this book therefore concentrate on how to put programs together e¬ectively. statements
A program is a sequence of statements separated by semi- empty statement
compound statement
colons and followed by ˜end™. More precisely, the syntax rules group
statement
program ’’ statement list end begingroup
endgroup
statement list ’’ empty | statement ; statement list save command
save
de¬ne a program in terms of a statement . symbolic token list
,
But what are statements? Well, they are of various kinds. An “equation” interim command
states that two expressions are supposed to be equal. An “assignment” assigns interim
:=
the value of an expression to a variable. A “declaration” states that certain
variables will have a certain type. A “de¬nition” de¬nes a macro. A “title”
gives a descriptive name to the character that is to follow. A “command” orders
to do some speci¬c operation, immediately. The “empty statement”
tells to do absolutely nothing. And a “compound statement” is a
list of other statements treated as a group.
statement ’’ equation | assignment | declaration
| de¬nition | title | command | empty
| begingroup statement list statement endgroup
We™ve given the syntax for equation and assignment in Chapter 10; the syntax
for declaration appeared in Chapter 7; de¬nition and title and command
will appear in later chapters. Our main concern just now is with the ¬nal type
of statement , where begingroup and endgroup bind other statements into a
unit, just as parentheses add structure to the elements of an algebraic expression.
The main purpose of grouping is to protect the values of variables in one
part of the program from being clobbered in another. A symbolic token can be
given a new meaning inside a group, without changing the meaning it had outside
that group. (Recall that deals with three basic kinds of tokens, as
discussed in Chapter 6; it is impossible to change the meaning of a numeric token
or a string token, but symbolic tokens can change meanings freely.)
There are two ways to protect the values of variables in a group. One is
called a save command , and the other is called an interim command :
save command ’’ save symbolic token list
symbolic token list ’’ symbolic token
| symbolic token list , symbolic token
interim command ’’ interim internal quantity := right-hand side
The symbolic tokens in a save command all lose their current meanings, but
those old meanings are put into a safe place and restored at the end of the current
group. Each token becomes unde¬ned, as if it had never appeared before. For
156 Chapter 17: Grouping


example, the command tag
spark
internal quantity
save x, y
beginchar
endchar
e¬ectively causes all previously known variables like x1 and y5r to become in- tracingrestores
accessible; the variable x1 could now appear in a new equation, where it would
have no connection with its out-of-group value. You could also give the silly
command
save save;
this would make the token ˜save™ itself into a tag instead of a spark , so you
couldn™t use it to save anything else until the group ended.
An interim command is more restrictive than a save, since it applies only to
an internal quantity . (Recall that internal quantities are special variables
like tracingequations that take numeric values only; a complete list of all the standard
internal quantities can be found in Chapter 25, but that list isn™t exhaustive because
you can de¬ne new ones for your own use.) treats an interim command just
like an ordinary assignment, except that it undoes the assignment when the group ends.
If you save something two or more times in the same group, the ¬rst saved
value takes precedence. For example, in the construction
begingroup
...
interim autorounding := 0; save x;
...
interim autorounding := 1; save x;
...
endgroup
the values of autorounding and x after the end of the group will be their previous values
just before the statement ˜interim autorounding := 0™. (Incidentally, these might not
be the values they had upon entry to the group).
Tokens and internal quantities regain their old meanings and values at the end
of a group only if they were explicitly saved in a save or interim command.
All other changes in meaning and/or value will survive outside the group.
The beginchar operation of plain includes a begingroup, and
endchar includes endgroup. Thus, for example, interim assignments can be
made in a program for one character without any e¬ect on other characters.
A save command that™s not in a group simply clears the meanings of the
symbolic tokens speci¬ed; their old meanings are not actually saved, because
they never will have to be restored. An interim command outside a group acts just
like a normal assignment.
If you set the internal quantity tracingrestores to a positive value,
will make a note in your transcript ¬le whenever it is restoring the former value
of a symbolic token or internal quantity. This can be useful when you™re debugging a
program that doesn™t seem to make sense.
Chapter 17: Grouping 157


Groups can also be used within algebraic expressions. This is the other group expression
whatever
important reason for grouping; it allows to do arbitrarily compli- FULTON
cated things while in the middle of other calculations, thereby greatly increasing WILLIS
the power of macro de¬nitions (which we shall study in the next chapter). A
group expression has the general form
begingroup statement list expression endgroup
and it ¬ts into the syntax of expressions at the primary level. The meaning
of a group expression is: “Perform the list of statements, then evaluate the
expression, then restore anything that was saved in this group.”
Group expressions belong in the syntax rules for each type of expression,
but they were not mentioned in previous chapters because it would have been
unnecessarily distracting. Thus, for example, the syntax for numeric primary actually
includes the additional alternative
begingroup statement list numeric expression endgroup.
The same goes for pair primary , picture primary , etc.; Chapter 25 has the complete
rules of syntax for all types of expressions.
EXERCISE 17.1
What is the value of the expression
begingroup x:=x+1; x endgroup + begingroup x:=2x; x endgroup
if x initially has the value a? What would the value have been if the two group
expressions had appeared in the opposite order? Verify your answers using the expr
routine of Chapter 8.
EXERCISE 17.2
Appendix B de¬nes whatever to be an abbreviation for the group expression
˜begingroup save ?; ? endgroup™. Why does this work?
EXERCISE 17.3
What is the value of ˜begingroup save ?; (?, ?) endgroup™ ?
EXERCISE 17.4
According to exercise 10.2, the assignment ˜x3 := whatever ™ will make the
numeric variable x3 behave like new, without a¬ecting other variables like x2 . Devise
a similar stratagem that works for arrays of picture variables.




It is often di¬cult
to account for some beginners grouping right away
and others proving almost hopeless.
” A. G. FULTON, Notes on Ri¬‚e Shooting (1913)

Rock bands prefer San Francisco groupies to New York groupies.
” ELLEN WILLIS, But Now I™m Gonna Move (1971)
(page 158)




18
De¬nitions
(also called Macros)
Chapter 18: De¬nitions (also called Macros) 159


You can often save time writing programs by letting single tokens ”
de¬nitions
stand for sequences of other tokens that are used repeatedly. For example, replacement text
Appendix B de¬nes ˜- - -™ to be an abbreviation for ˜. . tension in¬nity . .™, and parameters
arguments
this de¬nition is preloaded as part of the plain base. Programs that rotatedaround
use such de¬nitions are not only easier to write, they™re also easier to read. But capsules
:=
Appendix B doesn™t contain every de¬nition that every programmer might want; assignment
the present chapter therefore explains how you can make de¬nitions of your own.
In the simplest case, you just say
def symbolic token = replacement text enddef
and the symbolic token will henceforth expand into the tokens of the replacement
text. For example, Appendix B says
def --- = ..tension infinity.. enddef.
The replacement text can be any sequence of tokens not including ˜enddef ™; or
it can include entire subde¬nitions like ˜def . . . enddef ™, according to certain
rules that we shall explain later.
De¬nitions get more interesting when they include parameters, which
are replaced by arguments when the de¬nition is expanded. For example, Ap-
pendix B also says
def rotatedaround(expr z,theta) =
shifted -z rotated theta shifted z enddef;
this means that an expression like ˜z1 rotatedaround (z2 , 30)™ will expand into
˜z1 shifted ’z2 rotated 30 shifted z2 ™.
The parameters ˜z™ and ˜theta™ in this de¬nition could have been any
symbolic tokens whatever; there™s no connection between them and appearances
of ˜z™ and ˜theta™ outside the de¬nition. (For example, ˜z™ would ordinarily
stand for ˜(x,y)™, but it™s just a simple token here.) The de¬nition could even
have been written with “primitive” tokens as parameters, like
def rotatedaround(expr;,+) =
shifted-; rotated+shifted; enddef;
the e¬ect would be exactly the same. (Of course, there™s no point in doing such
a thing unless you are purposely trying to make your de¬nition inscrutable.)
When ˜rotatedaround™ is used, the arguments that are substituted for
z and theta are ¬rst evaluated and put into “capsules,” so that they will behave
like primary expressions. Thus, for example, ˜z1 rotatedaround (z2 + z3 , 30)™ will
not expand into ˜z1 shifted ’z2 + z3 rotated 30 shifted z2 + z3 ™”which means
something entirely di¬erent”but rather into ˜z1 shifted ’± rotated 30 shifted ±™,
where ± is a nameless internal variable that contains the value of z2 + z3 .
A capsule value cannot be changed, so an expr parameter should not appear
at the left of the assignment operator ˜:=™.
160 Chapter 18: De¬nitions (also called Macros)


Macros are great when they work, but complicated macros sometimes surprise tracingmacros
EXPR0
their creators. provides “tracing” facilities so that you can see
group expressions
what the computer thinks it™s doing, when you™re trying to diagnose the reasons for re¬‚ectedabout
unexpected behavior. If you say ˜tracingmacros := 1™, the transcript ¬le of your run save
transform
will record every macro that is subsequently expanded, followed by the values of its xxpart
arguments as soon as they have been computed. For example, ˜rotatedaround (up , 30)™ yypart
xypart
might produce the following lines of diagnostic information:
yxpart
special-purpose macros
rotatedaround(EXPR0)(EXPR1)->shifted-(EXPR0)rotated(EXPR1)sh
beginlogochar
ifted(EXPR0) expr
(EXPR0)<-(0,1)
(EXPR1)<-30
Here™s another example from Appendix B. It illustrates the usefulness of group
expressions in macro de¬nitions:
def re¬‚ectedabout (expr p, q) =
transformed begingroup
save T ; transform T ;
p transformed T = p;
q transformed T = q;
xxpart T = ’yypart T ;
xypart T = yxpart T ;
T endgroup enddef ;
thus a new transform, T , is computed in the midst of another expression, and the
macro ˜re¬‚ectedabout(p, q)™ essentially expands into ˜transformed T ™.
Some macros, like ˜rotatedaround™, are meant for general-purpose use.
But it™s also convenient to write special-purpose macros that simplify the devel-
opment of particular typefaces. For example, let™s consider the logo
from this standpoint. The program for ˜ ™ in Chapter 11 starts with
beginchar("E",14u#+2s#,ht#,0); pickup logo_pen;
and the programs for ˜ ™, ˜ ™, etc., all have almost the same beginning. Therefore
we might as well put the following de¬nition near the top of the ¬le logo.mf:
def beginlogochar(expr code, unit_width) =
beginchar(code,unit_width*u#+2s#,ht#,0);
pickup logo_pen enddef;
Then we can start the ˜ ™ by saying simply
beginlogochar("E",14);
similar simpli¬cations apply to all seven letters. Notice from this example that
macros can be used inside macros (since ˜beginchar™ and ˜pickup™ are them-
selves macros, de¬ned in Appendix B); once you have de¬ned a macro, you have
essentially extended the language. Notice also that expr parame-
ters can be expressions of any type; for example, "E" is a string, and the ¬rst
parameter of ˜rotatedaround™ is a pair.
Chapter 18: De¬nitions (also called Macros) 161


Chapter 11 didn™t give the programs for superellipse
su¬x
˜ ™ or ˜ ™. It turns out that those programs can su¬x
be simpli¬ed if we write them in terms of an aux- text
iliary subroutine called ˜super_half™. For exam-
ple, here is how the ˜ ™ is made: (Figure 18a will be inserted here; too
bad you can™t see it now.)
beginlogochar("O",15);
x1=x4=.5w; top y1=h+o; bot y4=-o;
x2=w-x3=1.5u+s; y2=y3=barheight;
super_half(2,1,3);
super_half(2,4,3);
labels(1,2,3,4); endchar;
The super_half routine is supposed to draw half of a superellipse, through three
points whose subscripts are speci¬ed.
We could de¬ne super_half as a macro with three expr parameters,
referring to the ¬rst point as ˜z[i]™, say; but there™s a better way. Parameters
to macros can be classi¬ed as su¬xes, by saying su¬x instead of expr. In this
case the actual arguments may be any su¬x , i.e., any sequence of subscripts
and tags that complete the name of a variable as explained in Chapter 7. Here™s
what super_half looks like, using this idea:
def super_half(suffix i,j,k) =
draw z.i{0,y.j-y.i}
... (.8[x.j,x.i],.8[y.i,y.j]){z.j-z.i}
... z.j{x.k-x.i,0}
... (.8[x.j,x.k],.8[y.k,y.j]){z.k-z.j}
... z.k{0,y.k-y.j} enddef;
EXERCISE 18.1
Would the program for ˜ ™ still work if the two calls of super_half had been
˜super_half(3,1,2)™ and ˜super_half(3,4,2)™ ?
EXERCISE 18.2
Guess the program for ™s ˜ ™, which has the same width as ˜ ™.
Besides parameters of type expr and su¬x, also allows a third
type called text. In this case the actual argument is any sequence of tokens,
and this sequence is not evaluated beforehand; a text argument is simply copied in
place of the corresponding parameter. This makes it possible to write macros that deal
with lists of things. For example, Appendix B™s ˜de¬ne pixels™ macro is de¬ned thus:
def define_pixels(text t) =
forsuffixes a=t: a := a# * hppp; endfor enddef;
This means that ˜define_pixels(em,cap)™ will expand into
forsuffixes a=em,cap: a := a# * hppp; endfor
which, in turn, expands into the tokens ˜em := em# * hppp; cap := cap# * hppp;™ as we
will see in Chapter 19.
162 Chapter 18: De¬nitions (also called Macros)


Let™s look now at a subroutine for drawing serifs, since this typi¬es the sort serifs
penpos
of special-purpose macro one expects to see in the design of a meta-typeface.
jut
Serifs can take many forms, so we must choose from myriads of possibilities. We shall ]]
consider two rather di¬erent approaches, one based on outline-¬lling and the other
based on the use of a ¬xed pen nib. In both cases it will be necessary to omit some
of the re¬nements that would be desirable in a complete typeface design, to keep the
examples from getting too complicated.

Our ¬rst example is a serif routine that
constructs six points z$a , z$b , . . . , z$f
around a given triple of “penpos” points z$l , z$ ,
z$r ; here $ is a su¬x that™s a parameter to the
serif macro. Other parameters are: breadth ,
the distance between the parallel lines that run (Figure 18b will be inserted here; too bad
you can™t see it now.)
from z$l to z$a and from z$r to z$f ; theta , the
direction angle of those two lines; left jut , the
distance from z$l to z$b ; and right jut , the dis-
tance from z$r to z$e . (The serif “juts out” by
the amounts of the jut parameters.) There™s
also a serif edge macro, which constructs the
path shown. The routines refer to three variables that are assumed to apply to all
serifs: slab , the vertical distance from z$b and z$e to z$c and z$d ; bracket , the vertical
distance from z$a and z$f to z$l and z$r ; and serif darkness , a fraction that controls
how much of the triangular regions (z$a , z$l , z$b ) and (z$f , z$r , z$e ) will be ¬lled in.

def serif (su¬x $)(expr breadth , theta , left jut , right jut ) =
penpos$ (breadth /abs sind theta , 0);
z$a ’ z$l = z$f ’ z$r = (bracket /abs sind theta ) — dir theta ;
y$c = y$d ; y$b = y$e = y$ ; y$b ’ y$c = if theta < 0 : ’ ¬ slab ;
x$b = x$c = x$l ’ left jut ; x$d = x$e = x$r + right jut ;
labels($a, $b, $c, $d, $e, $f ) enddef ;
def serif edge su¬x $ =
(serif bracket ($a, $l, $b) - - z$c
- - z$d - - reverse serif bracket ($f, $r, $e)) enddef ;
def serif bracket (su¬x i, j, k) =
(z.i{z.j ’ z.i} . . . serif darkness [z.j, .5[z.i, z.k] ]{z.k ’ z.i}
. . . z.k{z.k ’ z.j}) enddef ;

EXERCISE 18.3
Under what circumstances will the serif edge go through points z$l and z$r ?

EXERCISE 18.4
Should this serif macro be used before points z$l , z$ , and z$r have been
de¬ned, or should those points be de¬ned ¬rst?

Here are two sample letters that show how these serif routines might be used.
The programs assume that the font has several additional ad hoc parameters:
u, a unit of character width; ht , the character height; thin and thick , the two stroke
weights; and jut , the amount by which serifs protrude on a “normal” letter like ˜H™.
Chapter 18: De¬nitions (also called Macros) 163




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




beginchar ("A", 13u#, ht#, 0);
z1 = (.5w, 1.05h); % top point
x4l = w ’ x5r = u; y4l = y5r = slab ; % bottom points
numeric theta [ ];
theta 4 = angle(z1 ’ z4l ); % left stroke angle
theta 5 = angle(z1 ’ z5r ); % right stroke angle
serif (4, thin , theta 4 , .6jut , jut ); % left serifs
serif (5, thick , theta 5 , jut , .6jut ); % right serifs
z0 = z4r + whatever — dir theta 4
= z5l + whatever — dir theta 5 ; % inside top point
¬ll z1 - - serif edge 4 - - z0 % the left stroke
& z0 - - serif edge 5 - - z1 & cycle; % the right stroke
penpos2 (whatever , theta 4 );
penpos3 (whatever , theta 5 );
y2r = y3r = .5[y4 , y0 ]; % crossbar height
y2l = y3l = y2r ’ thin ; % crossbar thickness
z2 = whatever [z1 , z4r ];
z3 = whatever [z1 , z5l ];
penstroke z2e - - z3e ; % the crossbar
penlabels(0, 1, 2, 3, 4, 5); endchar;
beginchar ("I", 6u#, ht#, 0);
x1 = x2 = .5w;
y1 = h ’ y2 ; y2 = slab ;
serif (1, thick , ’90, 1.1jut, 1.1jut); % upper serifs
serif (2, thick , 90, 1.1jut, 1.1jut); % lower serifs
¬ll serif edge 2 - - reverse serif edge 1 - - cycle; % the stroke
penlabels(1, 2); endchar;
The illustration was prepared with thin = .5pt , thick = 1.1pt , u = .6pt , ht = 7pt ,
slab = .25pt , jut = .9pt , bracket = pt , and serif darkness = 1/3.
EXERCISE 18.5
Could the equations de¬ning y1 and y2 in the program for "I" have been
replaced by ˜y1c = h™ and ˜y2c = 0™ ?
EXERCISE 18.6
Write the program for an "H" to go with these letters.
164 Chapter 18: De¬nitions (also called Macros)


A second approach to serifs can be based on the example at the end of Chap-
ter 16. In this case we assume that broad pen is a ˜pensquare xscaled px
yscaled py rotated phi ™ for some px > py and some small angle phi . Thicker strokes
will be made by using this pen to ¬ll a larger region; the serif routine is given the
distance xx between z$l and z$r . There™s a pair variable called dishing that controls
the curvature between z$c and z$d . Top and bottom serifs are similar, but they are
su¬ciently di¬erent that it™s easier to write separate macros for each case.
def bot serif (su¬x $)(expr xx , theta , left jut , right jut ) =
penpos$ (xx , 0); z$a ’ z$l = z$f ’ z$r = (bracket /abs sind theta ) — dir theta ;
y$c = top y$l ; y$d = y$r ; x$c = x$l ’ left jut ; x$d = x$r + right jut ;
z$b = z$l + whatever — dir theta = z$c + whatever — dir phi ;
z$e = z$r + whatever — dir theta = z$d + whatever — dir ’phi ;
labels($a, $b, $c, $d, $e, $f ) enddef ;
def bot serif edge su¬x $ =
(z$a . . controls z$b . . z$c
- - (¬‚ex (z$c , .5[z$c , z$d ] + dishing , z$d )) shifted (0, ’epsilon )
- - z$d . . controls z$e . . z$f ) enddef ;




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




beginchar ("A", 13u#, ht #, 0); pickup broad pen ;
z1 = (.5w, top h); lft x4l = w ’ rt x5r = 1.2u; y4l = y5r = 0;
numeric theta [ ]; theta 4 = angle(z1 ’ z4l ); theta 5 = angle(z1 ’ z5r );
numeric xxx ; px — sind(theta 5 ’ phi ) + xxx — sind theta 5 = px — cosd phi + xx ;
bot serif (4, 0, theta 4 , .8jut , .8jut ); bot serif (5, xxx , theta 5 , .6jut , .8jut );
z0 = z4r + whatever — dir theta 4 = z5l + whatever — dir theta 5 ;
¬lldraw z1 - - bot serif edge 4 - - z0 & z0 - - bot serif edge 5 - - z1 & cycle;
top y2 = top y3 = .45bot y0 ; z2 = whatever [z1 , z4r ]; z3 = whatever [z1 , z5l ];
draw z2 - - z3 ; penlabels(0, 1, 2, 3, 4, 5); endchar;
beginchar ("I", 6u#, ht #, 0); pickup broad pen ;
x1 = x2 = .5w; y1 = h; y2 = 0;
top serif (1, xx , ’90, 1.1jut , 1.1jut ); bot serif (2, xx , 90, 1.1jut , 1.1jut );
¬lldraw bot serif edge 2 - - reverse top serif edge 1 - - cycle;
penlabels(1, 2); endchar;
In the illustration, px = .8pt , py = .2pt , phi = 20, xx = .3pt , u = .6pt , ht = 7pt ,
jut = .9pt , bracket = pt , and dishing = (.25pt , 0) rotated 20.
Chapter 18: De¬nitions (also called Macros) 165


EXERCISE 18.7 de¬nition
enddef
Write the missing code for top serif and top serif edge .
is
=
EXERCISE 18.8 :=
(For mathematicians.) Explain the equation for xxx in the program for "A". de¬nition heading
def
parameter heading
EXERCISE 18.9 delimited parameters
Write the program for an "H" to go with these letters. (
)
parameter type
A close look at the serif edge routines in these examples will reveal that some
expr
parentheses are curiously lacking: We said ˜def serif edge su¬x $™ instead su¬x
of ˜def serif edge (su¬x $)™, and we used the macro by saying ˜serif edge 5 ™ instead of text
parameter tokens
˜serif edge (5)™. The reason is that allows the ¬nal parameter of a macro ,
to be without delimiters; this is something that could not have been guessed from a undelimited parameters
primary
study of previous examples. It is time now to stop looking at speci¬c cases and to start
secondary
examining the complete set of rules for macro de¬nitions. Here is the syntax: tertiary
expr
de¬nition ’’ de¬nition heading is replacement text enddef expr
of
is ’’ = | := su¬x
de¬nition heading ’’ def symbolic token parameter heading text
| vardef heading vardef heading
leveldef heading
| leveldef heading
parameter heading ’’ delimited parameters undelimited parameters
delimited parameters ’’ empty
| delimited parameters ( parameter type parameter tokens )
parameter type ’’ expr
| suffix
| text
parameter tokens ’’ symbolic token
| parameter tokens , symbolic token
undelimited parameters ’’ empty
| primary symbolic token
| secondary symbolic token
| tertiary symbolic token
| expr symbolic token
| expr symbolic token of symbolic token
| suffix symbolic token
| text symbolic token

(We™ll discuss vardef heading and leveldef heading in Chapter 20.) The basic idea is
that we name the macro to be de¬ned, then we name zero or more delimited parameters
(i.e., parameters in parentheses), then we name zero or more undelimited parameters.
Then comes an ˜=™ sign, followed by the replacement text, and enddef . The ˜=™ sign
might also be ˜:=™ ; both mean the same thing.

Delimited parameters are of type expr, su¬x, or text; two or more param-
eters of the same type may be listed together, separated by commas. For
example, ˜(expr a, b)™ means exactly the same thing as ˜(expr a)(expr b)™. Undelim-
ited parameters have eight possible forms, as shown in the syntax.
166 Chapter 18: De¬nitions (also called Macros)


The replacement text is simply ¬led away for future use, not interpreted, vardef
primarydef
when reads a de¬nition. But a few tokens are treated specially:
secondarydef
tertiarydef
def , vardef , primarydef , secondarydef , and tertiarydef are considered quote
to introduce de¬nitions inside de¬nitions. capsule
enddef ends the replacement text, unless it matches a previous def -like token
(as listed in the preceding rule).
Each symbolic token that stands for a parameter, by virtue of its appear-
ance in the parameter heading or leveldef heading , is changed to a special
internal “parameter token” wherever it occurs in the replacement text. When-
ever this special token is subsequently encountered, will substitute
the appropriate argument.
quote disables any special interpretation of the immediately following token.
A ˜quote™ doesn™t survive in the replacement text (unless, of course, it has
been quoted).
EXERCISE 18.10
Check your understanding of these rules by ¬guring out what the replacement
text is, in the following weird de¬nition:
def foo(text t) expr e of p :=
def t = e enddef; quote def quote t = p enddef
does not expand macros when it reads a de¬nition ; but at almost
all other times it will replace a de¬ned token by the corresponding replacement
text, after ¬nding all the arguments. The replacement text will then be read as if it
had been present in the program all along.
How does determine the arguments to a macro? Well, it knows
what kinds of arguments to expect, based on the parameter heading. Let™s
consider delimited arguments ¬rst:
A delimited expr argument should be of the form ˜( expression )™; the expres-
sion is evaluated and put into a special “capsule” token that will be substituted
for the parameter wherever it appears in the replacement text.
A delimited su¬x argument should be of the form ˜( su¬x )™; subscripts that
occur in the su¬x are evaluated and replaced by numeric tokens. The result
is a list of zero or more tokens that will be substituted for the parameter
wherever it appears in the replacement text.
A delimited text argument should be of the form ˜( text )™, where text is any
sequence of tokens that is balanced with respect to the delimiters surrounding
it. This sequence of tokens will be substituted for the parameter wherever it
appears in the replacement text.
When there are two or more delimited parameters, you can separate the ar-
guments by commas instead of putting parentheses around each one. For
example, three delimited arguments could be written either as ˜(a)(b)(c)™ or
˜(a, b)(c)™ or ˜(a)(b, c)™ or ˜(a, b, c)™. However, this abbreviation doesn™t work
after text arguments, which must be followed by ˜)™ because text arguments
can include commas.
Chapter 18: De¬nitions (also called Macros) 167


Chapter 8 points out that you can use other delimiters besides parentheses. In delimiters
primary
general, a comma following a delimited expr or su¬x argument is equivalent
secondary
to two tokens ˜) (™, corresponding to whatever delimiters enclose that comma. tertiary
expression
EXERCISE 18.11 of
=
After ˜def f(expr a)(text b,c)=...enddef™ and ˜delimiters {{ }}™, what
:=
are the arguments in ˜f{{x,(,}}((}}))™ ? endgroup
end
The rules for undelimited arguments are similar. An undelimited primary, group
secondary, tertiary, or expr is the longest syntactically correct primary , ¬ll
erase
secondary , tertiary , or expression that immediately follows the delimited argu- hide
ments. An undelimited ˜expr x of y™ speci¬es two arguments, found by taking the JOHNSON
WEBSTER
longest syntactically correct expression of primary . In each of these cases, the ex-
pression might also be preceded by an optional ˜=™ or ˜:=™. An undelimited su¬x is
the longest su¬x that immediately follows the delimited arguments; also
allows ˜( su¬x )™ in this case, but not ˜= su¬x ™ or ˜:= su¬x ™. An undelimited text
essentially runs to the end of the current statement; more precisely, it runs to the ¬rst
˜;™ or ˜endgroup™ or ˜end™ that is not part of a group within the argument.
Appendix B contains lots of macros that illustrate these rules. For example,
def ¬ll expr c = addto currentpicture contour c enddef ;
def erase text t = cullit; t withweight ’1; cullit enddef ;
these are slight simpli¬cations of the real de¬nitions, but they retain the basic ideas.
The command ˜erase ¬ll p™ causes ˜¬ll p™ to be the text argument to erase, after
which ˜p™ becomes the expr argument to ¬ll.
EXERCISE 18.12
The ˜pickup™ macro in Appendix B starts with ˜def pickup secondary q™;
why is the argument a secondary instead of an expression?
EXERCISE 18.13
Explain why the following ˜hide ™ macro allows you to hide any sequence of
statements in the midst of an expression:
def hide (text t) = gobble begingroup t; endgroup enddef ;
def gobble primary g = enddef ;




DEFINI TION, s. [de¬nitio, Latin.]
1. A short description of a thing by its properties.
” SAMUEL JOHNSON, A Dictionary of the English Language (1755)

DEFINI TION, n. [L. de¬nitio. See De¬ne.]
1. A brief description of a thing by its properties;
as a de¬nition of wit or of a circle.
” NOAH WEBSTER, An American Dictionary of the English Language (1828)
(page 168)




19
Conditions
and Loops
Chapter 19: Conditions and Loops 169


If decisions never had to be made, life would be much easier, and so would pro- loops
condition
gramming. But sometimes it is necessary to choose between alternatives, and mouth
allows programs to take di¬erent paths depending on the circum- stomach
elseif
stances. You just say something like else
¬
if not decisions : life := programming := easier (much ) condition
elseif choice = a: program a if
:
else: program b ¬ ¬
alternatives
which reduces, for example, to ˜program b ™ if and only if decisions = true and else
:
choice = a. The normal left-to-right order of program interpretation can also be
elseif
modi¬ed by specifying “loops,” which tell the computer to read certain tokens :
repeatedly, with minor variations, until some condition becomes true. We have
seen many examples of these mechanisms already; the purpose of the present
chapter is to discuss the entire range of possibilities.
™s conditions and loops are di¬erent from those in most other
programming languages, because the conditional or iterated code does not have
to ¬t into the syntactic structure. For example, you can write strange things like
p = (if b: 0,0)..(1,5 else: u,v fi)
where the conditional text ˜0, 0) . . (1, 5™ makes no sense by itself, although it
becomes meaningful when read in context. In this respect conditions and loops
behave like macros. They specify rules of token transformation that can be said
to take place in ™s “mouth” before the tokens are actually digested
in the computer™s “stomach.”
The ¬rst conditional example above has three alternatives, in the form
if boolean1 : text1 elseif boolean2 : text2 else: text3 ¬
and the second example has just two; there can be any number of ˜elseif ™ clauses
before ˜else:™. Only one of the conditional texts will survive, namely the ¬rst one
whose condition is true; ˜else:™ is always true. You can also omit ˜else:™ entirely,
in which case ˜else: empty ™ is implied just before the closing ˜¬™. For example,
plain ™s mode setup routine includes the conditional command
if unknown mag : mag := 1; ¬
whose e¬ect is to set mag equal to 1 if it hasn™t already received a value; in this
case there™s only one alternative.
EXERCISE 19.1
Would it be wrong to put the ˜;™ after the ˜¬™ in the example just given?
The informal rules just stated can, of course, be expressed more formally as
rules of syntax:
condition ’’ if boolean expression : conditional text alternatives fi
alternatives ’’ empty
| else : conditional text
| elseif boolean expression : conditional text alternatives
170 Chapter 19: Conditions and Loops


Every conditional construction begins with ˜if ™ and ends with ˜¬™. The conditional texts if
Boolean expressions
are any sequences of tokens that are balanced with respect to ˜if ™ and ˜¬™; furthermore,
Boole
˜elseif ™ and ˜else™ can occur in a conditional text only when enclosed by ˜if ™ and ˜¬™. boolean
boolean primary
Each ˜if ™ and ˜elseif ™ must be followed by a boolean expression , i.e., by an true
false
expression whose value is either ˜true™ or ˜false™. Boolean expressions are (
named after George Boole, the founder of algebraic approaches to logic. Chapter 7 )
begingroup
points out that variables can be of type boolean, and numerous examples of boolean
endgroup
expressions appear in Chapter 8. It™s time now to be more systematic, so that we known
will know the facts about boolean expressions just as we have become well-versed in unknown
cycle
numeric expressions, pair expressions, picture expressions, path expressions, transform
odd
expressions, and pen expressions. Here are the relevant syntax rules: not
boolean secondary
boolean primary ’’ boolean variable and
boolean tertiary
| true | false or
| ( boolean expression ) boolean expression
| begingroup statement list boolean expression endgroup relation
¡
| 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.)

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 .

EXERCISE 20.7
De¬ne a ˜transum™ macro operation that yields the sum of two transforms.
(If t3 = t1 transum t2 , then z transformed t3 = z transformed t1 + z transformed t2 ,
for all pairs z.)
Chapter 20: More About Macros 179


Now we™ve covered all the types of de¬nition , and it™s time to take stock and mastication
expansion process
think about the total picture. ™s mastication process converts an
digestion process
input ¬le into a long sequence of tokens, as explained in Chapter 6, and its digestive Conditions
processes work strictly on those tokens. When a symbolic token is about to be digested, if
¬
looks up the token™s current meaning, and in certain cases will elseif
expand that token into a sequence of other tokens before continuing; this “expansion else
Loops
process” applies to macros and to if and for, as well as to certain other special prim-
for
itives that we shall consider momentarily. Expansion continues until an unexpandable forsu¬xes
token is found; then the digestion process can continue. Sometimes, however, the ex- forever
exitif
pansion is not carried out; for example, after has digested a def token, it
scantokens
stops all expansion until just after it reaches the corresponding enddef . A complete input
list of all occasions when tokens are not expanded appears later in this chapter. ¬lename
endinput
expandafter
Let™s consider all the tokens that cause expansion to occur, whenever expan- backslash
sion hasn™t been inhibited:

Macros. When a macro is expanded, ¬rst reads and evaluates the
arguments (if any), as already explained. (Expansion continues while expr and su¬x
arguments are being evaluated, but it is suppressed within text arguments.) Then
replaces the macro and its arguments by the replacement text.
Conditions. When ˜if ™ is expanded, reads and evaluates the
boolean expression, then skips ahead, if necessary, until coming to either ˜¬™ or a
condition that™s true; then it will continue to read the next token. When ˜elseif ™ or
˜else™ or ˜¬™ is expanded, a conditional text has just ended, so skips to the
closing ˜¬™ and the expansion is empty.
Loops. When ˜for™ or ˜forsu¬xes™ or ˜forever™ is expanded,
reads the speci¬cations up to the colon, then reads the loop text (without expansion)
up to the endfor. Finally it rereads the loop text repeatedly, with expansion. When
˜exitif ™ is expanded, evaluates the following boolean expression and throws
away the semicolon; if the expression proves to be true, the current loop is terminated.
scantokens string primary . When ˜scantokens™ is expanded,
evaluates the following primary expression, which should be of type string. This string
is converted to tokens by the rules of Chapter 6, as if it had been input from a ¬le
containing just one line of text.
input ¬lename . When ˜input™ is expanded, the expansion is null, but
prepares to read from the speci¬ed ¬le before looking at any more to-
kens from its current source. A ¬lename is subject to special restrictions explained
on the next page.
endinput. When ˜endinput™ is expanded, the expansion is null. But the
next time gets to the end of an input line, it will stop reading from the
¬le containing that line.
expandafter. When ˜expandafter™ is expanded, ¬rst reads one
more token, without expanding it; let™s call this token t. Then reads the
token that comes after t (and possibly more tokens, if that token takes an argument),
replacing it by its expansion. Finally, puts t back in front of that expansion.
\. When ˜\™ is expanded, the expansion is null, i.e., empty.
180 Chapter 20: More About Macros


The syntax for ¬lename is not standard in , because di¬erent ¬le names
input
operating systems have di¬erent conventions. You should ask your local sys-
scantokens
tem wizards for details on just how they have decided to implement ¬le names. The scantokens
situation is complicated by the fact that ™s process of converting to tokens delimiters
inner
is irreversible; for example, ˜x01™ and ˜x1.0™ both yield identical sequences of tokens. let
Therefore doesn™t even try to convert a ¬le name to tokens; an input op- newinternal
outer
eration must appear only in a text ¬le, not in a list of tokens like the replacement text
showtoken
of a macro! (You can get around this restriction by saying showvariable
expandafter
scantokens "input foo" everyjob

or, more generally,
scantokens ("input " & fname )
if fname is a string variable containing the ¬lename you want to input.) Although
¬le names have nonstandard syntax, a sequence of six or fewer ordinary letters and/or
digits followed by a space should be a ¬le name that works in essentially the same way
on all installations of . Uppercase letters are considered to be distinct from
their lowercase counterparts, on many systems.
Here now is the promised list of all cases when expandable tokens are not ex-
panded. Some of the situations involve primitives that haven™t been discussed
yet, but we™ll get to them eventually. Expansion is suppressed at the following times:
When tokens are being deleted during error recovery (see Chapter 5).
When tokens are being skipped because conditional text is being ignored.
When is reading the de¬nition of a macro.
When is reading a loop text, or the symbolic token that immedi-
ately follows for or forsu¬xes.
When is reading the text argument of a macro.
When is reading the initial symbolic token of a declared variable
in a type declaration.
When is reading the symbolic tokens to be de¬ned by delimiters,
inner, let, newinternal, or outer.
When is reading the symbolic tokens to be shown by showtoken
or showvariable.
When is reading the token after expandafter, everyjob, or the
˜=™ following let.
The expansion process is not suppressed while reading the su¬x that follows the initial
token of a declared variable , not even in a vardef heading .
Chapter 20: More About Macros 181


SUETONIUS
DOPPING




The two lieutenants,
Fonteius Capito in Germany,
and Claudius Macro in Africa,
who opposed his advancement,
were put down.
” SUETONIUS, Sergius Sulpicius Galba (c. 125 A.D.)

By introducing macro instructions in the source language,
the designer can bring about the same ease of programming
as could be achieved by giving the computer
a more powerful operation list than it really has.
But naturally, one does not get the same advantages
in terms of economy of memory space and computer time
as would be obtained if the more powerful instructions
were really built into the machine.
” O. DOPPING, Computers & Data Processing (1970)
(page 182)




21
Random
Numbers
Chapter 21: Random Numbers 183


It™s fun to play games with by writing programs that incorporate chance
music
an element of chance. You can generate unpredictable shapes, and you can uniformdeviate
add patternless perturbations to break up the rigid symmetry that is usually normaldeviate
random number
associated with mathematical constructions. Musicians who use computers to bell-shaped
synthesize their compositions have found that music has more “life” if its rhythms scatter plots
are slightly irregular and o¬beat; perfect 1“2“3“4 pulses sound pretty dull by
contrast. The same phenomenon might prove to be true in typography.
allows you to introduce controlled indeterminacy in two
ways: (1) ˜uniformdeviate t™ gives a number u that™s randomly distributed be-
tween 0 and t; (2) ˜normaldeviate™ gives a random number x that has the
so-called normal distribution with mean zero and variance one.
More precisely, if t > 0 and u = uniformdeviate t, we will have 0 ¤ u < t,
and for each fraction 0 ¤ p ¤ 1 we will have 0 ¤ u < pt with approximate
probability p. If t < 0, the results are similar but negated, with 0 ≥ u > t. Finally if
t = 0, we always have u = 0; this is the only case where u = t is possible.
A normaldeviate, x, will be positive about half the time and negative about
half the time. Its distribution is “bell-shaped” in the sense that a particular
2
value x occurs with probability roughly proportional to e’x /2 ; the graph of this func-
tion looks something like a bell. The probability is about 68% that |x| < 1, about 95%
that |x| < 2, and about 99.7% that |x| < 3. It™s a pretty safe bet that |x| < 4.

Instead of relying on mathematical formulas to explain this random be-
havior, we can actually see the results graphically by letting draw
some “scatter plots.” Consider the following program, which draws a 10 pt—10 pt
square and puts 100 little dots inside it:
beginchar (incr code , 10pt #, 10pt #, 0);
pickup pencircle scaled .3pt ; draw unitsquare scaled w;
pickup pencircle scaled 1pt ;
for k = 1 upto 100:
drawdot(uniformdeviate w, uniformdeviate w); endfor endchar.
The resulting “characters,” if we repeat the experiment ten times, look like this:
.
And if we replace ˜uniformdeviate w™ by ˜.5w + w/6 — normaldeviate™, we get
.
Finally, if we say ˜drawdot(uniformdeviate w, .5w + w/6 — normaldeviate)™ the
results are a mixture of the other two cases:
.
EXERCISE 21.1
Consider the program fragment ˜if uniformdeviate 1 < 1/3: case a else: case b ¬™.
True or false: case b will occur about three times as often as case a .
184 Chapter 21: Random Numbers


EXERCISE 21.2 logo
craziness
™s uniformdeviate operator usually doesn™t give you an integer. Ex-
plain how to generate random integers between 1 and n, in such a way that each
value will be about equally likely.
EXERCISE 21.3
What does the formula ˜(uniformdeviate 1)[z1 , z2 ]™ represent?
EXERCISE 21.4
Guess what the following program will produce:
beginchar(incr code,100pt#,10pt#,0);
for n:=0 upto 99:
fill unitsquare xscaled 1pt yscaled uniformdeviate h
shifted (n*pt,0); endfor endchar.
EXERCISE 21.5
And what does this puzzle program draw?
beginchar(incr code,24pt#,10pt#,0);
numeric count[];
pickup pencircle scaled 1pt;
for n:=1 upto 100:
x:=.5w+w/6*normaldeviate;
y:=floor(x/pt);
if unknown count[y]: count[y]:=-1; fi
drawdot(x,pt*incr count[y]); endfor endchar.

Let™s try now to put more “life” in the logo, by asking Lady Luck
to add small perturbations to each of the key points. First we de¬ne noise ,

vardef noise = normaldeviate — craziness enddef ;

the craziness parameter will control the degree of haphazard variation. Then we can
write the following program for the logo™s ˜ ™:

beginlogochar ("N", 15);
x1 = leftstemloc + noise ;
x2 = leftstemloc + noise ;
w ’ x4 = leftstemloc + noise ;
w ’ x5 = leftstemloc + noise ; (Figure 21a will be inserted here; too
bad you can™t see it now.)
bot y1 = noise ’ o ;
top y2 = h + o + noise ;
y3 = y4 + ygap + noise ;
bot y4 = noise ’ o ;
top y5 = h + o + noise ;
z3 = whatever [z4 , z5 ];
draw z1 - - z2 - - z3 ; draw z4 - - z5 ; labels(1, 2, 3, 4, 5); endchar.

The illustration here was drawn with craziness = 0, so there was no noise.
Chapter 21: Random Numbers 185


Three trials of the 9 pt ˜ ™ with craziness = .1pt gave the following results: randomseed
Beethoven
beauty
STRINDBERG
MIES VAN DER ROHE


(Figure 21b&c&d will be inserted here; too bad you can™t see it now.)




And here™s what happens if you do similar things to all the letters of , with
craziness decreasing from .45pt to zero in steps of .05pt :




Every time you run a program that refers to random numbers, you™ll get
di¬erent results, because uses the date and time of day to change
its generator. This unpredictable behavior is normally what you want, but it can be
troublesome if your program draws a lovely shape that you™d like to see again. Or
perhaps one of your runs will uncover a program bug; you won™t be able to diagnose
the problem, because it probably won™t recur! The solution is to say
randomseed := numeric expression
and to remember the value of that numeric expression. (The value will automatically
be recorded in the transcript ¬le of your run.) You will get the same sequence of
uniform and normal deviates on any two runs that begin with the same randomseed,
because ™s numbers are only “pseudo-random.”

A musician whom I knew amused himself
by tuning his piano arbitrarily, without any rhyme or reason.
Afterwards he played Beethoven™s Sonate Path´tique by heart.
e
It was an unbelievable delight to hear an old piece come back to life.
I had heard this sonata for twenty years,
never dreaming that it was capable of being developed further.
” AUGUST STRINDBERG, Chance in Artistic Creation (1894)

[Education] must lead us from chance and arbitrariness
to rational clarity and intellectual order.
” L. MIES VAN DER ROHE, Inaugural Address (1938)
(page 186)




22
Strings
Chapter 22: Strings 187


is not a word processor, but a programmer can process ditto
title
words and other short strings of symbols in rudimentary ways. Strings can tracingtitles
help explain what a program is doing; for example, the io.mf ¬le of Chapter 5 proo¬ng
GFtoDVI
mentions "The letter O" as a title that should appear on proofsheets, and it labels
also says "O" in order to identify the position of a character in the output font. concatenation
ampersand
Chapter 6 points out that a string token is any sequence of characters string primary
enclosed in double-quote (") marks, except that you™re not allowed to use the (
)
double-quote character itself in this way. If you need that character, plain - begingroup
provides it in a string of length 1 called ditto . Thus endgroup
jobname
readstring
"A string expression can contain a ˜" & ditto & "™ mark" str
char
even though a string token cannot. decimal
substring
A string expression can be used all by itself as a statement, just as if it
of
were an equation or declaration or command. Such a statement is called a title , string secondary
string tertiary
provided that it is immediately followed by a ˜;™. If tracingtitles > 0 when a
string expression
title is encountered, will type the title on the user™s terminal. If &
**
proo¬ng > 0 when a title is encountered, will copy the title into the
command line
output ¬le, so that it can be put onto proofsheets by postprocessors such as the mfput
GFtoDVI program described in Appendix H.
Appendix H explains how to specify the strings that are used as labels for the
key points on proofsheets.
Here™s the full syntax for string expressions. All of the activity except for
concatenation (˜&™) takes place at the primary level:
string primary ’’ string token
| string variable
| ( string expression )
| begingroup statement list string expression endgroup
| jobname
| readstring
| str su¬x
| char numeric primary
| decimal numeric primary
| substring pair primary of string primary
string secondary ’’ string primary
string tertiary ’’ string secondary
string expression ’’ string tertiary
| string expression & string tertiary
The new features here are jobname, readstring, str, char, decimal, and substring;
we shall consider each of them in turn.
The name of your job ( jobname) is the name of the ¬rst ¬le you input,
provided that the ¬rst line of instructions to (the ˜**™ line or
command line) causes input of some ¬le. Otherwise the job name is mfput, as in
Experiment 1 of Chapter 5.
188 Chapter 22: Strings


When you say ˜readstring™, stops and waits for the user to type readstring
stop
a line at the terminal. The value of readstring is the contents of this line,
su¬x
with trailing spaces eliminated. (You probably should use the message command str
¬rst, to give the user a clue about what to type; for example, see the expr.mf ¬le char
ASCII
of Chapter 8, which gets its input expressions via readstring. The stop macro of decimal representation
Appendix B makes use of the fact that readstring halts the computer; it doesn™t substring
subpath
actually look at the string.)
graph paper
reverse
An arbitrary su¬x is converted to a string by str, using the method by ASCII
which displays su¬x arguments in diagnostic typeouts. Negative oct
octal notation
subscripts are enclosed in square brackets; spaces or dots are inserted between tokens
hex
whose characters belong to the same class (according to the table in Chapter 6). For hexadecimal notation
example, if n = 1 then ˜str x[n]a™ is "x1a"; ˜str x n a™ is "x.n.a".

The result of ˜char n™ is a string of length 1, representing the character whose
ASCII code is n. (Appendix C explains this code.) The value of n is
¬rst rounded to the nearest integer, then multiples of 256 are added or subtracted if
necessary until 0 ¤ n < 256; this de¬nes char n in all cases.

The decimal representation of a known numeric value x is available in string
form as ˜decimal x™. If x is negative, the ¬rst character of this string will
be ˜-™. If x is not an integer, a decimal point will be included, followed by as many
digits as are necessary to characterize the value. (These conventions are the same as
those illustrated in the example outputs of Chapter 8.)

The rules for substring are like the rules for subpath in Chapter 14. -
thinks of a string as if its characters were written in the squares of a
piece of graph paper, between coordinates x = 0 and x = n, where n is the length
of the string. In simple cases, substring (a, b) then refers to the characters between
x = a and x = b. The rules for the general case are slightly more involved: If b < a, the
result will be the reverse of substring (b, a). Otherwise a and b are replaced respectively
by max(0, min(n, round a)) and max(0, min(n, round b)); this leads to the simple case
0 ¤ a ¤ b ¤ n described above, when the resulting string has length b ’ a.

<<

. 6
( 13)



>>