§487  TEX82  PART 28: CONDITIONAL PROCESSING 181

487. Conditional processing. We consider now the way TEX handles various kinds of commands. define if char code = 0 { ‘ ’ } define if cat code = 1 { ‘ ’ } define if int code = 2 { ‘ ’ } define if dim code = 3 { ‘ ’ } define if odd code = 4 { ‘ ’ } define if vmode code = 5 { ‘ ’ } define if hmode code = 6 { ‘ ’ } define if mmode code = 7 { ‘ ’ } define if inner code = 8 { ‘ ’ } define if void code = 9 { ‘ ’ } define if hbox code = 10 { ‘ ’ } define if vbox code = 11 { ‘ ’ } define ifx code = 12 { ‘ ’ } define if eof code = 13 { ‘ ’ } define if true code = 14 { ‘ ’ } define if false code = 15 { ‘ ’ } define if case code = 16 { ‘ ’ }

⟨ Put each of TEX’s primitives into the hash table 226 ⟩ +≡

primitive(  , if test , if char code); primitive(  , if test , if cat code);
primitive(  , if test , if int code); primitive(  , if test , if dim code);
primitive(  , if test , if odd code); primitive(  , if test , if vmode code);
primitive(  , if test , if hmode code); primitive(  , if test , if mmode code);
primitive(  , if test , if inner code); primitive(  , if test , if void code);
primitive(  , if test , if hbox code); primitive(  , if test , if vbox code);
primitive(  , if test , ifx code); primitive(  , if test , if eof code);
primitive(  , if test , if true code); primitive(  , if test , if false code);

primitive( , if test , if case code);

488. ⟨ Cases of print cmd chr for symbolic printing of primitives 227 ⟩ +≡ if test : case chr code of

182  PART 28: CONDITIONAL PROCESSING  TEX82 §489

if cat code: print esc( ); if int code: print esc( ); if dim code: print esc( ); if odd code: print esc( ); if vmode code: print esc( ); if hmode code: print esc( ); if mmode code: print esc( ); if inner code: print esc( ); if void code: print esc( ); if hbox code: print esc( ); if vbox code: print esc( ); ifx code: print esc( ); if eof code: print esc( ); if true code: print esc( ); if false code: print esc( ); if case code: print esc( ); othercases print esc( ) endcases; 489. Conditions can be inside conditions, and this nesting has a stack that is independent of the save stack .

Four global variables represent the top of the condition stack: cond ptr points to pushed-down entries, if any; if limit specifies the largest code of a fi or else command that is syntactically legal; cur if is the name of the current type of conditional; and if line is the line number at which it began.

If no conditions are currently in progress, the condition stack has the special state cond ptr = null , if limit = normal , cur if = 0, if line = 0. Otherwise cond ptr points to a two-word node; the type, subtype, and link fields of the first word contain if limit , cur if , and cond ptr at the next level, and the second word contains the corresponding if line.

define if node size = 2 { number of words in stack entry for conditionals }

define if line field (#) ≡ mem[# + 1].int

define if code = 1 { code for being evaluated }

define fi code = 2 { code for }

define else code = 3 { code for }

define or code = 4 { code for } ⟨ Global variables 13 ⟩ +≡ cond ptr : pointer ; { top of the condition stack } if limit : normal . . or code; { upper bound on fi or else codes } cur if : small number ; { type of conditional being worked on } if line: integer ; { line where that conditional began }

490. ⟨ Set initial values of key variables 21 ⟩ +≡ cond ptr ← null ; if limit ← normal ; cur if ← 0; if line ← 0;

491. ⟨ Put each of TEX’s primitives into the hash table 226 ⟩ +≡

primitive(  , fi or else, fi code); text (frozen fi ) ←  ; eqtb[frozen fi ] ← eqtb[cur val ];
primitive(  , fi or else, or code); primitive(  , fi or else, else code);

492. ⟨ Cases of print cmd chr for symbolic printing of primitives 227 ⟩ +≡ fi or else: if chr code = fi code then print esc( )

else if chr code = or code then print esc( )

else print esc( );

493. When we skip conditional text, we keep track of the line number where skipping began, for use in error messages. ⟨ Global variables 13 ⟩ +≡ skip line: integer ; { skipping began here }

494. Here is a procedure that ignores text until coming to an ,  , or  at level zero of  …

nesting. After it has acted, cur chr will indicate the token that was found, but cur tok will not be set (because this makes the procedure run faster). procedure pass text ;

label done; var l: integer ; { level of … nesting }

save scanner status : small number ; { scanner status upon entry } begin save scanner status ← scanner status ; scanner status ← skipping ; l ← 0; skip line ← line; loop begin get next ;

if cur cmd = fi or else then begin if l = 0 then goto done; if cur chr = fi code then decr (l); end

else if cur cmd = if test then incr (l);

end; done: scanner status ← save scanner status ;

end;

495. When we begin to process a new , we set if limit ← if code; then if  or  or  occurs
before the current  condition has been evaluated,  will be inserted. For example, a sequence of

commands like ‘ ’ would otherwise require something after the ‘ ’. ⟨ Push the condition stack 495 ⟩ ≡

begin p ← get node(if node size); link (p) ← cond ptr ; type(p) ← if limit ; subtype(p) ← cur if ; if line field (p) ← if line; cond ptr ← p; cur if ← cur chr ; if limit ← if code; if line ← line; end

This code is used in section 498.

496. ⟨ Pop the condition stack 496 ⟩ ≡ begin p ← cond ptr ; if line ← if line field (p); cur if ← subtype(p); if limit ← type(p); cond ptr ← link (p); free node(p, if node size); end

This code is used in sections 498, 500, 509, and 510.

497. Here’s a procedure that changes the if limit code corresponding to a given value of cond ptr . procedure change if limit (l : small number ; p : pointer );

label exit ; var q: pointer ; begin if p = cond ptr then if limit ← l { that’s the easy case } else begin q ← cond ptr ;

loop begin if q = null then confusion( );

if link (q) = p then begin type(q) ← l; return; end; q ← link (q); end;

end; exit : end; 498. A condition is started when the expand procedure encounters an if test command; in that case expand reduces to conditional , which is a recursive procedure. procedure conditional ;

label exit , common ending ; var b: boolean; { is the condition true? }

r: . . ; { relation to be evaluated } m, n: integer ; { to be tested against the second operand } p, q: pointer ; { for traversing token lists in tests } save scanner status : small number ; { scanner status upon entry } save cond ptr : pointer ; { cond ptr corresponding to this conditional } this if : small number ; { type of this conditional }

begin ⟨ Push the condition stack 495 ⟩; save cond ptr ← cond ptr ; this if ← cur chr ; ⟨ Either process or set b to the value of a boolean condition 501 ⟩; if tracing commands > 1 then ⟨ Display the value of b 502 ⟩;

if b then

begin change if limit (else code, save cond ptr ); return; { wait for or } end;

⟨ Skip to or , then goto common ending 500 ⟩; common ending : if cur chr = fi code then ⟨ Pop the condition stack 496 ⟩

else if limit ← fi code; { wait for } exit : end;

499. In a construction like ‘  ’, the first  that we come to after learning
that the  is false is not the  we’re looking for. Hence the following curious logic is needed.

500. ⟨ Skip to or , then goto common ending 500 ⟩ ≡

loop begin pass text ;

if cond ptr = save cond ptr then begin if cur chr ≠ or code then goto common ending ; print err ( ␣ ); print esc( ); help1 ( ´ ␣ ␣ ␣ ␣ ´ ␣ ␣ ␣ ); error ; end

else if cur chr = fi code then ⟨ Pop the condition stack 496 ⟩; end

This code is used in section 498.

501. ⟨ Either process or set b to the value of a boolean condition 501 ⟩ ≡

case this if of

if char code, if cat code: ⟨ Test if two characters match 506 ⟩; if int code, if dim code: ⟨ Test relation between integers or dimensions 503 ⟩; if odd code: ⟨ Test if an integer is odd 504 ⟩; if vmode code: b ← (abs (mode) = vmode); if hmode code: b ← (abs (mode) = hmode); if mmode code: b ← (abs (mode) = mmode); if inner code: b ← (mode < 0); if void code, if hbox code, if vbox code: ⟨ Test box register status 505 ⟩; ifx code: ⟨ Test if two tokens match 507 ⟩; if eof code: begin scan four bit int ; b ← (read open[cur val ] = closed );

end; if true code: b ← true; if false code: b ← false; if case code: ⟨ Select the appropriate case and return or goto common ending 509 ⟩; end { there are no other cases }

This code is used in section 498.

502. ⟨ Display the value of b 502 ⟩ ≡ begin begin diagnostic; if b then print ( ) else print ( ); end diagnostic(false); end

This code is used in section 498.

503. Here we use the fact that , , and are consecutive ASCII codes. ⟨ Test relation between integers or dimensions 503 ⟩ ≡

begin if this if = if int code then scan int else scan normal dimen; n ← cur val ; ⟨ Get the next non-blank non-call token 406 ⟩; if (cur tok ≥ other token + ) ∧ (cur tok ≤ other token + ) then r ← cur tok − other token else begin print err ( ␣ ␣ ␣ ␣ ); print cmd chr (if test , this if );

help1 ( ␣ ␣ ␣ ␣ ␣` ´ ␣` ´ ␣ ␣` ´ ␣ ´ ); back error ; r ← ; end;

if this if = if int code then scan int else scan normal dimen;

case r of

: b ← (n < cur val ); : b ← (n = cur val ); : b ← (n > cur val ); end; end

This code is used in section 501.

504. ⟨ Test if an integer is odd 504 ⟩ ≡ begin scan int ; b ← odd (cur val ); end

This code is used in section 501.

505. ⟨ Test box register status 505 ⟩ ≡ begin scan eight bit int ; p ← box (cur val ); if this if = if void code then b ← (p = null ) else if p = null then b ← false

else if this if = if hbox code then b ← (type(p) = hlist node)

else b ← (type(p) = vlist node); end

This code is used in section 501.

506. An active character will be treated as category 13 following  or following  .

We use the fact that active characters have the smallest tokens, among all control sequences.

define get x token or active char ≡ begin get x token; if cur cmd = relax then

if cur chr = no expand flag then begin cur cmd ← active char ; cur chr ← cur tok − cs token flag − active base; end;

end

⟨ Test if two characters match 506 ⟩ ≡ begin get x token or active char ; if (cur cmd > active char ) ∨ (cur chr > 255) then { not a character }

begin m ← relax ; n ← 256;

end else begin m ← cur cmd ; n ← cur chr ;

end; get x token or active char ; if (cur cmd > active char ) ∨ (cur chr > 255) then

begin cur cmd ← relax ; cur chr ← 256;

end; if this if = if char code then b ← (n = cur chr ) else b ← (m = cur cmd ); end

This code is used in section 501.

507. Note that ‘ ’ will declare two macros different if one is long or outer and the other isn’t, even though the texts of the macros are the same.

We need to reset scanner status , since control sequences are allowed, but we might be scanning a macro definition or preamble. ⟨ Test if two tokens match 507 ⟩ ≡

begin save scanner status ← scanner status ; scanner status ← normal ; get next ; n ← cur cs ; p ← cur cmd ; q ← cur chr ; get next ; if cur cmd ≠ p then b ← false else if cur cmd < call then b ← (cur chr = q)

else ⟨ Test if two macro texts match 508 ⟩; scanner status ← save scanner status ; end

This code is used in section 501.

508. Note also that ‘ ’ decides that macros and are different in examples like this:

⟨ Test if two macro texts match 508 ⟩ ≡ begin p ← link (cur chr ); q ← link (equiv (n)); { omit reference counts } if p = q then b ← true else begin while (p ≠ null ) ∧ (q ≠ null ) do

if info(p) ≠ info(q) then p ← null

else begin p ← link (p); q ← link (q);

end; b ← ((p = null ) ∧ (q = null )); end;

end

This code is used in section 507.

509. ⟨ Select the appropriate case and return or goto common ending 509 ⟩ ≡ begin scan int ; n ← cur val ; { n is the number of cases to pass } if tracing commands > 1 then

begin begin diagnostic; print ( ␣ ); print int (n); print char ( ); end diagnostic(false); end;

while n ≠ 0 do begin pass text ; if cond ptr = save cond ptr then

if cur chr = or code then decr (n)

else goto common ending

else if cur chr = fi code then ⟨ Pop the condition stack 496 ⟩; end;

change if limit (or code, save cond ptr ); return; { wait for , , or } end

This code is used in section 501.

510. The processing of conditionals is complete except for the following code, which is actually part of expand . It comes into play when , , or is scanned. ⟨ Terminate the current conditional and skip to 510 ⟩ ≡

if cur chr > if limit then if if limit = if code then insert relax { condition not yet evaluated } else begin print err ( ␣ ); print cmd chr (fi or else, cur chr );

help1 ( ´ ␣ ␣ ␣ ␣ ´ ␣ ␣ ␣ ); error ; end

else begin while cur chr ≠ fi code do pass text ; { skip to } ⟨ Pop the condition stack 496 ⟩; end

This code is used in section 367.