Low-level as of October 29, 1995 Beware: These specifications change daily!

This is a preliminary description of what the new language will look like at the lowest level. Please forgive the author for the terseness of this document; there hasn’t been time to explain things yet. Also please remember that the low-level language is not what programmers will usually be writing; it is intended as a vehicle for defining nicer high-level languages. users will almost always work with a set of macros and other definitions called a “base” file; the PLAIN base will be defined in The book, in a fashion similar to the way PLAIN format has been defined in The TEXbook.

1. The lowest level: Tokens. is governed by sequences of tokens, which it gets either by reading a file or by regurgitating a list of tokens that were previously read. So we can understand tokens by understanding what happens when reads from a file. A file is a sequence of lines of text, where each line of text is a sequence of zero or more characters. The characters are assumed to be those of standard ASCII (codes ´ 040 through ´ 176 in Appendix C of The TEXbook). Any other characters that might appear in the file are flagged as errors, except that certain systems may make allowances for certain characters. [For example, the WAITS implementation places codes ´ 030 , ´ 032 , ´ 034 , and ´ 035 into the same class as equal signs, because the special keys for those two-character combinations on WAITS keyboards are too tempting to ignore.]

Each line of text is converted into zero or more tokens according to the following rules, repeated until no more characters remain on the line:

1) If the next character is a space, or if it’s a period that isn’t followed by a decimal digit or a period, ignore it and move on.

2) If the next character is a percent sign, ignore it and also ignore everything else that remains on the current line. (Percent signs allow you to put comments in your file that are unseen by .)

3) If the next character is a decimal digit or a period that’s followed by a decimal digit, the next token is called a numeric token. It is the longest sequence of contiguous characters starting at the current place that satisfies the following syntax:

⟨numeric token⟩ ⟶ ⟨digit string⟩ | . ⟨digit string⟩ | ⟨digit string⟩. ⟨digit string⟩ ⟨decimal digit⟩ ⟶ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ⟨digit string⟩ ⟶ ⟨decimal digit⟩ | ⟨digit string⟩ ⟨decimal digit⟩

Numeric tokens are interpreted according to ordinary decimal notation; the value of the number must be less than 4096. converts decimal fractions to the nearest multiple of 2^{−16}.

4) If the next character is a double-quote mark ( " ), the next token is called a string token. It consists of all characters following the double-quote up to but not including the next double-quote on the current line. There must be at least one more double-quote remaining on the line, otherwise you get an error message.

5) If the next character is a left parenthesis, a right parenthesis, a comma, or a semicolon, the next token is that single character.

6) Otherwise the next token consists of the next character together with all immediately following characters of the same class.

Rules 1–5 tell what to do for 17 of the 95 possible ASCII characters that might be next. The most interesting rule is number 6, which depends on a breakdown of the remaining 78 ASCII characters into 12 classes as shown in Table 1. Two characters are in the same class if and only if they belong to the same row in the class table.

For example, the (ridiculous) line

              xx3.1.6..[[a+-bc_d.e] ]"a string %" <>$1."+-""" % forget this

produces 17 tokens: ‘xx’, ‘3.1’ (which is numeric), ‘.6’ (another numeric), ‘..’, ‘[[’, ‘a’, ‘+-’, ‘bc_d’, ‘e’, ‘]’, ‘]’, ‘a string %’ (which is indeed a string), ‘<>’, ‘$’, ‘1’ (another numeric token), ‘+-’ (a string, hence different from the other ‘+-’), and ‘’ (an empty string). Notice that three of the spaces and two of the periods were deleted by rule 1.

         ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz
                                 <=>:|
                                   `'
                                   +-
                                  /*\
                                   !?
                                  #&@$
                                   ^~
                                   [
                                   ]
                                   {}

.  (see rules 1, 3, 6)
,  (see rule 5)
;  (see rule 5)
(  (see rule 5)
)  (see rule 5)
"  (see rule 4)

                               0123456789  (see rule 3)

%  (see rule 2)

Table 1. The visible ASCII characters, divided into classes. Characters in the bottom eight rows are subject to special rules as indicated.

2. The next lowest level: Variables. But what do tokens mean? Well, numeric tokens stand for numbers and string tokens stand for strings, but the other tokens are just arbitrary symbols that can stand for almost anything. Let’s say that tokens of the third kind are symbolic tokens.

Some of the symbolic tokens have predefined “primitive” meanings when begins its operations, but it is possible to change the meaning of any symbolic token. The ‘let’ command does this; one simply says ‘let’ ⟨symbolic token⟩ = ⟨symbolic token⟩ ’. For example, you can even make a left parenthesis denote the same thing as ‘+’, if you want to confuse everybody who tries to read your code.

Symbolic tokens are further subdivided into two categories based on their current meaning. If the token currently stands for one of ’s primitives, or for a macro that was not defined with vardef, we shall call it a spark; otherwise we call it a tag. Thus, almost every token you can think of is initially a tag, available for use as a name, except those that were needed to define ’s fundamental operations. Such pre-reserved tokens can be redefined and used as names, if you want to use them in your own way; but you probably won’t have to, since they’re generally words that don’t make desirable names.

Tags are used for the variables in programs. These variables can be structured, like arrays and records in more conventional programming languages; for example, ‘x32a’ might be a variable that would be written ‘x[32].a’ in PASCAL. A variable identifier has the following syntax:

⟨variable⟩ ⟶ ⟨tag⟩ ⟨suffix⟩ | ⟨internal quantity⟩

⟨suffix⟩ ⟶ ⟨empty⟩ | ⟨suffix⟩ ⟨subscript⟩ | ⟨suffix⟩ ⟨tag⟩ | ⟨suffix⟩ ⟨internal quantity⟩ ⟨subscript⟩ ⟶ ⟨numeric token⟩ | [ ⟨numeric expression⟩]

A ⟨suffix⟩ is taken to be as long as possible; i.e., if a ⟨suffix⟩ is followed by a ⟨subscript⟩ or a ⟨tag⟩, the ⟨suffix⟩ will be extended.

Notice the two permissible forms of subscripts: a numeric token can be written without brackets, or a bracketed expression can be used. For example, if i is a variable whose value is 7, the variable identifiers ‘b7’, ‘b007’, ‘b[7]’, ‘b[i]’, and ‘b[21-2i]’ are all equivalent. On the other hand, ‘b.007’ would be different, since it involves the fractional subscript ‘.007’. Also, ‘b.i’ would be different; in this case the ‘i’ is simply a tag that appears as a suffix, it’s not a subscript.

Incidentally, the ‘[’ and ‘]’ that appear in the syntax for ⟨subscript⟩ stand for any tokens that have ’s primitive meanings for left bracket and right bracket, respectively. They aren’t necessarily brackets; indeed, if the tokens ‘[’ and ‘]’ have been redefined, they no longer can be used to produce subscripts. Similar remarks apply to all of the tokens in all of the rules below. doesn’t look at the form of a token; only the current meaning is relevant.

Variables can be of many types:

    ⟨type⟩ ⟶ numeric | string | boolean | path | pen | picture | transform | pair

To specify a type other than numeric, you simply give a type declaration that lists the relevant identifiers. For example, the declaration

                                  pair right, left, a.zz

says that ‘right’, ‘left’, and ‘a.zz’ will be variables of type pair, so that equations like

                              right = -left = 2a.zz = (1,0)

can be given later. These equations, incidentally, define right = (1, 0), left = (−1, 0), and a.zz = (.5, 0).

The declaration of an array variable is independent of all the subscript values; all subscripts in the declaration are therefore given in a special collective form. For example,

                                 path p[], x[]arc, f[][]

declares all variables of the form p[i] and x[i]arc and f[i][j] to be of type path. This declaration doesn’t affect the types of variables like p or p3arc. considers a declaration like ‘path p3’ to be illegal, since it falsely implies that only p3 (not p2) is a path; subscripts in a type declaration must be collective.

Here are the formal syntax rules:

⟨declaration⟩ ⟶ ⟨type⟩ ⟨declaration list⟩

⟨declaration list⟩ ⟶ ⟨declared variable⟩ | ⟨declaration list⟩, ⟨declared variable⟩ ⟨declared variable⟩ ⟶ ⟨symbolic token⟩ ⟨declared suffix⟩ ⟨declared suffix⟩ ⟶ ⟨empty⟩ | ⟨declared suffix⟩ []

| ⟨declared suffix⟩ ⟨tag⟩ | ⟨declared suffix⟩ ⟨internal quantity⟩

A variable that hasn’t been declared is automatically of type numeric, but its value is undefined until it appears in an equation. Declarations destroy all previous values; thus, a declaration like ‘numeric x’ isn’t redundant, since it removes any existing value that x may have had, of whatever type. Incidentally, this declaration doesn’t affect other values like x2 or x2arc or x.x that might coexist with x.

3. The next lowest level: Expressions. The declaration ‘delimiters ⟨symbolic token⟩ ⟨symbolic token⟩ ’ declares a pair of tokens to be matching delimiters. For example, the PLAIN base says ‘delimiters ()’ so that parentheses do the usual thing. Any distinct symbolic tokens can be defined to act as delimiters, and many different pairs of delimiters can be in use simultaneously.

There are eight kinds of expressions in , corresponding to the eight types numeric, string, etc. The full syntax is quite long, but most of it falls into a simple pattern: There are four levels of precedence called the primary level (tightest binding), the secondary level (next tightest), the tertiary level (next loosest), and the expression level (loosest); they’re something like freshmen, sophomores, juniors, and seniors. If α, β, and γ are types, most of the syntax rules are of the following general form:

⟨α primary⟩ ⟶ ⟨α variable⟩ | ⟨α constant⟩ | ⟨left delimiter⟩ ⟨α expression⟩ ⟨right delimiter⟩ | begingroup ⟨statement list⟩ ⟨α expression⟩ endgroup | ⟨operator that takes type β to type α⟩ ⟨β primary⟩ ⟨α secondary⟩ ⟶ ⟨α primary⟩

| ⟨β secondary⟩ ⟨multiplicative operator taking types β and γ to type α⟩ ⟨γ primary⟩

⟨α tertiary⟩ ⟶ ⟨α secondary⟩

| ⟨β tertiary⟩ ⟨additive operator taking types β and γ to type α⟩ ⟨γ secondary⟩

⟨α expression⟩ ⟶ ⟨α tertiary⟩ | ⟨β expression⟩ ⟨external operator taking types β and γ to type α⟩ ⟨γ tertiary⟩

These schematic rules don’t give the whole story, but they give the general structure of the plot.

The complete syntax appears below, as a set of rules that can be used as a summary of all the primitive features that provides within expressions. (We shall see later that macros can be used to extend the language; hence this list doesn’t really exhaust the possibilities.)

⟨expression⟩ ⟶ ⟨boolean expression⟩ | ⟨string expression⟩ | ⟨path expression⟩ | ⟨pen expression⟩ | ⟨picture expression⟩ | ⟨transform expression⟩ | ⟨numeric expression⟩ | ⟨pair expression⟩

⟨primary⟩ ⟶ ⟨boolean primary⟩ | ⟨string primary⟩ | ⟨path primary⟩ | ⟨pen primary⟩ | ⟨picture primary⟩ | ⟨transform primary⟩ | ⟨numeric primary⟩ | ⟨pair primary⟩ | ⟨future pen primary⟩

⟨secondary⟩ ⟶ ⟨boolean secondary⟩ | ⟨string secondary⟩ | ⟨path secondary⟩ | ⟨pen secondary⟩ | ⟨picture secondary⟩ | ⟨transform secondary⟩ | ⟨numeric secondary⟩ | ⟨pair secondary⟩ | ⟨future pen secondary⟩

⟨tertiary⟩ ⟶ ⟨boolean tertiary⟩ | ⟨string tertiary⟩ | ⟨path tertiary⟩ | ⟨pen tertiary⟩ | ⟨picture tertiary⟩ | ⟨transform tertiary⟩ | ⟨numeric tertiary⟩ | ⟨pair tertiary⟩

Boolean expressions:

⟨boolean primary⟩ ⟶ ⟨boolean variable⟩ | true | false | ⟨left delimiter⟩ ⟨boolean expression⟩ ⟨right delimiter⟩ | begingroup ⟨statement list⟩ ⟨boolean expression⟩ endgroup | known ⟨primary⟩ | cycle ⟨primary⟩ | ⟨type⟩ ⟨primary⟩ | odd ⟨numeric primary⟩ | charexists ⟨numeric primary⟩ | not ⟨boolean primary⟩

⟨boolean secondary⟩ ⟶ ⟨boolean primary⟩

| ⟨boolean secondary⟩ and ⟨boolean primary⟩

⟨boolean tertiary⟩ ⟶ ⟨boolean secondary⟩

| ⟨boolean tertiary⟩ or ⟨boolean secondary⟩

⟨boolean expression⟩ ⟶ ⟨boolean tertiary⟩ | ⟨numeric expression⟩ ⟨relation⟩ ⟨numeric tertiary⟩ | ⟨string expression⟩ ⟨relation⟩ ⟨string tertiary⟩ | ⟨pair expression⟩ ⟨relation⟩ ⟨pair tertiary⟩ | ⟨boolean expression⟩ ⟨relation⟩ ⟨boolean tertiary⟩

⟨relation⟩ ⟶ < | <= | > | >= | = | <> String expressions:

⟨string primary⟩ ⟶ ⟨string variable⟩ | ⟨string token⟩ | ⟨left delimiter⟩ ⟨string expression⟩ ⟨right delimiter⟩ | begingroup ⟨statement list⟩ ⟨string expression⟩ endgroup

          | jobname | readstring | str ⟨suffix⟩

| char ⟨numeric primary⟩ | decimal ⟨numeric primary⟩

| substring ⟨pair expression⟩ of ⟨string primary⟩

⟨string secondary⟩ ⟶ ⟨string primary⟩ ⟨string tertiary⟩ ⟶ ⟨string secondary⟩ ⟨string expression⟩ ⟶ ⟨string tertiary⟩

| ⟨string expression⟩ & ⟨string tertiary⟩

Path expressions:

⟨path primary⟩ ⟶ ⟨path variable⟩ | ⟨left delimiter⟩ ⟨path expression⟩ ⟨right delimiter⟩ | begingroup ⟨statement list⟩ ⟨path expression⟩ endgroup | reverse ⟨path primary⟩ | makepath ⟨pen primary⟩ | makepath ⟨future pen primary⟩ | subpath ⟨pair expression⟩ of ⟨path primary⟩

⟨path secondary⟩ ⟶ ⟨path primary⟩ | ⟨path secondary⟩ ⟨transform⟩ ⟨path tertiary⟩ ⟶ ⟨path secondary⟩ ⟨path subexpression⟩ ⟶ ⟨path tertiary⟩ | ⟨pair tertiary⟩

| ⟨path subexpression⟩ ⟨path join⟩ ⟨path tertiary⟩

⟨path join⟩ ⟶ ⟨direction specifier⟩ ⟨basic path join⟩ ⟨direction specifier⟩ ⟨direction specifier⟩ ⟶ ⟨empty⟩ | {curl ⟨numeric expression⟩ }

| { ⟨pair expression⟩ } | { ⟨numeric expression⟩, ⟨numeric expression⟩ }

⟨basic path join⟩ ⟶ & | .. | .. ⟨tension⟩.. | .. ⟨controls⟩.. ⟨tension⟩ ⟶ tension ⟨tension amount⟩ | tension ⟨tension amount⟩ and ⟨tension amount⟩ ⟨tension amount⟩ ⟶ ⟨numeric primary⟩ | atleast ⟨numeric primary⟩ ⟨controls⟩ ⟶ controls ⟨pair primary⟩ | controls ⟨pair primary⟩ and ⟨pair primary⟩ ⟨path expression⟩ ⟶ ⟨path subexpression⟩ | ⟨path subexpression⟩ ⟨direction specifier⟩

| ⟨path subexpression⟩ ⟨path join⟩ cycle

Picture expressions:

⟨picture primary⟩ ⟶ ⟨picture variable⟩ | nullpicture | ⟨left delimiter⟩ ⟨picture expression⟩ ⟨right delimiter⟩ | begingroup ⟨statement list⟩ ⟨picture expression⟩ endgroup | ⟨plus or minus⟩ ⟨picture primary⟩

⟨picture secondary⟩ ⟶ ⟨picture primary⟩ | ⟨picture secondary⟩ ⟨transform⟩ ⟨picture tertiary⟩ ⟶ ⟨picture secondary⟩

| ⟨picture tertiary⟩ ⟨additive op⟩ ⟨picture secondary⟩

⟨picture expression⟩ ⟶ ⟨picture tertiary⟩ Pen expressions:

⟨pen primary⟩ ⟶ ⟨pen variable⟩ | nullpen | ⟨left delimiter⟩ ⟨pen expression⟩ ⟨right delimiter⟩ | begingroup ⟨statement list⟩ ⟨pen expression⟩ endgroup

⟨future pen primary⟩ ⟶ pencircle | makepen ⟨path primary⟩ ⟨pen secondary⟩ ⟶ ⟨pen primary⟩ ⟨future pen secondary⟩ ⟶ ⟨future pen primary⟩ | ⟨future pen secondary⟩ ⟨transform⟩

| ⟨pen secondary⟩ ⟨transform⟩

⟨pen tertiary⟩ ⟶ ⟨pen secondary⟩ | ⟨future pen secondary⟩ ⟨pen expression⟩ ⟶ ⟨pen tertiary⟩

Transform expressions:

⟨transform primary⟩ ⟶ ⟨transform variable⟩ | ⟨left delimiter⟩ ⟨transform expression⟩ ⟨right delimiter⟩ | begingroup ⟨statement list⟩ ⟨transform expression⟩ endgroup

⟨transform secondary⟩ ⟶ ⟨transform primary⟩ | ⟨transform secondary⟩ ⟨transform⟩ ⟨transform⟩ ⟶ rotated ⟨numeric primary⟩ | slanted ⟨numeric primary⟩ | scaled ⟨numeric primary⟩

| shifted ⟨pair primary⟩ | transformed ⟨transform primary⟩ | xscaled ⟨numeric primary⟩ | yscaled ⟨numeric primary⟩ | zscaled ⟨pair primary⟩

⟨transform tertiary⟩ ⟶ ⟨transform secondary⟩ ⟨transform expression⟩ ⟶ ⟨transform tertiary⟩

Numeric expressions:

⟨numeric primary⟩ ⟶ ⟨numeric variable⟩ | ⟨numeric token primary⟩ | normaldeviate | ⟨expression parameter⟩ | ⟨internal quantity⟩ | ⟨left delimiter⟩ ⟨numeric expression⟩ ⟨right delimiter⟩ | begingroup ⟨statement list⟩ ⟨numeric expression⟩ endgroup | oct ⟨string primary⟩ | hex ⟨string primary⟩ | ASCII ⟨string primary⟩ | length ⟨string primary⟩ | length ⟨path primary⟩ | length ⟨pair primary⟩ | length ⟨numeric primary⟩ | ⟨pair part specifier⟩ ⟨pair primary⟩ | ⟨transform part specifier⟩ ⟨transform primary⟩ | ⟨unary operator⟩ ⟨numeric primary⟩ | angle ⟨pair primary⟩ | totalweight ⟨picture primary⟩ | turningnumber ⟨path primary⟩ | directiontime ⟨pair expression⟩ of ⟨path primary⟩

⟨pair part specifier⟩ ⟶ xpart | ypart

⟨transform part specifier⟩ ⟶ xxpart | xypart | yxpart | yypart | xpart | ypart

⟨scalar multiplication operator⟩ ⟶ + | -

| ⟨numeric token primary not followed by + or - or numeric token⟩

⟨numeric token primary⟩ ⟶ ⟨numeric token not followed by ‘/⟨numeric token⟩’ ⟩

| ⟨numeric token⟩ / ⟨numeric token⟩

⟨unary operator⟩ ⟶ ⟨scalar multiplication operator⟩

| sqrt | mexp | mlog | sind | cosd | floor | uniformdeviate

⟨numeric secondary⟩ ⟶ ⟨numeric primary⟩ | ⟨numeric secondary⟩ ⟨multiplicative op⟩ ⟨numeric primary⟩ | ⟨numeric secondary⟩ [ ⟨numeric expression⟩, ⟨numeric expression⟩]

⟨multiplicative op⟩ ⟶ * | / ⟨numeric tertiary⟩ ⟶ ⟨numeric secondary⟩

| ⟨numeric tertiary⟩ ⟨additive op⟩ ⟨numeric secondary⟩ | ⟨numeric tertiary⟩ ⟨pythagorean additive op⟩ ⟨numeric secondary⟩

⟨additive op⟩ ⟶ + | - ⟨pythagorean additive op⟩ ⟶ ++ | +-+ ⟨numeric expression⟩ ⟶ ⟨numeric tertiary⟩

Pair expressions:

⟨pair primary⟩ ⟶ ⟨pair variable⟩ | ⟨left delimiter⟩ ⟨numeric expression⟩, ⟨numeric expression⟩ ⟨right delimiter⟩ | ⟨left delimiter⟩ ⟨pair expression⟩ ⟨right delimiter⟩ | begingroup ⟨statement list⟩ ⟨pair expression⟩ endgroup | point ⟨numeric expression⟩ of ⟨path primary⟩ | precontrol ⟨numeric expression⟩ of ⟨path primary⟩ | postcontrol ⟨numeric expression⟩ of ⟨path primary⟩ | penoffset ⟨pair expression⟩ of ⟨pen primary⟩ | penoffset ⟨pair expression⟩ of ⟨future pen primary⟩ | ⟨scalar multiplication operator⟩ ⟨pair primary⟩

⟨pair secondary⟩ ⟶ ⟨pair primary⟩ | ⟨pair secondary⟩ ⟨multiplicative op⟩ ⟨numeric primary⟩ | ⟨numeric secondary⟩ * ⟨pair primary⟩ | ⟨numeric secondary⟩ [ ⟨pair expression⟩, ⟨pair expression⟩] | ⟨pair secondary⟩ ⟨transform⟩

⟨pair tertiary⟩ ⟶ ⟨pair secondary⟩ | ⟨pair tertiary⟩ ⟨additive op⟩ ⟨pair secondary⟩ | ⟨path tertiary⟩ intersectiontimes ⟨path secondary⟩

⟨pair expression⟩ ⟶ ⟨pair tertiary⟩

One of the most important consequences of these rules is that always knows the type of the expression it is dealing with. For example, a ⟨pair variable⟩ is a variable that has been declared to have type pair; such a variable will be recognized as a ⟨pair expression⟩ and not as any other kind of expression. There are only a few exceptions: (1) A ⟨pair tertiary⟩ can be a ⟨path subexpression⟩; but it is considered to be only a ⟨pair tertiary⟩ unless the ⟨path subexpression⟩ interpretation is mandatory, i.e., unless followed by ‘{’, ‘..’, or ‘&’. The boolean expression ‘path((0,0))’ is false. (2) A ⟨boolean expression⟩ like ‘x=y’ that involves the equality relation looks something like an equation. will consider an equals sign to be a ⟨relation⟩ unless the expression to its left occurs at the very beginning of a statement, or just after an equals sign or ‘:=’ in an equation or assignment. (3) Similarly, a ⟨type⟩ that occurs at the beginning of a statement is not considered to be part of a ⟨boolean primary⟩; it is considered to be the beginning of a type declaration. (4) After a ⟨path join⟩, the ‘cycle’ operator is not considered to be part of a ⟨boolean primary⟩.

4. Macro definitions. ’s most powerful way to produce new high-level constructions is to make one token stand for a combination of other tokens. One easy way to make this happen is to say

def ⟨symbolic token⟩ = ⟨replacement text⟩ enddef;

this is called a definition. For example, the definition

                                   def -- = - - enddef

simply says that the token ‘--’ is to be replaced by two consecutive ‘-’ tokens. Here’s an even more trivial definition:

                                     def \ = enddef;

it causes a single backslash token to be replaced by nothing at all. ( actually has this definition built in, because it makes the use of analogous to the use of TEX, especially in command lines when you’re running the program.)

More interesting definitions include parameters that are replaced by arguments when the token appears later. In this way definitions provide the capabilities of subroutines as well as the features of simple macro expansion. It’s convenient for the sake of brevity to give the general rules first, and examples later—even though good expository technique would go the other way; so here’s more syntax:

⟨definition⟩ ⟶ ⟨definition heading⟩ = ⟨replacement text⟩ enddef ⟨definition heading⟩ ⟶ def ⟨symbolic token⟩ ⟨parameter heading⟩ | vardef ⟨defined variable⟩ ⟨parameter heading⟩ ⟨parameter heading⟩ ⟶ ⟨parameter list⟩ ⟨undelimited parameter heading⟩ ⟨parameter list⟩ ⟶ ⟨empty⟩ | ⟨parameter list⟩ ⟨parameter declaration⟩ ⟨parameter declaration⟩ ⟶ ⟨left delimiter⟩ ⟨parameter type⟩ ⟨parameter tokens⟩ ⟨right delimiter⟩ ⟨parameter type⟩ ⟶ expr | text | suffix ⟨parameter tokens⟩ ⟶ ⟨tag⟩ | ⟨parameter tokens⟩, ⟨tag⟩ ⟨defined variable⟩ ⟶ ⟨declared variable⟩ | ⟨declared variable⟩ @#

For example, the parameter list

                             (suffix i,j)(text foo)(expr */*)

introduces four parameter tokens ‘i’, ‘j’, ‘foo’, and ‘*/*’; they will be treated specially whenever they occur within the ⟨replacement text⟩.

The ⟨replacement text⟩ is any sequence of tokens that is balanced with respect to unquoted def and enddef tokens. This rule needs some explanation: There’s a primitive operation (initially called quote) that inhibits special interpretation of whatever token follows it in a replacement text. A few tokens are treated specially when is scanning the replacement text of a definition, unless they’ve been quoted:

1) def, vardef, primarydef, secondarydef, and tertiarydef, which introduce definitions inside definitions.

2) enddef, which ends the replacement text unless it is matched by a preceding def-like token as listed in rule 1.

3) a parameter token (like ‘i’ or ‘*/*’ in the example above), which is replaced by a special internal token that will tell to substitute an argument when this token is encountered again.

4) quote, which disables any special interpretation of the immediately following token; this token doesn’t survive in the replacement text, unless of course it has been quoted.

5) #@, @, and @#, which will be replaced respectively by the prefix, the name, and the suffix of this macro when it’s used. (This rule applies only to vardef macros; and @# is replaced only when the ⟨defined variable⟩ in the definition heading ends with @#.)

Rule 5 is the only unusual one, but the examples below should make it clear. The mnemonic for distinguishing #@ from @# is that the @ sign represents where the macro is “at,” and the other sign retrieves the tokens that either precede or follow the “at” position.

It may be worthwhile to reiterate the fact that these rules don’t really apply to the specific tokens enddef, quote, #@, @, and @#; they apply to tokens whose meaning (at the time is recording the definition) is the same as the primitive meaning that those other tokens had when was started up. In particular, if the meanings of enddef, quote, #@, @, and @# have changed at the time of definition recording, no quoting is actually necessary. But some other token had better have received the meaning of enddef, or the definition will never end!

A defined quantity that has parameters must be supplied with corresponding arguments when it is used. The arguments are enclosed in delimiters (usually parentheses). It’s also possible to use a comma between arguments; in this context a comma can be thought of as an abbreviation for ⟨right delimiter⟩ ⟨left delimiter⟩ with respect to the delimiter pair that preceded the comma.

The argument corresponding to a parameter of type expr can be any ⟨expression⟩. This expression is effectively parenthesized before it is substituted into the replacement text, hence it occurs as a ⟨primary⟩ in expressions. Expression parameters are evaluated as much as possible before the macro is invoked, but they need not have “known” values. They should not appear on the left of ‘:=’ operations.

The argument corresponding to a parameter of type suffix can be any ⟨suffix⟩. Subscripts in that suffix, if any, will have been evaluated and replaced by (signed) numeric tokens of the corresponding value, before the argument is actually substituted into the replacement text.

The argument corresponding to a parameter of type text is any sequence of tokens that are balanced with respect to the enclosing delimiters. This means that text arguments cannot be followed by commas. For example, a list of three arguments can usually be given either as ‘(a,b,c)’ or ‘(a,b)(c)’ or ‘(a)(b,c)’ or ‘(a)(b)(c)’; but only the second and last of these alternatives is permitted when the second argument corresponds to a text parameter. Since delimiters need not be parentheses, a text argument need not be balanced with respect to parentheses; but it’s usually not a good idea to play with unbalanced parentheses unless you have a really special reason. Text arguments are not “evaluated” at the time of a macro call; they are simply stored away, and substituted for the corresponding parameter when it shows up in the replacement text.

The defined quantity in a vardef can be any ⟨defined variable⟩, not simply a ⟨tag⟩; for example, you can define ‘a[]b@#’. What does this mean? Well, it means that a supposed variable name like ‘a35b42c.d’ becomes a macro call instead. The prefix that gets substituted for #@ in the replacement text will be ‘a35’ in this example; the name that gets substituted for @ will be ‘b’; and the suffix that gets substituted for @# will be ‘42c.d’. In simpler cases the prefix and suffix are empty. If there’s no @# at the end of the ⟨defined variable⟩, the suffix part is always empty. For example, after a definition of a[]b, the text ‘a35b42c.d’ will be interpreted as simply ‘a35b’ and will not look ahead for a suffix.

One of the important definitions in the PLAIN base is

                              vardef z@# = (x@#,y@#) enddef;

it converts variable names like ‘z20’ and ‘z[i]r’ into ‘(x20,y20)’ and ‘(x[i]r,y[i]r)’, respectively, making it possible to give convenient names to the components of a pair variable without explicitly defining z as a pair variable. Consider also the definition

                           vardef p[]slope=(#@dx,#@dy) enddef;

this converts, e.g., ‘p5slope’ into ‘(p5dx,p5dy)’.

actually puts begingroup...endgroup around the replacement text of vardef’ed macros, in order to make them variable-like. Thus, ‘z20’ really stands for ‘begingroup (x20,y20) endgroup’.

Here now are a few more examples, as promised. The first one is intended to set up a triple of points that represent the position of a broad pen. For example, ‘penpos(3,20,45)’ will stand for pen position 3 at which the pen is 20 pixels broad and inclined at an angle of 45^{∘}; there will be three points z3, z3l, and z3r, representing the middle of the pen, its left edge, and its right edge. Here’s one way to define penpos accordingly:

                      def penpos(suffix i)(expr l,theta)=
                           z.i.r-z.i.quote l = (l,0) rotated theta;
                           z.i = .5[z.i.quote l, z.i.r] enddef
Now the tokens ‘penpos(4,15,d+90)’ will expand into ‘z4r-z4l=(15,0)rotated135; z4=.5[z4l,z4r]’, if

d=45. It wouldn’t have been necessary to quote any of the appearances of ‘l’ if another name had been chosen for the parameter. For example,

                       def penpos(suffix i)(expr length,theta)=
                            z.i.r-z.i.l = (length,0) rotated theta;
                            z.i = .5[z.i.l, z.i.r] enddef

would have been simpler. The quote operation has been provided mostly to permit definitions within definitions, not to compensate for poorly chosen parameter names.

The following example illustrates the use of a text parameter.

                 def label(text t)=
                      forsuffixes $=t: autolabel(z$,"point"&str$); endfor

The expansion of ‘label(1,[i+1],7a)’ will be

               forsuffixes $=1,[i+1],7a: autolabel(z$,"point"&str$); endfor

and this, in turn, is a macro-like construction that essentially expands into

        autolabel(z1,"point1"); autolabel(z2,"point2"); autolabel(z7a,"point7a");

if i=1, after which the ‘autolabel’ and ‘z’ macros expand the text even more.

Going back to the syntax for ⟨parameter heading⟩, you’ll note that there’s something called an ⟨undelimited parameter heading⟩ that hasn’t yet been explained. Well, here’s the missing syntax:

⟨undelimited parameter heading⟩ ⟶ ⟨empty⟩ | primary ⟨tag⟩ | secondary ⟨tag⟩ | tertiary ⟨tag⟩ | expr ⟨tag⟩ | expr ⟨tag⟩ of ⟨tag⟩ | suffix ⟨tag⟩ | text ⟨tag⟩

In this case the macro works just as usual, but its arguments are parsed like the components of expressions or statements; no delimiters are required. The first undelimited argument may be preceded by an optional ‘=’ or ‘:=’ (except in the case of undelimited text arguments). When ‘primary ⟨tag⟩ ’ is given, the argument is a ⟨primary⟩; when ‘expr ⟨tag⟩ ’ is given, the argument is an ⟨expression⟩; and ‘expr ⟨tag⟩ of ⟨tag⟩ ’ parses the first argument as an expression and the second as a primary. This essentially extends the syntax of expressions so that additional operators are provided. An undelimited text argument runs until the end of the current statement; more precisely, until the next semicolon or endgroup or end that is not

nested inside begingroup..endgroup.

For example, plain defines rounding as follows:

                       vardef round primary x = floor(x+.5) enddef;

it’s not necessary to put parenthesis around the argument when that argument is a numeric primary as in ‘round 1.5u’. And here’s a slightly more complex example that rounds its argument to a “good” value with respect to a given “pen width” w[i]:

                               vardef good[] primary x=
                                 begingroup save t;
                                 if odd w@: t=(floor x)+.5;
                                 else: t=round x;
                                 fi; t endgroup enddef

Now, for example, if w3=4.9 and x10=15.2, the result of ‘good3 x10’ will be 15.5.

The following example defines a transform that is like a given one but fixes the origin:

           vardef unshifted primary t = t shifted-((0,0) transformed t) enddef

And here’s an absolute-value operator that works on pairs and numbers:

                       def abs primary x =
                         if pair x: (xpart x ++ ypart x)
                         elseif numeric x: if x<0: (-x) else: x fi
                         else: x fi
                         enddef

It’s also possible to define macros that act as infix operators:

⟨infix definition⟩ ⟶ ⟨level indicator⟩ ⟨tag⟩ ⟨symbolic token⟩ ⟨tag⟩ = ⟨replacement text⟩ enddef

    ⟨level indicator⟩ ⟶ primarydef | secondarydef | tertiarydef

For example,

                         secondarydef x++y = sqrt(x*x+y*y) enddef

would be a way to define the ++ operator. (However, ’s built-in ++ is much better, because it won’t overflow when x*x or y*y are out of range.) Exponentiation can be defined by

                         primarydef x**y = mexp(y*mlog x) enddef

but such a definition blows up when x < 0 and y = 2. Here’s a better way:

          primarydef x**y =
            begingroup save t;
            if y=2: t=x*x; % that's the most common case
            elseif x>0: t=mexp(y*mlog x);
            elseif y=floor y: t=1;
              if y>=0: for n=1 step 1 until y: t:=t*x endfor;
              else: for n=-1 step -1 until y: t:=t/x endfor;
              fi;
            else: errmessage("Undefined power " & decimal x & "**" & decimal y);
              t=1;
            fi; t endgroup enddef

The ⟨level indicator⟩ indicates how the arguments should be parsed within expressions. If it is ‘primarydef’, the first argument is parsed as a secondary and the second as a primary; if it is ‘secondarydef’, the first argument is parsed as a tertiary and the second as a secondary; if it is ‘tertiarydef’, the first argument is parsed as an expression and the second as a tertiary.

Here is a way to define the sum of two transforms:

            secondarydef x transum y =
              begingroup save t; transform t;
              (0,1) transformed t = (0,1) transformed x + (0,1) transformed y;
              (1,0) transformed t = (1,0) transformed x + (1,0) transformed y;
              (0,0) transformed t = (0,0) transformed x + (0,0) transformed y;
              t endgroup enddef

A symbolic token whose current meaning was defined in a def or an infix definition is not usable as a ⟨tag⟩ in a variable; it has become a spark. But the symbolic tokens defined by vardef are like variables, except that their suffixes no longer carry values. For example, after making the definitions above, it is obviously impossible to refer to a variable called ‘x.transum’ or ‘good3y’; but ‘x.good’ would be OK.

5. Conditions and loops. also handles a few other things with a macro-like behavior, in the sense that they effectively change the text that reaches ’s “stomach.” The first of these allows you to select between different pieces of program:

⟨conditional⟩ ⟶ if ⟨boolean expression⟩: ⟨conditional text⟩ ⟨alternatives⟩ fi ⟨alternatives⟩ ⟶ ⟨empty⟩ | else: ⟨conditional text⟩

| elseif ⟨boolean expression⟩: ⟨conditional text⟩ ⟨alternatives⟩

Conditionals are expanded whenever macros would be expanded, as in TEX; in particular, conditionals are permitted in the middle of expressions and commands. When you write

if β_{1}: ⟨text_{1}⟩ elseif β_{2}: ⟨text_{2}⟩ elseif β_{3}: ⟨text_{3}⟩ else: ⟨text_{4}⟩ fi

will evaluate β_{1} first; if it’s true, ⟨text_{1}⟩ will be interpreted and everything else from elseif to fi will be ignored. But if β_{1} is false, ⟨text_{1}⟩ will be ignored and β_{2} will be evaluated in a similar fashion. Ultimately if β_{1}, β_{2}, and β_{3} all turn out to be false, ⟨text_{4}⟩ will be read.

The conditional texts inside a conditional construction are arbitrary sequences of tokens that are balanced with respect to if and fi.

There’s also a general iteration facility:

⟨iteration⟩ ⟶ for ⟨symbolic token⟩ ⟨gets⟩ ⟨expression list⟩: ⟨iterated text⟩ endfor | for ⟨symbolic token⟩ ⟨gets⟩ ⟨arithmetic progression⟩: ⟨iterated text⟩ endfor | forsuffixes ⟨symbolic token⟩ ⟨gets⟩ ⟨suffix list⟩: ⟨iterated text⟩ endfor | forever: ⟨iterated text⟩ endfor

⟨gets⟩ ⟶ = | :=

⟨expression list⟩ ⟶ ⟨expression⟩ | ⟨expression list⟩, ⟨expression⟩

⟨suffix list⟩ ⟶ ⟨suffix⟩ | ⟨suffix list⟩, ⟨suffix⟩

⟨arithmetic progression⟩ ⟶ ⟨initial value and step size⟩ until ⟨numeric expression⟩ ⟨initial value and step size⟩ ⟶ ⟨numeric expression⟩ step ⟨numeric expression⟩ ⟨exit clause⟩ ⟶ exitif ⟨boolean expression⟩;

Here ⟨iterated text⟩ is any sequence of tokens that is balanced with respect to unquoted appearances of

for/forsuffixes/forever and endfor delimiters.

These iterative statements have the conventional meaning of similar constructions in other languages, but it is necessary to spell out the rules precisely because each language has certain quirks. The statement

                          forsuffixes s = σ_{1},σ_{2},σ_{3}: ⟨text⟩ endfor

is equivalent to

⟨text(σ_{1})⟩ ⟨text(σ_{2})⟩ ⟨text(σ_{3})⟩

where ⟨text(σ)⟩ means that the value of suffix σ is inserted in place of the token s in the text. It’s just as if s were a suffix parameter to a macro; subscripts in σ_{1}, σ_{2}, and σ_{3} are evaluated before the replacement is done. Similarly

                              for e = ϵ_{1},ϵ_{2},ϵ_{3}: ⟨text⟩ endfor

is equivalent to

⟨text(ϵ_{1})⟩ ⟨text(ϵ_{2})⟩ ⟨text(ϵ_{3})⟩

with e treated as an expr parameter.

Arithmetic progressions are similar but the rules are slightly more fussy. The statement

                          for n = 1 step 2 until 7: ⟨text⟩ endfor

is equivalent to

                              for n = 1,3,5,7: ⟨text⟩ endfor

but

                          for n = 1 step 2 until 0: ⟨text⟩ endfor

skips over the text entirely. In general

                        for n = ν_{1} step ν_{2} until ν_{3}: ⟨text⟩ endfor

causes to evaluate the expressions (once and for all) and then to form an arithmetic progression of values as follows: If ν_{2} > 0 and ν_{1} > ν_{3}, or if ν_{2} < 0 and ν_{1} < ν_{3}, the sequence is empty; otherwise the sequence is ν_{1} followed by the sequence that would be obtained if ν_{1} + ν_{2} were substituted for ν_{1}. (In particular, if ν_{2} = 0 the sequence is ν_{1}, ν_{1}, ν_{1}, … repeated endlessly.)

In all of these instances the iteration index (i.e., the tokens s, e, and n in our examples) are treated as macro parameters inside the ⟨iterated text⟩; they have no connection with similarly named variables elsewhere in the program. For example, if you say

                           n=0; for n=1: m=n; endfor; show m,n

you will find that m=1 and n=0.

Nested iterations like the following are possible:

                for i=1 step 1 until n:
                 for j=i+1 step 1 until n: a[i][j]=a[j][i]; endfor; endfor

But can’t do such things with super efficiency; in this example, it has to scan and redefine and undefine the inner for loop n times.

The ‘forever’ construction iterates the text repeatedly, and you may well wonder how this could possibly be useful. Well, there’s a way to get out of a loop without terminating it normally. When - encounters the construction

exitif ⟨boolean expression⟩;

as it interprets an iterated text, it will conclude the iteration immediately if the boolean expression is true.

There are a few other things that transform program text something like macro expansion. The construction ‘expandafter ⟨token⟩ ⟨token⟩ ’ expands the second token before the first, as in TEX. The construction ‘scantokens ⟨string primary⟩ ’ converts the string primary to a sequence of tokens as if it were a line of characters read from a file.

If encounters the construction ‘input ⟨filename⟩ ’ while reading a file, it will begin to read from the specified file instead (and will resume the previous file later). This operation is permitted only when is scanning a line of characters that have not yet been converted to tokens; otherwise the file name might be garbaged by the tokenizing process. (You can use input inside macros by saying, e.g., ‘scantokens"input ⟨filename⟩ ".) The command ‘endinput’ tells to cease reading a particular file at the end of the current line. As in TEX, the syntax of file names is system dependent, and both of these commands are performed only when they are “expanded” like macros and conditionals.

Here’s a somewhat silly program that illustrates a few of these ideas:

                           Yes=1; No=0;
                           forever: message "Are you happy?  ";
                            result:=scantokens readstring;
                            exitif known result;
                            message "(Please type Yes or No.)";
                           endfor;

The user is assumed to be cooperative; lots of potential responses in this example would get incredibly mixed up. There is a safer alternative:

                           string answer;
                           forever: message "Are you happy?  ";
                            answer:=readstring;
                            exitif answer="Yes";
                            exitif answer="No";
                            message "(Please type Yes or No.)";
                           endfor;

6. Statements and commands. A program is a sequence of statements separated by semicolons and followed by end. More precisely, the syntax

⟨program⟩ ⟶ ⟨statement list⟩ end

⟨statement list⟩ ⟶ ⟨empty⟩ | ⟨statement⟩; ⟨statement list⟩

defines a ⟨program⟩ in terms of a ⟨statement⟩.

But what are statements? Well, they are of various kinds. An “equation” states that two expressions are supposed to be equal. An “assignment” assigns the value of an expression to a variable. A “declaration” states that certain variables will have a certain type. A “definition” defines a macro. A “title” gives a descriptive name to the character that is to follow. And a “command” orders to do some specific operation, immediately.

⟨statement⟩ ⟶ ⟨equation⟩ | ⟨assignment⟩ | ⟨declaration⟩ | ⟨definition⟩ | ⟨title⟩ | ⟨command⟩

| begingroup ⟨statement list⟩ ⟨statement⟩ endgroup

⟨equation⟩ ⟶ ⟨expression⟩ = ⟨right-hand side⟩ ⟨assignment⟩ ⟶ ⟨variable⟩:= ⟨right-hand side⟩ ⟨right-hand side⟩ ⟶ ⟨expression⟩ | ⟨equation⟩ | ⟨assignment⟩ ⟨title⟩ ⟶ ⟨string expression⟩

We have already given the syntax and rules for ⟨declaration⟩ and ⟨definition⟩; the syntax for each of the various kinds of ⟨command⟩ appears below.

Multiple equations and assignments are performed from right to left. For example,

                                  x+1=y:=y+1=z:=1/2x=z+2

first equates .5x to z+2, then assigns this common value to z, then equates z to y+1, then assigns this to y, and finally equates y to x+1. This, of course, is more complicated than anything a person ought to write, but it is still instructive to see what it means. Let’s suppose that x, y, and z have not appeared in any equations or assignments before this point. Then ‘1/2x=z+2’ makes x an independent variable and gives z the value .5x-2. The next assignment wipes this out and gives z the value .5x instead. Then y+1 is equated to z, so y becomes equal to .5x-1; but the subsequent assignment wipes out y’s former value and makes it .5x. Finally, the equation x+1=y becomes x+1=.5x, so x is set to −2; this causes y and z to become equal to −1.

A numeric variable is either “independent” or “dependent” or “known.” Before it receives a value it is independent; later it may be expressed as a linear combination of independent variables (e.g., ‘.5x-2’); and eventually its value ought to become precisely known, so that we can do something with it. Every equation that encounters is transformed into a linear combination of independent variables that is equated to zero. An independent variable whose coefficient is greatest, in absolute value, is chosen to depend on the others; this variable becomes dependent or known. Notice that each equation reduces the number of independent variables by one, unless the equation is redundant (e.g., ‘x=x’) or inconsistent (e.g., ‘0=1’). Thus if you have n independent variables, you should give n equations in order to define their values.

Each component of a pair or transform variable is, similarly, independent or dependent or known. An equation between pair expressions is equivalent to two equations (one for the x part, and one for the y part). Similarly, an equation between transform expressions is equivalent to six equations. won’t complain if one or more of these equations prove to be redundant, although it does report an error if two unequal expressions of type numeric have been equated.

The other five types of variables can appear in equations, but only in simple ways. A boolean, string, pen, picture, or path variable is either “unknown” or “known.” Unknown variables of these types should appear only by themselves on one side or the other of an equation. If, for example, s[] is an array of strings, will be able to deduce from

                    s1=s2; s3=s4; s1=s3; s2="gosh";

that s4 equals "gosh". But will not be able to deduce from the equation "h"&s5="heck" that

s5="eck".

prefers equations to assignments. If you find yourself using assignments a lot, you are probably not getting the best results; you’re still locked into old-style “imperative” programming languages, poor soul.

So much for equations and assignments. A ⟨title⟩ is a string that is simply ignored as if it were a comment, except in two circumstances: If tracingtitles>0, the title is displayed on the user’s terminal when it is encountered. And if proofing>0, the title is written into the output so that it can be displayed as a caption on the proofsheet that follows.

The quantities tracingtitles and proofing, just mentioned, are special internal variables of - , by which it is possible to interact with the system in a variety of ways. Here is a complete list of ’s internal variables, together with a brief indication of their meanings:

tracingtitles  show titles online when they appear
tracingequations show each variable when it becomes known via an equation
tracingcapsules show capsules as well as variables
tracingchoices  show the control points chosen for paths
tracingspecs  show subdivision of paths into octants before digitizing
tracingpens  show details of pens that are made
tracingcommands show commands and operations before they are performed
tracingmacros  show macros before they are expanded
tracingedges  show digitized edges as they are computed
tracingoutput  show digitized edges as they are output
tracingstats  log the memory usage at end of job
tracingonline  show long diagnostics on terminal as well as in the log file
year  the current year (e.g., 1984)
month  the current month (e.g, 3 ≡ March)
day  the current day of the month
time  the number of minutes since midnight when this job started
charcode  the number of the next character to be output
charfam  the class of the next character to be output
charwd  the width of the next character to be output
charht  the height of the next character to be output
chardp  the depth of the next character to be output
charic  the italic correction of the next character to be output
chardx  horizontal device displacement of the next character, in pixels
chardy  vertical device displacement of the next character, in pixels
designsize  the unit of measure used for charwd...charic, in points
hppp  the number of horizontal pixels per point
vppp  the number of vertical pixels per point
pausing  positive to display lines before they are executed
proofing  positive for proof mode, negative if there’s no output at all

fontmaking  positive if font metric output is to be produced
showstopping  positive if show commands are to stop like errors
smoothing  positive if certain glitches are to be removed automatically
autorounding  positive to fix horizontal/vertical curve points; > 1 for diagonals
granularity  the number of pixels per “large” pixel
warningcheck  positive for warnings when variables get large values
turningcheck  positive to reorient clockwise paths; > 1 to flag strange ones

All of these quantities are numeric, and initially zero (except for year, month, day, and time, which are initialized to the time the run began). Plain sets smoothing:=1 and autorounding:=2, because such adjustments are usually desirable. You can use any of these quantities in expressions or on the left-hand side of assignments—they show up under the heading ⟨internal quantity⟩ in the syntax rules above—but when you assign a new value to them it should be numeric and “known.”

All of the remaining statements of are called commands, and they will be listed here in a more or less arbitrary order. Macros and conditionals and such things are not expanded in commands when the next token is being defined or used in some unusual way; but expansion usually happens unless there’s a good reason for not doing so.

• A save command has the syntax

⟨save command⟩ ⟶ save ⟨symbolic token list⟩

⟨symbolic token list⟩ ⟶ ⟨symbolic token⟩ | ⟨symbolic token list⟩, ⟨symbolic token⟩

This tells to put the current meaning of the symbolic tokens into a safe place, and to restore those meanings at the end of the current group. Each token in the list becomes undefined, as if it had never appeared before; in particular, it loses any “primitive” meaning that would have prevented it from being used as a ⟨tag⟩.

• An interim command

interim ⟨internal quantity⟩:= ⟨right-hand side⟩

acts like an ordinary assignment, but the previous value of the internal quantity is saved away, to be restored at the end of the current group.

• The let command let ⟨symbolic token⟩ = ⟨symbolic token⟩

assigns the current meaning of the right-hand token as the current meaning of the left-hand token. For example, after ‘let diamonds=forever’, the token ‘diamonds’ will introduce loops. If the left-hand token was the first token in any variable names, those variables all disappear. If the right-hand token was the first token in any variable names, those variables still have their old names; the left-hand token will act like an undefined variable in that case. (The purpose of let is to redefine primitive meanings, not variable names.)

Note: If the right-hand symbol is one of a pair of matching delimiters, the subsequent behavior of the left-hand symbol is undefined. For example, it’s a bad idea to say ‘let[[=(; let]]=)’.

• The shipout command shipout ⟨picture expression⟩

puts the pixels of positive weight, as defined by the given picture, into a generic font output file, where they will be the image associated with character number charcode+256*charfam. (However, no output is done if proofing<0.) This command also saves the charwd, charht, chardp, charic, and chardw values and associates them with the current charcode number modulo 256. (The charcode, charfam, and chardw values are rounded to integers before shipout uses them.)

• The special commands

special ⟨string expression⟩ | numspecial ⟨numeric expression⟩

specify non-pixel information that is shipped to the generic output file, if proofing is nonnegative. Such special information is unrestricted, although it should follow conventions that are understood by the software that reads the output; such conventions may grow up over the years after has been “frozen,” so that there will always be an easy way to make extensions. Initially the PLAIN base will use special and numspecial to implement some conventions that provide rudimentary proof outputs; for example, the autolabel operation in one of the examples above will expand into a sequence of special and numspecial commands.

• The drawing command does ’s main duty:

⟨drawing command⟩ ⟶ addto ⟨picture variable⟩ also ⟨picture expression⟩ | addto ⟨picture variable⟩ contour ⟨path expression⟩ ⟨with list⟩ | addto ⟨picture variable⟩ doublepath ⟨path expression⟩ ⟨with list⟩

⟨with list⟩ ⟶ ⟨empty⟩ | ⟨with list⟩ ⟨with clause⟩

⟨with clause⟩ ⟶ withpen ⟨pen expression⟩ | withweight ⟨numeric expression⟩

The amount of weight should be −3, −2, −1, 1, 2, or 3. A contour should be a cyclic path; a doublepath is a contour obtained by going from the beginning to the end and then back to the beginning. When a pen is specified, the envelope of the contour is used. The effect of this command is to add the specified weight to each pixel inside the specified contour or envelope. (Complications arise if the path “winds around” some points more than once; such pixels are “colored” more than once. The manual will explain this further.)

• Arrays of pixels can be modified and “standardized” by the cull command, which looks like this:

⟨cull command⟩ ⟶ cull ⟨picture variable⟩ ⟨keep or drop⟩ ⟨pair expression⟩ | ⟨cull command⟩ withweight ⟨numeric expression⟩

    ⟨keep or drop⟩ ⟶ keeping | dropping

Each pixel whose weight is included in or excluded from the given closed interval is replaced by a pixel of the specified weight, and all other pixels get weight zero. For example,

                               cull pic dropping (-4095,0)

zeros out all pixels of negative weight and changes pixels of weight > 2 to weight 1. The interval must be such that pixels of weight zero remain of weight zero.

• The display commands provide online graphic output:

⟨openwindow command⟩ ⟶ openwindow ⟨window number⟩ ⟨screen rectangle⟩ at ⟨pair expression⟩ ⟨window number⟩ ⟶ ⟨numeric expression⟩ ⟨screen rectangle⟩ ⟶ from ⟨pair expression⟩ to ⟨pair expression⟩ ⟨display command⟩ ⟶ display ⟨picture variable⟩ inwindow ⟨window number⟩

A window number must be between 0 and 15. The statement

openwindow k from (r_{0}, c_{0}) to (r_{1}, c_{1}) at (x, y)

associates a rectangular area of the user’s screen with ’s pixels. The (r, c) coordinates are row and column numbers on the screen, considering the top row and left column to be number zero; note that this is quite different from the Cartesian coordinates used elsewhere in . The values will all be rounded to integers. Point (x, y) of ’s raster will be equated to the upper left corner of the rectangle, i.e., to the upper left corner of the pixel in screen column c_{0} of screen row r_{0}. The window itself contains r_{1} − r_{0} rows and c_{1} − c_{0} columns; thus, (r_{1}, c_{1}) is the screen pixel diagonally just southeast of the lower right corner of the window, but it is not in the window itself. A window can be opened any number of times (hence moved to different locations on the screen, if desired), but it must be opened at least once before it is used in a display command. Opening a window blanks the corresponding screen rectangle. The effect of overlapping windows is undefined, because does not always update pixels that have previously been displayed in a window area.

• The protection commands

outer ⟨symbolic token list⟩ | inner ⟨symbolic token list⟩

apply or remove a “protection tag” to the given tokens. If a token tagged outer occurs when is skipping over tokens at high speed, the program will stop and insert an appropriate delimiter, since outer tokens are supposed to occur only at “quiet” times. (Unquiet times occur when is skipping tokens because of a false conditional, or because it is reading the replacement text of a macro or iteration definition, or because it is scanning a text parameter to a macro, or because it is flushing erroneous tokens that were found at the end of a statement.) Without such protection, a missing right delimiter could cause to eat up your whole program before any error was detected; the protections keep such errors localized. Changing the protection tag has no other effect on a token’s meaning.

• The show commands provide diagnostic output:

⟨show command⟩ ⟶ show ⟨expression list⟩ | showvariable ⟨symbolic token list⟩ | showtoken ⟨symbolic token list⟩

              | showstats
              | showdependencies

The first of these displays the value of each expression, in turn. The showvariable command gives the structure of all variables that begin with a given name, together with their values in an abbreviated form; this allows you to see which of its subscripts and attributes have occurred. The showtoken command gives the current meaning of a token (i.e., whether it is a primitive or not). The showstats command tells you how much of ’s internal memory is currently being used. And showdependencies tells you about every dependent variable that is currently not known. In each case pauses to allow subsequent interaction, as if an error had occurred, if showstopping is positive; but the “error message” is simply ‘OK’.

• The mode commands control error recovery interaction; they are the same as in TEX:

                     batchmode | nonstopmode | scrollmode | errorstopmode

• The command ‘randomseed := ⟨numeric expression⟩ ’ initializes the random number generator to a specific value. If you do this you can guarantee consistent results in different runs; otherwise uses day+time/256/256 as the seed value, so you will rarely get the same pseudo-random results twice.

• The token ‘dump’ can be substituted for ‘end’, if a special version of is being used. This stores the macros defined so far, so that they can be loaded as a base file. (It is analogous to TEX’s \dump command.)

• The command ‘everyjob ⟨symbolic token⟩ ’ tells that the designated token should be inserted first, just before the input file is read, when a job starts. (This is meaningful only in a base file that will be loaded at the beginning of a run; it is analogous to TEX’s \everyjob command.)

• The command ‘message ⟨string expression⟩ ’ displays the string on the user’s terminal, beginning with a new line. And ‘errmessage ⟨string expression⟩ ’ displays the string in ’s error message format, then stops as if an error had occurred. And ‘errhelp ⟨string expression⟩ ’ displays the string if the user asks for help on the next errmessage error, unless the string is empty.

• The ‘delimiters ⟨symbolic token⟩ ⟨symbolic token⟩ ’ command has already been described.

• The command ‘newinternal ⟨symbolic token list⟩ ’ defines each symbolic token to act like an internal quantity (i.e., like ‘pausing’, ‘proofing’, etc.); the values of these internal quantities are set to zero initially.

• The font metric commands specify information that will pass to a special output file for font metric data if fontmaking is positive. The syntax of these commands is explained in the appendix. (The idea is to specify information for ligatures, kerns, extensible characters, and character lists, as well as certain esoteric data that goes in the TFM header.)

• Finally, there’s the empty command, which consists of no tokens and causes to do nothing. This may seem useless, but actually it’s very handy: You can always feel safe when you put extra semicolons between statements (e.g., in conditionals or iterations).

Appendix: Font metric commands. A special series of commands is available to specify data that will go in the TFM output file if ‘fontmaking’ is positive. To fully understand these commands you should understand the conventions of font metric files; see, for example, the listing of TEX82 or the TEXware report.

⟨font metric command⟩ ⟶ charlist ⟨byte list⟩ | extensible ⟨byte⟩: ⟨top byte⟩, ⟨mid byte⟩, ⟨bot byte⟩, ⟨rep byte⟩ | headerbyte ⟨numeric expression⟩: ⟨byte list⟩ | fontdimen ⟨numeric expression⟩: ⟨expression list⟩ | ligtable ⟨ligtable program sequence⟩

⟨byte list⟩ ⟶ ⟨byte⟩ | ⟨byte list⟩, ⟨byte⟩

⟨byte⟩ ⟶ ⟨numeric expression⟩ | ⟨string expression⟩

⟨top byte⟩ ⟶ ⟨byte⟩ ⟨mid byte⟩ ⟶ ⟨byte⟩ ⟨bot byte⟩ ⟶ ⟨byte⟩ ⟨rep byte⟩ ⟶ ⟨byte⟩ ⟨ligtable program sequence⟩ ⟶ ⟨ligtable step⟩

| ⟨ligtable program sequence⟩, ⟨ligtable step⟩

⟨ligtable step⟩ ⟶ ⟨ligature replacement⟩ | ⟨kern specification⟩

| ⟨byte⟩: ⟨ligtable step⟩ ⟨ligature replacement⟩ ⟶ ⟨byte⟩ =: ⟨byte⟩ ⟨kern specification⟩ ⟶ ⟨byte⟩ kern ⟨numeric expression⟩

A ⟨byte⟩ specifies an eight-bit number; it is either a numeric expression—which should have a known value between 0 and 255, inclusive, when rounded to the nearest integer—or it is a string expression for a string of length 1. In the latter case it denotes the ASCII code of that string character. For example, ‘"A"’ and ‘64.61’ both specify the byte value 65.

Several characters of a font can be linked together in a series by means of the charlist command. For example,

                charlist oct"000", oct"020", oct"022", oct"040", oct"060"

is used in the font amathx to specify the left parentheses that TEX uses in displayed math formulas, in increasing order of size. (Font amathx is shown on page 432 of The TEXbook.)

An extensible character is specified by giving top, middle, bottom, and repeatable characters that TEX can string together to make arbitrarily large delimiters. For example, the extensible left parentheses in amathx are defined by

                  extensible oct"060": oct"060", 0, oct"100", oct"102";

this says that character code ´ 060 specifies an extensible delimiter constructed from character number ´ 060 as the top piece, character number ´ 100 as the bottom piece, and character number ´ 102 as the piece that should be repeated as often as necessary to reach a desired size. (In this particular example there is no “middle” piece, but characters like braces have a middle piece as well.)

Numeric parameters of a font are specified by saying, e.g.,

                               fontdimen 3: 2.5, 6.5, 0, 4x

which says that parameters 3–6 are to be 2.5, 6.5, zero, and 4x, respectively. These are the parameters that TEX calls \fontdimen3 through \fontdimen6. Byte-valued parameters in the TFM header are specified in a similar way:

                              headerbyte 33: 0, 214, 0, "c"

says that bytes 33–36 of the header (which constitute the ninth word, since there are four bytes per word) will be 0, 214, 0, and 99. This can be used for various hacks; for example, it’s possible to override the normal check sum by specifying values of the first four header bytes. Here is a conventional way to put the name of a font family into the TFM header:

              def BCPLstring(expr s,max)=
                for l:=if length(s)>max: max else: length(s) fi: l
                for n:=1 step 1 until l: , substring (n-1,n) of s endfor
                for n:=l+1 step 1 until max: , 0 endfor endfor enddef;
              def fontfamily expr s=headerbyte 49: BCPLstring(s,19) enddef;
              def codingscheme expr s=headerbyte 9: BCPLstring(s,39) enddef;
              fontfamily "HELVETICA"; codingscheme "XEROX TEXT";

Ligatures and kerns are specified by little “programs” like the following:

            ligtable "f": "i" =: oct"200", "f" =: oct"201", "/": ")" kern 1.5;
            ligtable oct"201": "i" =: oct "202";

Paraphrased, this cryptic code means: If character ‘f’ is followed by ‘i’, replace those two characters by number ´ 200 ; if ‘f’ is followed by another ‘f’, replace them both by character number ´ 201 ; if ‘f’ or ‘/’ is followed by a right parenthesis, insert 1.5 points of space between them; and if ´ 201 is followed by ‘i’, replace those two characters by ´ 202 (which presumably is the ligature ‘ffi’).

A character code should not appear more than once in the following places: (1) As one of the bytes of a charlist that is followed by a comma; (2) as the byte in an extensible recipe that is followed by a colon; (3) as a byte in a ligtable that is followed by a colon. Each of these usages is mutually exclusive; for example, only the final character of a charlist can be extensible or form a ligature.

Index to primitive tokens.

" 1
#@ 9
% 1
& 5
* 7

+ 6, 7

++, +-+ 7

, 7, 8, 12, 16, 20

- 6, 7

. 1
.. 5
/ 7

: 12, 20 := 12, 14, 15, 16 ; 12, 14

< 4
<= 4
<> 4

= 4, 8, 11, 14, 17

=: 20
> 4
>= 4
@ 9

@# 8, 9 [ 2, 3, 7

\ 8

] 2, 3, 7

{ 5
} 5
addto 17
also 17
and 4, 5
angle 6
ASCII 6
at 18
atleast 5
autorounding 16
batchmode 18
begingroup 3, 9, 14
boolean 3
char 5
charcode 15
chardp 15
chardx 15
chardy 15
charexists 4
charfam 15
charht 15
charic 15
charlist 20
charwd 15
contour 17
controls 5

                      cosd 7
                      cull 17
                      curl 5
                      cycle 4
                      day 15
                      decimal 5
                      def 8
                      delimiters 3
                      designsize 15
                      directiontime 6
                      display 17
                      doublepath 17
                      dropping 17
                      dump 19
                      else 12
                      elseif 12
                      end 14
                      enddef 8, 11
                      endfor 12
                      endgroup 3, 9, 14
                      endinput 13
                      errhelp 19
                      errmessage 19
                      errorstopmode 18
                      everyjob 19
                      exitif 12
                      expandafter 13
                      expr 8, 9
                      extensible 20
                      false 4
                      fi 12
                      floor 7
                      fontdimen 20
                      fontmaking 16
                      for 12
                      forever 12
                      forsuffixes 12
                      from 17
                      granularity 16
                      headerbyte 20
                      hex 6
                      hppp 15
                      if 12
                      inner 18
                      input 13
                      interim 16
                      intersectiontimes 7
                      inwindow 17
                      keeping 17
                      kern 20
                      known 4
                      jobname 5
                      length 6

                                              let 16
                                              ligtable 20
                                              makepath 5
                                              makepen 6
                                              message 19
                                              mexp 7
                                              mlog 7
                                              month 15
                                              newinternal 19
                                              nonstopmode 18
                                              normaldeviate 6
                                              not 4
                                              nullpen 6
                                              nullpicture 5
                                              numeric 3
                                              numspecial 16
                                              oct 6
                                              odd 4

of 5, 6, 7, 10

                                              openwindow 17
                                              or 4
                                              outer 18
                                              pair 3
                                              path 3
                                              pausing 15
                                              pen 3
                                              pencircle 6
                                              penoffset 7
                                              picture 3
                                              point 7
                                              postcontrol 7
                                              precontrol 7
                                              primary 9
                                              primarydef 8, 11
                                              proofing 15
                                              quote 8
                                              randomseed 19
                                              readstring 5
                                              reverse 5
                                              rotated 6
                                              save 16
                                              scaled 6
                                              scantokens 13
                                              scrollmode 19
                                              secondary 9
                                              secondarydef 8, 11
                                              shifted 6
                                              shipout 16
                                              show 18
                                              showdependencies 18
                                              showstats 18
                                              showstopping 16
                                              showtoken 18

                                                                      showvariable 18
                                                                      sind 7
                                                                      slanted 6
                                                                      smoothing 16
                                                                      special 16
                                                                      sqrt 7
                                                                      step 12
                                                                      str 5
                                                                      string 3
                                                                      subpath 5
                                                                      substring 5
                                                                      suffix 8
                                                                      tension 5
                                                                      tertiary 9
                                                                      tertiarydef 8, 11
                                                                      text 8
                                                                      time 15
                                                                      to 17
                                                                      totalweight 6
                                                                      tracingcapsules 15
                                                                      tracingchoices 15
                                                                      tracingcommands 15
                                                                      tracingedges 15
                                                                      tracingequations 15
                                                                      tracingmacros 15
                                                                      tracingonline 15
                                                                      tracingoutput 15
                                                                      tracingpens 15
                                                                      tracingspecs 15
                                                                      tracingstats 15
                                                                      tracingtitles 15
                                                                      transform 3
                                                                      transformed 6
                                                                      true 4
                                                                      turningcheck 16
                                                                      turningnumber 6
                                                                      uniformdeviate 7
                                                                      until 12
                                                                      vardef 8
                                                                      vppp 15
                                                                      warningcheck 16
                                                                      withpen 17
                                                                      withweight 17
                                                                      xpart 6
                                                                      xscaled 6
                                                                      xxpart 6
                                                                      xypart 6
                                                                      year 15
                                                                      ypart 6
                                                                      yscaled 6
                                                                      yxpart 6
                                                                      yypart 6
                                                                      zscaled 6