§402  TEX82  PART 26: BASIC SCANNING SUBROUTINES 155

402. Basic scanning subroutines. Let’s turn now to some procedures that TEX calls upon frequently to digest certain kinds of patterns in the input. Most of these are quite simple; some are quite elaborate. Almost all of the routines call get x token, which can cause them to be invoked recursively.

403. The scan left brace routine is called when a left brace is supposed to be the next non-blank token. (The term “left brace” means, more precisely, a character whose catcode is left brace.) TEX allows to appear before the left brace. procedure scan left brace; { reads a mandatory left brace }

begin ⟨ Get the next non-blank non-relax non-call token 404 ⟩; if cur cmd ≠ left brace then

begin print err (  ␣ ␣  );

    help4 ( ␣    ␣     ␣   ␣         ␣     ␣  ␣ ´ ␣   ␣   ␣  )
    (  ␣     ␣    ␣  ␣      ␣      ␣      ␣    ␣  )
    ( ␣    ␣ ␣    ␣    ␣ ␣        ␣     ␣     ␣  )

(  ␣   ´ ␣        ␣  ␣   ␣     ␣   ␣      ␣`   ´ ␣  ); back error ;

cur tok ← left brace token + ; cur cmd ← left brace; cur chr ← ; incr (align state); end;

end;

404. ⟨ Get the next non-blank non-relax non-call token 404 ⟩ ≡ repeat get x token; until (cur cmd ≠ spacer ) ∧ (cur cmd ≠ relax )

This code is used in sections 403, 1078, 1084, 1151, 1160, 1211, 1226, and 1270.

405. The scan optional equals routine looks for an optional ‘ ’ sign preceded by optional spaces; ‘ ’ is not ignored here. procedure scan optional equals ;

begin ⟨ Get the next non-blank non-call token 406 ⟩;

if cur tok ≠ other token + then back input ;

end;

406. ⟨ Get the next non-blank non-call token 406 ⟩ ≡ repeat get x token; until cur cmd ≠ spacer

This code is used in sections 405, 441, 455, 503, 526, 577, 785, 791, and 1045.

156  PART 26: BASIC SCANNING SUBROUTINES  TEX82 §407

407. In case you are getting bored, here is a slightly less trivial routine: Given a string of lowercase letters, like ‘ ’ or ‘ ’ or ‘ ’, the scan keyword routine checks to see whether the next tokens of input match this string. The match must be exact, except that uppercase letters will match their lowercase counterparts; uppercase equivalents are determined by subtracting − , rather than using the uc code table, since TEX uses this routine only for its own limited set of keywords.

If a match is found, the characters are effectively removed from the input and true is returned. Otherwise false is returned, and the input is left essentially unchanged (except for the fact that some macros may have been expanded, etc.). function scan keyword (s : str number ): boolean; { look for a given string }

label exit ;

var p: pointer ; { tail of the backup list } q: pointer ; { new node being added to the token list via store new token } k: pool pointer ; { index into str pool }

begin p ← backup head ; link (p) ← null ; k ← str start [s];

while k < str start [s + 1] do begin get x token; { recursion is possible here } if (cur cs = 0) ∧ ((cur chr = so(str pool [k])) ∨ (cur chr = so(str pool [k]) − + )) then

begin store new token(cur tok ); incr (k); end

else if (cur cmd ≠ spacer ) ∨ (p ≠ backup head ) then begin back input ; if p ≠ backup head then back list (link (backup head )); scan keyword ← false; return; end;

end; flush list (link (backup head )); scan keyword ← true;

exit : end;

408. Here is a procedure that sounds an alarm when mu and non-mu units are being switched. procedure mu error ;

begin print err ( ␣ ␣ );

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

end;

409. The next routine ‘scan something internal ’ is used to fetch internal numeric quantities like ‘ ’, and also to handle the ‘ ’ when expanding constructions like ‘ ’ and ‘ ’. Soon we will be considering the scan int procedure, which calls scan something internal ; on the other hand, scan something internal also calls scan int , for constructions like ‘ ` $ ’ or ‘ ’. So we have to declare scan int as a forward procedure. A few other procedures are also declared at this point. procedure scan int ; forward ; { scans an integer value } ⟨ Declare procedures that scan restricted classes of integers 433 ⟩ ⟨ Declare procedures that scan font-related stuff 577 ⟩ 410. TEX doesn’t know exactly what to expect when scan something internal begins. For example, an integer or dimension or glue value could occur immediately after ‘ ’; and one can even say with respect to token lists in constructions like ‘ ’. On the other hand, only integers are allowed after a construction like ‘ ’. To handle the various possibilities, scan something internal has a level parameter, which tells the “highest” kind of quantity that scan something internal is allowed to produce. Six levels are distinguished, namely int val , dimen val , glue val , mu val , ident val , and tok val .

The output of scan something internal (and of the other routines scan int , scan dimen, and scan glue below) is put into the global variable cur val , and its level is put into cur val level . The highest values of cur val level are special: mu val is used only when cur val points to something in a “muskip” register, or to one of the three parameters , , ; ident val is used only when cur val points to a font identifier; tok val is used only when cur val points to null or to the reference count of a token list. The last two cases are allowed only when scan something internal is called with level = tok val .

If the output is glue, cur val will point to a glue specification, and the reference count of that glue will have been updated to reflect this reference; if the output is a nonempty token list, cur val will point to its reference count, but in this case the count will not have been updated. Otherwise cur val will contain the integer or scaled value in question.

define int val = 0 { integer values }

define dimen val = 1 { dimension values }

define glue val = 2 { glue specifications }

define mu val = 3 { math glue specifications }

define ident val = 4 { font identifier }

define tok val = 5 { token lists } ⟨ Global variables 13 ⟩ +≡ cur val : integer ; { value returned by numeric scanners } cur val level : int val . . tok val ; { the “level” of this value }

411. The hash table is initialized with ‘  ’, ‘  ’, ‘  ’, and ‘  ’ all having register as

their command code; they are distinguished by the chr code, which is either int val , dimen val , glue val , or mu val . ⟨ Put each of TEX’s primitives into the hash table 226 ⟩ +≡

primitive(  , register , int val ); primitive(  , register , dimen val );
primitive(  , register , glue val ); primitive(  , register , mu val );

412. ⟨ Cases of print cmd chr for symbolic printing of primitives 227 ⟩ +≡ register : if chr code = int val then print esc( )

else if chr code = dimen val then print esc( )

else if chr code = glue val then print esc( ) else print esc( ); 413. OK, we’re ready for scan something internal itself. A second parameter, negative, is set true if the value that is found should be negated. It is assumed that cur cmd and cur chr represent the first token of the internal quantity to be scanned; an error will be signalled if cur cmd < min internal or cur cmd > max internal .

define scanned result end (#) ≡ cur val level ← #; end define scanned result (#) ≡ begin cur val ← #; scanned result end

procedure scan something internal (level : small number ; negative : boolean);

{ fetch an internal parameter } var m: halfword ; { chr code part of the operand token }

p: 0 . . nest size; { index into nest } begin m ← cur chr ;

case cur cmd of

def code: ⟨ Fetch a character code from some table 414 ⟩; toks register , assign toks , def family , set font , def font : ⟨ Fetch a token list or font identifier, provided

that level = tok val 415 ⟩; assign int : scanned result (eqtb[m].int )(int val ); assign dimen: scanned result (eqtb[m].sc)(dimen val ); assign glue: scanned result (equiv (m))(glue val ); assign mu glue: scanned result (equiv (m))(mu val ); set aux : ⟨ Fetch the space factor or the prev depth 418 ⟩; set prev graf : ⟨ Fetch the prev graf 422 ⟩; set page int : ⟨ Fetch the dead cycles or the insert penalties 419 ⟩; set page dimen: ⟨ Fetch something on the page so far 421 ⟩; set shape: ⟨ Fetch the par shape size 423 ⟩; set box dimen: ⟨ Fetch a box dimension 420 ⟩; char given, math given: scanned result (cur chr )(int val ); assign font dimen: ⟨ Fetch a font dimension 425 ⟩; assign font int : ⟨ Fetch a font integer 426 ⟩; register : ⟨ Fetch a register 427 ⟩; last item: ⟨ Fetch an item in the current node, if appropriate 424 ⟩; othercases ⟨ Complain that can’t do this; give zero result 428 ⟩ endcases; while cur val level > level do ⟨ Convert cur val to a lower level 429 ⟩; ⟨ Fix the reference count, if any, and negate cur val if negative 430 ⟩; end;

414. ⟨ Fetch a character code from some table 414 ⟩ ≡ begin scan char num; if m = math code base then scanned result (ho(math code(cur val )))(int val ) else if m < math code base then scanned result (equiv (m + cur val ))(int val )

else scanned result (eqtb[m + cur val ].int )(int val ); end

This code is used in section 413.

415. ⟨ Fetch a token list or font identifier, provided that level = tok val 415 ⟩ ≡

if level ≠ tok val then

begin print err (  ␣       ␣       ␣  ␣  );

  help3 ( ␣      ␣      ␣    ␣    ␣     ␣ ␣        ␣` ´  )
  (  ␣   ␣   ´ ␣      ␣   ␣   ␣ ␣      ␣  ␣   ␣ ␣  )

(  ␣  ␣` ␣     ´ ␣  ␣   ␣     ␣  ␣   ␣  ); back error ;

scanned result (0)(dimen val );

end else if cur cmd ≤ assign toks then

begin if cur cmd < assign toks then { cur cmd = toks register } begin scan eight bit int ; m ← toks base + cur val ; end;

scanned result (equiv (m))(tok val );

end else begin back input ; scan font ident ; scanned result (font id base + cur val )(ident val );

end

This code is used in section 413.

416. Users refer to ‘  ’ only in horizontal mode, and to ‘  ’ only in vertical

mode; so we put the associated mode in the modifier part of the set aux command. The set page int command has modifier 0 or 1, for ‘ ’ and ‘ ’, respectively. The set box dimen command is modified by either width offset , height offset , or depth offset . And the last item command is modified by either int val , dimen val , glue val , input line no code, or badness code.

define input line no code = glue val + 1 { code for } define badness code = glue val + 2 { code for }

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

primitive(  , set aux , hmode); primitive(  , set aux , vmode);
primitive(  , set page int , 0); primitive(  , set page int , 1);
primitive(  , set box dimen, width offset ); primitive(  , set box dimen, height offset );
primitive(  , set box dimen, depth offset ); primitive(  , last item, int val );
primitive(  , last item, dimen val ); primitive(  , last item, glue val );
primitive(  , last item, input line no code); primitive(  , last item, badness code);

417. ⟨ Cases of print cmd chr for symbolic printing of primitives 227 ⟩ +≡

set aux : if chr code = vmode then print esc(  ) else print esc(  );
set page int : if chr code = 0 then print esc(  ) else print esc(  );

set box dimen: if chr code = width offset then print esc( )

else if chr code = height offset then print esc( )

else print esc( ); last item: case chr code of

int val : print esc( ); dimen val : print esc( ); glue val : print esc( ); input line no code: print esc( ); othercases print esc( ) endcases; 418. ⟨ Fetch the space factor or the prev depth 418 ⟩ ≡

if abs (mode) ≠ m then begin print err ( ␣ ); print cmd chr (set aux , m);

  help4 (  ␣   ␣     ␣  ␣           ␣    ␣  ␣          ␣  )
  (  ␣   ␣     ␣  ␣          ␣    ␣  ␣        ␣     ␣  )
  (  ␣  ␣     ␣  ␣          ␣      ␣       ␣  )
  ( ´ ␣          ␣    ␣   ␣    ␣   ␣     ␣    ␣  ); error ;

if level ≠ tok val then scanned result (0)(dimen val )

else scanned result (0)(int val );

end else if m = vmode then

begin cur val ← prev depth; cur val level ← dimen val ; end

else begin cur val ← space factor ; cur val level ← int val ;

end

This code is used in section 413.

419. ⟨ Fetch the dead cycles or the insert penalties 419 ⟩ ≡ begin if m = 0 then cur val ← dead cycles else cur val ← insert penalties ; cur val level ← int val ; end

This code is used in section 413.

420. ⟨ Fetch a box dimension 420 ⟩ ≡ begin scan eight bit int ; if box (cur val ) = null then cur val ← 0 else cur val ← mem[box (cur val ) + m].sc; cur val level ← dimen val ; end

This code is used in section 413.

421. Inside an routine, a user may wish to look at the page totals that were present at the moment when output was triggered.

define max dimen ≡ ´ 7777777777 { 2^{30} − 1 } ⟨ Fetch something on the page so far 421 ⟩ ≡

begin if (page contents = empty ) ∧ (¬output active) then

if m = 0 then cur val ← max dimen else cur val ← 0 else cur val ← page so far [m]; cur val level ← dimen val ; end

This code is used in section 413.

422. ⟨ Fetch the prev graf 422 ⟩ ≡ if mode = 0 then scanned result (0)(int val ) { prev graf = 0 within } else begin nest [nest ptr ] ← cur list ; p ← nest ptr ;

while abs (nest [p].mode field ) ≠ vmode do decr (p);

scanned result (nest [p].pg field )(int val );

end

This code is used in section 413.

423. ⟨ Fetch the par shape size 423 ⟩ ≡ begin if par shape ptr = null then cur val ← 0 else cur val ← info(par shape ptr ); cur val level ← int val ; end

This code is used in section 413.

424. Here is where  ,  , and  are implemented. The reference count for

will be updated later.

We also handle and here, because they are legal in similar contexts. ⟨ Fetch an item in the current node, if appropriate 424 ⟩ ≡

if cur chr > glue val then begin if cur chr = input line no code then cur val ← line else cur val ← last badness ; { cur chr = badness code } cur val level ← int val ; end

else begin if cur chr = glue val then cur val ← zero glue else cur val ← 0; cur val level ← cur chr ; if ¬is char node(tail ) ∧ (mode ≠ 0) then

case cur chr of

int val : if type(tail ) = penalty node then cur val ← penalty (tail ); dimen val : if type(tail ) = kern node then cur val ← width(tail ); glue val : if type(tail ) = glue node then begin cur val ← glue ptr (tail ); if subtype(tail ) = mu glue then cur val level ← mu val ; end;

end { there are no other cases } else if (mode = vmode) ∧ (tail = head ) then

case cur chr of

int val : cur val ← last penalty ; dimen val : cur val ← last kern; glue val : if last glue ≠ max halfword then cur val ← last glue; end; { there are no other cases }

end

This code is used in section 413.

425. ⟨ Fetch a font dimension 425 ⟩ ≡ begin find font dimen(false); font info[fmem ptr ].sc ← 0; scanned result (font info[cur val ].sc)(dimen val ); end

This code is used in section 413.

426. ⟨ Fetch a font integer 426 ⟩ ≡ begin scan font ident ; if m = 0 then scanned result (hyphen char [cur val ])(int val ) else scanned result (skew char [cur val ])(int val ); end

This code is used in section 413.

427. ⟨ Fetch a register 427 ⟩ ≡ begin scan eight bit int ;

case m of

int val : cur val ← count (cur val ); dimen val : cur val ← dimen(cur val ); glue val : cur val ← skip(cur val ); mu val : cur val ← mu skip(cur val ); end; { there are no other cases } cur val level ← m; end

This code is used in section 413.

428. ⟨ Complain that can’t do this; give zero result 428 ⟩ ≡ begin print err ( ␣ ´ ␣ ␣` ); print cmd chr (cur cmd , cur chr ); print ( ´ ␣ ␣ ); print esc( ); help1 ( ´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ); error ; if level ≠ tok val then scanned result (0)(dimen val ) else scanned result (0)(int val ); end

This code is used in section 413.

429. When a glue val changes to a dimen val , we use the width component of the glue; there is no need to decrease the reference count, since it has not yet been increased. When a dimen val changes to an int val , we use scaled points so that the value doesn’t actually change. And when a mu val changes to a glue val , the value doesn’t change either. ⟨ Convert cur val to a lower level 429 ⟩ ≡

begin if cur val level = glue val then cur val ← width(cur val ) else if cur val level = mu val then mu error ; decr (cur val level ); end

This code is used in section 413.

430. If cur val points to a glue specification at this point, the reference count for the glue does not yet include the reference by cur val . If negative is true, cur val level is known to be ≤ mu val . ⟨ Fix the reference count, if any, and negate cur val if negative 430 ⟩ ≡

if negative then

if cur val level ≥ glue val then begin cur val ← new spec(cur val ); ⟨ Negate all three glue components of cur val 431 ⟩; end

else negate(cur val ) else if (cur val level ≥ glue val ) ∧ (cur val level ≤ mu val ) then add glue ref (cur val )

This code is used in section 413.

431. ⟨ Negate all three glue components of cur val 431 ⟩ ≡ begin negate(width(cur val )); negate(stretch(cur val )); negate(shrink (cur val )); end

This code is used in section 430.

432. Our next goal is to write the scan int procedure, which scans anything that TEX treats as an integer. But first we might as well look at some simple applications of scan int that have already been made inside of scan something internal .

433. ⟨ Declare procedures that scan restricted classes of integers 433 ⟩ ≡ procedure scan eight bit int ;

begin scan int ; if (cur val < 0) ∨ (cur val > 255) then

begin print err (  ␣        ␣  );

help2 ( ␣        ␣      ␣    ␣  ␣       ␣ ␣   ␣  )

( ␣ ␣ ␣ ␣ ␣ ); int error (cur val ); cur val ← 0; end;

end;

See also sections 434, 435, 436, and 437. This code is used in section 409.

434. ⟨ Declare procedures that scan restricted classes of integers 433 ⟩ +≡ procedure scan char num;

begin scan int ; if (cur val < 0) ∨ (cur val > 255) then

begin print err (  ␣         ␣  );

help2 ( ␣         ␣      ␣    ␣  ␣       ␣ ␣   ␣  )

( ␣ ␣ ␣ ␣ ␣ ); int error (cur val ); cur val ← 0; end;

end;

435. While we’re at it, we might as well deal with similar routines that will be needed later. ⟨ Declare procedures that scan restricted classes of integers 433 ⟩ +≡ procedure scan four bit int ;

begin scan int ; if (cur val < 0) ∨ (cur val > 15) then

begin print err (  ␣  );

help2 (  ␣ ␣        ␣  ␣    ␣ ␣      ␣       ␣ ␣   ␣  )

( ␣ ␣ ␣ ␣ ␣ ); int error (cur val ); cur val ← 0; end;

end;

436. ⟨ Declare procedures that scan restricted classes of integers 433 ⟩ +≡ procedure scan fifteen bit int ;

begin scan int ; if (cur val < 0) ∨ (cur val > ´ 77777 ) then

begin print err ( ␣ ); help2 ( ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ) ( ␣ ␣ ␣ ␣ ␣ ); int error (cur val ); cur val ← 0; end;

end;

437. ⟨ Declare procedures that scan restricted classes of integers 433 ⟩ +≡ procedure scan twenty seven bit int ;

begin scan int ; if (cur val < 0) ∨ (cur val > ´ 777777777 ) then

begin print err (  ␣         ␣  );

help2 ( ␣       ␣         ␣    ␣    ␣  ␣       ␣ ␣   ␣  )

( ␣ ␣ ␣ ␣ ␣ ); int error (cur val ); cur val ← 0; end;

end; 438. An integer number can be preceded by any number of spaces and ‘ ’ or ‘ ’ signs. Then comes either a decimal constant (i.e., radix 10), an octal constant (i.e., radix 8, preceded by ´ ), a hexadecimal constant (radix 16, preceded by ), an alphabetic constant (preceded by ` ), or an internal variable. After scanning is complete, cur val will contain the answer, which must be at most 2^{31} − 1 = 2147483647 in absolute value. The value of radix is set to 10, 8, or 16 in the cases of decimal, octal, or hexadecimal constants, otherwise radix is set to zero. An optional space follows a constant.

define octal token = other token + ´ { apostrophe, indicates an octal constant } define hex token = other token + { double quote, indicates a hex constant } define alpha token = other token + ` { reverse apostrophe, precedes alpha constants } define point token = other token + { decimal point } define continental point token = other token + { decimal point, Eurostyle }

⟨ Global variables 13 ⟩ +≡ radix : small number ; { scan int sets this to 8, 10, 16, or zero }

439. We initialize the following global variables just in case expand comes into action before any of the basic scanning routines has assigned them a value. ⟨ Set initial values of key variables 21 ⟩ +≡

cur val ← 0; cur val level ← int val ; radix ← 0; cur order ← 0;

440. The scan int routine is used also to scan the integer part of a fraction; for example, the ‘ ’ in ‘ ’ will be found by scan int . The scan dimen routine assumes that cur tok = point token after the integer part of such a fraction has been scanned by scan int , and that the decimal point has been backed up to be scanned again. procedure scan int ; { sets cur val to an integer }

label done;

var negative: boolean; { should the answer be negated? } m: integer ; { 2^{31} div radix , the threshold of danger } d: small number ; { the digit just scanned } vacuous : boolean; { have no digits appeared? } OK so far : boolean; { has an error message been issued? }

begin radix ← 0; OK so far ← true;

⟨ Get the next non-blank non-sign token; set negative appropriately 441 ⟩;

if cur tok = alpha token then ⟨ Scan an alphabetic character code into cur val 442 ⟩ else if (cur cmd ≥ min internal ) ∧ (cur cmd ≤ max internal ) then

scan something internal (int val , false) else ⟨ Scan a numeric constant 444 ⟩;

if negative then negate(cur val );

end;

441. ⟨ Get the next non-blank non-sign token; set negative appropriately 441 ⟩ ≡ negative ← false; repeat ⟨ Get the next non-blank non-call token 406 ⟩;

if cur tok = other token + then begin negative ← ¬negative; cur tok ← other token + ; end;

until cur tok ≠ other token +

This code is used in sections 440, 448, and 461.

442. A space is ignored after an alphabetic character constant, so that such constants behave like numeric ones. ⟨ Scan an alphabetic character code into cur val 442 ⟩ ≡

begin get token; { suppress macro expansion } if cur tok < cs token flag then

begin cur val ← cur chr ;

if cur cmd ≤ right brace then if cur cmd = right brace then incr (align state) else decr (align state);

end else if cur tok < cs token flag + single base then cur val ← cur tok − cs token flag − active base

else cur val ← cur tok − cs token flag − single base; if cur val > 255 then

begin print err (  ␣          ␣  );

  help2 ( ␣            ␣       ␣        ␣       ␣     ␣ ␣` ␣  )

( ␣ ´ ␣ ␣ ␣ ␣ ); cur val ← ; back error ; end

else ⟨ Scan an optional space 443 ⟩; end

This code is used in section 440.

443. ⟨ Scan an optional space 443 ⟩ ≡ begin get x token; if cur cmd ≠ spacer then back input ; end

This code is used in sections 442, 448, 455, and 1200.

444. ⟨ Scan a numeric constant 444 ⟩ ≡ begin radix ← 10; m ← 214748364; if cur tok = octal token then

begin radix ← 8; m ← ´ 2000000000 ; get x token;

end else if cur tok = hex token then

begin radix ← 16; m ← ´ 1000000000 ; get x token; end;

vacuous ← true; cur val ← 0; ⟨ Accumulate the constant until cur tok is not a suitable digit 445 ⟩; if vacuous then ⟨ Express astonishment that no number was here 446 ⟩ else if cur cmd ≠ spacer then back input ; end

This code is used in section 440.

445. define infinity ≡ ´ 17777777777 { the largest positive value that TEX knows } define zero token = other token + { zero, the smallest digit } define A token = letter token + { the smallest special hex digit } define other A token = other token + { special hex digit of type other char }

⟨ Accumulate the constant until cur tok is not a suitable digit 445 ⟩ ≡

loop begin if (cur tok < zero token + radix ) ∧ (cur tok ≥ zero token) ∧ (cur tok ≤ zero token + 9)

then d ← cur tok − zero token else if radix = 16 then

if (cur tok ≤ A token + 5) ∧ (cur tok ≥ A token) then d ← cur tok − A token + 10 else if (cur tok ≤ other A token + 5) ∧ (cur tok ≥ other A token) then

d ← cur tok − other A token + 10

else goto done

else goto done; vacuous ← false; if (cur val ≥ m) ∧ ((cur val > m) ∨ (d > 7) ∨ (radix ≠ 10)) then

begin if OK so far then
begin print err (  ␣   ␣  );
help2 ( ␣   ␣    ␣  ␣  ␣  ␣  ´  )

( ␣ ´ ␣ ␣ ␣ ␣ ␣ ␣ ); error ; cur val ← infinity ; OK so far ← false; end; end

else cur val ← cur val ∗ radix + d;

get x token;

end; done:

This code is used in section 444.

446. ⟨ Express astonishment that no number was here 446 ⟩ ≡

begin print err (  ␣       ␣       ␣  ␣  );

  help3 ( ␣      ␣      ␣    ␣    ␣     ␣ ␣        ␣` ´  )
  (  ␣   ␣   ´ ␣      ␣   ␣   ␣ ␣      ␣  ␣   ␣ ␣  )

(  ␣  ␣` ␣     ´ ␣  ␣   ␣     ␣  ␣   ␣  ); back error ;

end

This code is used in section 444.

447. The scan dimen routine is similar to scan int , but it sets cur val to a scaled value, i.e., an integral number of sp. One of its main tasks is therefore to interpret the abbreviations for various kinds of units and to convert measurements to scaled points.

There are three parameters: mu is true if the finite units must be ‘ ’, while mu is false if ‘ ’ units are disallowed; inf is true if the infinite units ‘ ’, ‘ ’, ‘ ’ are permitted; and shortcut is true if cur val already contains an integer and only the units need to be considered.

The order of infinity that was found in the case of infinite glue is returned in the global variable cur order . ⟨ Global variables 13 ⟩ +≡ cur order : glue ord ; { order of infinity found by scan dimen } 448. Constructions like ‘ ´ ’ are legal dimensions, so scan dimen may begin with scan int . This explains why it is convenient to use scan int also for the integer part of a decimal fraction.

Several branches of scan dimen work with cur val as an integer and with an auxiliary fraction f, so that the actual quantity of interest is cur val + f/2^{16}. At the end of the routine, this “unpacked” representation is put into the single word cur val , which suddenly switches significance from integer to scaled .

define attach fraction = 88 { go here to pack cur val and f into cur val } define attach sign = 89 { go here when cur val is correct except perhaps for sign } define scan normal dimen ≡ scan dimen(false, false, false)

procedure scan dimen(mu, inf , shortcut : boolean); { sets cur val to a dimension } label done, done1 , done2 , found , not found , attach fraction, attach sign; var negative: boolean; { should the answer be negated? }

f: integer ; { numerator of a fraction whose denominator is 2^{16} }

⟨ Local variables for dimension calculations 450 ⟩ begin f ← 0; arith error ← false; cur order ← normal ; negative ← false; if ¬shortcut then

begin ⟨ Get the next non-blank non-sign token; set negative appropriately 441 ⟩; if (cur cmd ≥ min internal ) ∧ (cur cmd ≤ max internal ) then

⟨ Fetch an internal dimension and goto attach sign, or fetch an internal integer 449 ⟩ else begin back input ;

if cur tok = continental point token then cur tok ← point token;

if cur tok ≠ point token then scan int else begin radix ← 10; cur val ← 0;

end; if cur tok = continental point token then cur tok ← point token; if (radix = 10) ∧ (cur tok = point token) then ⟨ Scan decimal fraction 452 ⟩; end;

end; if cur val < 0 then { in this case f = 0 }

begin negative ← ¬negative; negate(cur val );

end; ⟨ Scan units and set cur val to x · (cur val + f/2^{16}), where there are x sp per unit; goto attach sign if

the units are internal 453 ⟩; ⟨ Scan an optional space 443 ⟩;

attach sign: if arith error ∨ (abs (cur val ) ≥ ´ 10000000000 ) then

⟨ Report that this dimension is out of range 460 ⟩; if negative then negate(cur val ); end;

449. ⟨ Fetch an internal dimension and goto attach sign, or fetch an internal integer 449 ⟩ ≡

if mu then

begin scan something internal (mu val , false); ⟨ Coerce glue to a dimension 451 ⟩; if cur val level = mu val then goto attach sign; if cur val level ≠ int val then mu error ; end

else begin scan something internal (dimen val , false); if cur val level = dimen val then goto attach sign; end

This code is used in section 448.

450. ⟨ Local variables for dimension calculations 450 ⟩ ≡ num, denom: 1 . . 65536; { conversion ratio for the scanned units } k, kk : small number ; { number of digits in a decimal fraction } p, q: pointer ; { top of decimal digit stack } v: scaled ; { an internal dimension } save cur val : integer ; { temporary storage of cur val }

This code is used in section 448.

451. The following code is executed when scan something internal was called asking for mu val , when we really wanted a “mudimen” instead of “muglue.” ⟨ Coerce glue to a dimension 451 ⟩ ≡

if cur val level ≥ glue val then begin v ← width(cur val ); delete glue ref (cur val ); cur val ← v; end

This code is used in sections 449 and 455.

452. When the following code is executed, we have cur tok = point token, but this token has been backed up using back input ; we must first discard it.

It turns out that a decimal point all by itself is equivalent to ‘ ’. Let’s hope people don’t use that fact. ⟨ Scan decimal fraction 452 ⟩ ≡

begin k ← 0; p ← null ; get token; { point token is being re-scanned }

loop begin get x token; if (cur tok > zero token + 9) ∨ (cur tok < zero token) then goto done1 ; if k < 17 then { digits for k ≥ 17 cannot affect the result }

begin q ← get avail ; link (q) ← p; info(q) ← cur tok − zero token; p ← q; incr (k); end; end;

done1 : for kk ← k downto 1 do begin dig [kk − 1] ← info(p); q ← p; p ← link (p); free avail (q); end;

f ← round decimals (k);

if cur cmd ≠ spacer then back input ;

end

This code is used in section 448.

453. Now comes the harder part: At this point in the program, cur val is a nonnegative integer and f/2^{16} is a nonnegative fraction less than 1; we want to multiply the sum of these two quantities by the appropriate factor, based on the specified units, in order to produce a scaled result, and we want to do the calculation with fixed point arithmetic that does not overflow. ⟨ Scan units and set cur val to x · (cur val + f/2^{16}), where there are x sp per unit; goto attach sign if the

units are internal 453 ⟩ ≡ if inf then ⟨ Scan for units; goto attach fraction if found 454 ⟩; ⟨ Scan for units that are internal dimensions; goto attach sign with cur val set if found 455 ⟩; if mu then ⟨ Scan for units and goto attach fraction 456 ⟩; if scan keyword ( ) then ⟨ Adjust for the magnification ratio 457 ⟩; if scan keyword ( ) then goto attach fraction; { the easy case } ⟨ Scan for all other units and adjust cur val and f accordingly; goto done in the case of scaled

points 458 ⟩; attach fraction: if cur val ≥ ´ 40000 then arith error ← true

else cur val ← cur val ∗ unity + f; done:

This code is used in section 448.

454. A specification like ‘  ’ or ‘  ’ will lead to two error messages (one for each additional

keyword ). ⟨ Scan for units; goto attach fraction if found 454 ⟩ ≡

if scan keyword ( ) then begin cur order ← fil ; while scan keyword ( ) do

begin if cur order = filll then begin print err ( ␣ ␣ ␣ ␣ ); print ( ␣ ␣ ); help1 ( ␣ ´ ␣ ␣ ␣ ␣ ␣ ); error ; end

else incr (cur order );

end; goto attach fraction; end

This code is used in section 453.

455. ⟨ Scan for units that are internal dimensions; goto attach sign with cur val set if found 455 ⟩ ≡ save cur val ← cur val ; ⟨ Get the next non-blank non-call token 406 ⟩; if (cur cmd < min internal ) ∨ (cur cmd > max internal ) then back input

else begin if mu then

begin scan something internal (mu val , false); ⟨ Coerce glue to a dimension 451 ⟩; if cur val level ≠ mu val then mu error ; end

else scan something internal (dimen val , false);

v ← cur val ; goto found ;

end; if mu then goto not found ; if scan keyword ( ) then v ← (⟨ The em width for cur font 558 ⟩) else if scan keyword ( ) then v ← (⟨ The x-height for cur font 559 ⟩)

else goto not found ; ⟨ Scan an optional space 443 ⟩;

found : cur val ← nx plus y (save cur val , v, xn over d (v, f, ´ 200000 )); goto attach sign; not found :

This code is used in section 453.

456. ⟨ Scan for units and goto attach fraction 456 ⟩ ≡ if scan keyword ( ) then goto attach fraction

else begin print err (  ␣    ␣  ␣       ␣ ); print ( ␣  );

  help4 (  ␣    ␣  ␣          ␣  ␣    ␣    ␣    ␣  ␣  )
  ( ␣       ␣          ␣    ␣    ␣      ␣  ´ ␣    ␣  )
  (  ␣   ␣         ␣      ␣     ␣    ␣`  ´ ␣  ␣  )

(  ␣        ␣    ␣       ␣  ␣  ␣   ␣  ); error ; goto attach fraction;

end

This code is used in section 453.

457. ⟨ Adjust for the magnification ratio 457 ⟩ ≡ begin prepare mag ; if mag ≠ 1000 then

begin cur val ← xn over d (cur val , 1000, mag ); f ← (1000 ∗ f + ´ 200000 ∗ remainder ) div mag ; cur val ← cur val + (f div ´ 200000 ); f ← f mod ´ 200000 ; end;

end

This code is used in section 453.

458. The necessary conversion factors can all be specified exactly as fractions whose numerator and denominator are 32768 or less. According to the definitions here, 2660 dd ≈ 1000.33297 mm; this agrees well with the value 1000.333 mm cited by Bosshard in Technische Grundlagen zur Satzherstellung (Bern, 1980).

define set conversion end (#) ≡ denom ← #;

end define set conversion(#) ≡ begin num ← #; set conversion end

⟨ Scan for all other units and adjust cur val and f accordingly; goto done in the case of scaled points 458 ⟩ ≡ if scan keyword ( ) then set conversion(7227)(100) else if scan keyword ( ) then set conversion(12)(1)

else if scan keyword ( ) then set conversion(7227)(254)

else if scan keyword ( ) then set conversion(7227)(2540) else if scan keyword ( ) then set conversion(7227)(7200) else if scan keyword ( ) then set conversion(1238)(1157)

else if scan keyword ( ) then set conversion(14856)(1157)

else if scan keyword ( ) then goto done

else ⟨ Complain about unknown unit and goto done2 459 ⟩;

cur val ← xn over d (cur val , num, denom); f ← (num ∗ f + ´ 200000 ∗ remainder ) div denom; cur val ← cur val + (f div ´ 200000 ); f ← f mod ´ 200000 ;

done2 :

This code is used in section 453.

459. ⟨ Complain about unknown unit and goto done2 459 ⟩ ≡

begin print err (  ␣    ␣  ␣       ␣ ); print ( ␣  );

help6 (  ␣   ␣  ␣  ␣     ␣  ␣   ␣   ␣   ␣   ␣  )
(  ␣   ␣   ␣   ␣   ␣  ␣   ␣   ␣     ␣  ␣ ␣   ␣  )
( ´ ␣      ␣    ␣   ␣     ␣  ␣   ␣   ␣   ␣       ´ ␣  )
( ␣       ␣          ␣    ␣    ␣      ␣  ´ ␣    ␣  )
(  ␣   ␣         ␣      ␣     ␣    ␣`  ´ ␣  ␣  )

(  ␣        ␣    ␣       ␣  ␣  ␣   ␣  ); error ; goto done2 ;

end

This code is used in section 458.

460. ⟨ Report that this dimension is out of range 460 ⟩ ≡

begin print err (  ␣   ␣  );

help2 ( ␣   ´ ␣    ␣    ␣     ␣      ␣    ␣     ␣  ␣  )
(  ␣   ␣ ´ ␣   ␣   ␣       ␣     ␣ ␣  );

error ; cur val ← max dimen; arith error ← false; end

This code is used in section 448.

461. The final member of TEX’s value-scanning trio is scan glue, which makes cur val point to a glue specification. The reference count of that glue spec will take account of the fact that cur val is pointing to it.

The level parameter should be either glue val or mu val . Since scan dimen was so much more complex than scan int , we might expect scan glue to be even worse.

But fortunately, it is very simple, since most of the work has already been done. procedure scan glue(level : small number ); { sets cur val to a glue spec pointer }

label exit ; var negative: boolean; { should the answer be negated? }

q: pointer ; { new glue specification } mu: boolean; { does level = mu val ? }

begin mu ← (level = mu val ); ⟨ Get the next non-blank non-sign token; set negative appropriately 441 ⟩; if (cur cmd ≥ min internal ) ∧ (cur cmd ≤ max internal ) then

begin scan something internal (level , negative); if cur val level ≥ glue val then

begin if cur val level ≠ level then mu error ; return; end;

if cur val level = int val then scan dimen(mu, false, true) else if level = mu val then mu error ; end

else begin back input ; scan dimen(mu, false, false); if negative then negate(cur val ); end;

⟨ Create a new glue specification whose width is cur val ; scan for its stretch and shrink components 462 ⟩; exit : end;

462. ⟨ Create a new glue specification whose width is cur val ; scan for its stretch and shrink

components 462 ⟩ ≡ q ← new spec(zero glue); width(q) ← cur val ; if scan keyword ( ) then

begin scan dimen(mu, true, false); stretch(q) ← cur val ; stretch order (q) ← cur order ; end;

if scan keyword ( ) then begin scan dimen(mu, true, false); shrink (q) ← cur val ; shrink order (q) ← cur order ; end;

cur val ← q

This code is used in section 461.

463. Here’s a similar procedure that returns a pointer to a rule node. This routine is called just after TEX

has seen  or  ; therefore cur cmd will be either hrule or vrule. The idea is to store the default
rule dimensions in the node, then to override them if ‘  ’ or ‘  ’ or ‘  ’ specifications are found

(in any order).

define default rule = 26214 { 0.4 pt } function scan rule spec: pointer ;

label reswitch; var q: pointer ; { the rule node being created } begin q ← new rule; { width, depth, and height all equal null flag now } if cur cmd = vrule then width(q) ← default rule else begin height (q) ← default rule; depth(q) ← 0;

end; reswitch: if scan keyword ( ) then

begin scan normal dimen; width(q) ← cur val ; goto reswitch; end;

if scan keyword ( ) then begin scan normal dimen; height (q) ← cur val ; goto reswitch; end;

if scan keyword ( ) then begin scan normal dimen; depth(q) ← cur val ; goto reswitch; end;

scan rule spec ← q; end;