§321  TEX82  PART 23: MAINTAINING THE INPUT STACKS 131

321. Maintaining the input stacks. The following subroutines change the input status in commonly needed ways.

First comes push input , which stores the current state and creates a new level (having, initially, the same properties as the old).

define push input ≡ { enter a new input level, save the old }

begin if input ptr > max in stack then begin max in stack ← input ptr ; if input ptr = stack size then overflow ( ␣ ␣ , stack size); end;

input stack [input ptr ] ← cur input ; { stack the record } incr (input ptr ); end

322. And of course what goes up must come down.

define pop input ≡ { leave an input level, re-enter the old } begin decr (input ptr ); cur input ← input stack [input ptr ]; end

323. Here is a procedure that starts a new level of token-list input, given a token list p and its type t. If t = macro, the calling routine should set name and loc.

define back list (#) ≡ begin token list (#, backed up) { backs up a simple token list } define ins list (#) ≡ begin token list (#, inserted ) { inserts a simple token list }

procedure begin token list (p : pointer ; t : quarterword ); begin push input ; state ← token list ; start ← p; token type ← t; if t ≥ macro then { the token list starts with a reference count }

begin add token ref (p); if t = macro then param start ← param ptr else begin loc ← link (p);

if tracing macros > 1 then begin begin diagnostic; print nl ( );

case t of

mark text : print esc( ); write text : print esc( ); othercases print cmd chr (assign toks , t − output text + output routine loc) endcases; print ( ); token show (p); end diagnostic(false); end;

end; end

else loc ← p; end;

132  PART 23: MAINTAINING THE INPUT STACKS  TEX82 §324

324. When a token list has been fully scanned, the following computations should be done as we leave that level of input. The token type tends to be equal to either backed up or inserted about 2/3 of the time. procedure end token list ; { leave a token-list input level }

begin if token type ≥ backed up then { token list to be deleted } begin if token type ≤ inserted then flush list (start ) else begin delete token ref (start ); { update reference count }

if token type = macro then { parameters must be flushed }

while param ptr > param start do begin decr (param ptr ); flush list (param stack [param ptr ]); end;

end; end

else if token type = u template then if align state > 500000 then align state ← 0 else fatal error ( ␣ ␣ ␣ ␣ ␣ );

pop input ; check interrupt ; end;

325. Sometimes TEX has read too far and wants to “unscan” what it has seen. The back input procedure takes care of this by putting the token just scanned back into the input stream, ready to be read again. This procedure can be used only if cur tok represents the token to be replaced. Some applications of TEX use this procedure a lot, so it has been slightly optimized for speed. procedure back input ; { undoes one token of input }

var p: pointer ; { a token list of length one } begin while (state = token list ) ∧ (loc = null ) do end token list ; { conserve stack space } p ← get avail ; info(p) ← cur tok ; if cur tok < right brace limit then

if cur tok < left brace limit then decr (align state)

else incr (align state); push input ; state ← token list ; start ← p; token type ← backed up; loc ← p;

{ that was back list (p), without procedure overhead } end;

326. ⟨ Insert token p into TEX’s input 326 ⟩ ≡ begin t ← cur tok ; cur tok ← p; back input ; cur tok ← t; end

This code is used in section 282.

327. The back error routine is used when we want to replace an offending token just before issuing an error message. This routine, like back input , requires that cur tok has been set. We disable interrupts during the call of back input so that the help message won’t be lost. procedure back error ; { back up one token and call error }

begin OK to interrupt ← false; back input ; OK to interrupt ← true; error ; end;

procedure ins error ; { back up one inserted token and call error } begin OK to interrupt ← false; back input ; token type ← inserted ; OK to interrupt ← true; error ; end; 328. The begin file reading procedure starts a new level of input for lines of characters to be read from a file, or as an insertion from the terminal. It does not take care of opening the file, nor does it set loc or limit or line. procedure begin file reading ;

begin if in open = max in open then overflow (  ␣     ␣  , max in open);
if first = buf size then overflow (  ␣  , buf size);

incr (in open); push input ; index ← in open; line stack [index ] ← line; start ← first ; state ← mid line; name ← 0; { terminal input is now true } end;

329. Conversely, the variables must be downdated when such a level of input is finished: procedure end file reading ;

begin first ← start ; line ← line stack [index ]; if name > 17 then a close(cur file); { forget it } pop input ; decr (in open); end;

330. In order to keep the stack from overflowing during a long sequence of inserted ‘ ’ commands, the following routine removes completed error-inserted lines from memory. procedure clear for error prompt ;

begin while (state ≠ token list ) ∧ terminal input ∧ (input ptr > 0) ∧ (loc > limit ) do end file reading ; print ln; clear terminal ; end;

331. To get TEX’s whole input mechanism going, we perform the following actions. ⟨ Initialize the input routines 331 ⟩ ≡

begin input ptr ← 0; max in stack ← 0; in open ← 0; open parens ← 0; max buf stack ← 0; param ptr ← 0; max param stack ← 0; first ← buf size; repeat buffer [first ] ← 0; decr (first ); until first = 0; scanner status ← normal ; warning index ← null ; first ← 1; state ← new line; start ← 1; index ← 0; line ← 0; name ← 0; force eof ← false; align state ← 1000000; if ¬init terminal then goto final end ; limit ← last ; first ← last + 1; { init terminal has set loc and last } end

This code is used in section 1337.