§1055  TEX82  PART 47: BUILDING BOXES AND LISTS 395

1055. Building boxes and lists. The most important parts of main control are concerned with TEX’s chief mission of box-making. We need to control the activities that put entries on vlists and hlists, as well as the activities that convert those lists into boxes. All of the necessary machinery has already been developed; it remains for us to “push the buttons” at the right times.

1056. As an introduction to these routines, let’s consider one of the simplest cases: What happens when ‘ ’ occurs in vertical mode, or ‘ ’ in horizontal mode or math mode? The code in main control is short, since the scan rule spec routine already does most of what is required; thus, there is no need for a special action procedure.

Note that baselineskip calculations are disabled after a rule in vertical mode, by setting prev depth ← ignore depth. ⟨ Cases of main control that build boxes and lists 1056 ⟩ ≡ vmode + hrule, hmode + vrule, mmode + vrule: begin tail append (scan rule spec);

if abs (mode) = vmode then prev depth ← ignore depth

else if abs (mode) = hmode then space factor ← 1000;

end;

See also sections 1057, 1063, 1067, 1073, 1090, 1092, 1094, 1097, 1102, 1104, 1109, 1112, 1116, 1122, 1126, 1130, 1134, 1137,

1140, 1150, 1154, 1158, 1162, 1164, 1167, 1171, 1175, 1180, 1190, and 1193. This code is used in section 1045.

1057. The processing of things like  and  is slightly more complicated. But the code in

main control is very short, since it simply calls on the action routine append glue. Similarly, activates append kern. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ vmode + vskip, hmode + hskip, mmode + hskip, mmode + mskip: append glue; any mode(kern), mmode + mkern: append kern;

1058. The hskip and vskip command codes are used for control sequences like  and  as well as
for  and  . The difference is in the value of cur chr .
define fil code = 0 { identifies  and  }
define fill code = 1 { identifies  and  }
define ss code = 2 { identifies  and  }
define fil neg code = 3 { identifies  and  }
define skip code = 4 { identifies  and  }

define mskip code = 5 { identifies } ⟨ Put each of TEX’s primitives into the hash table 226 ⟩ +≡

primitive( , hskip, skip code);

primitive(  , hskip, fil code); primitive(  , hskip, fill code);
primitive(  , hskip, ss code); primitive(  , hskip, fil neg code);

primitive( , vskip, skip code);

primitive(  , vskip, fil code); primitive(  , vskip, fill code);
primitive(  , vskip, ss code); primitive(  , vskip, fil neg code);

primitive( , mskip, mskip code);

primitive(  , kern, explicit ); primitive(  , mkern, mu glue);

396  PART 47: BUILDING BOXES AND LISTS  TEX82 §1059

1059. ⟨ Cases of print cmd chr for symbolic printing of primitives 227 ⟩ +≡ hskip: case chr code of

skip code: print esc( ); fil code: print esc( ); fill code: print esc( ); ss code: print esc( ); othercases print esc( ) endcases;

vskip: case chr code of skip code: print esc( ); fil code: print esc( ); fill code: print esc( ); ss code: print esc( ); othercases print esc( ) endcases;

mskip: print esc( ); kern: print esc( ); mkern: print esc( );

1060. All the work relating to glue creation has been relegated to the following subroutine. It does not call build page, because it is used in at least one place where that would be a mistake. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure append glue;

var s: small number ; { modifier of skip command } begin s ← cur chr ;

case s of

fil code: cur val ← fil glue; fill code: cur val ← fill glue; ss code: cur val ← ss glue; fil neg code: cur val ← fil neg glue; skip code: scan glue(glue val ); mskip code: scan glue(mu val ); end; { now cur val points to the glue specification } tail append (new glue(cur val )); if s ≥ skip code then

begin decr (glue ref count (cur val ));

if s > skip code then subtype(tail ) ← mu glue; end;

end;

1061. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure append kern;

var s: quarterword ; { subtype of the kern node } begin s ← cur chr ; scan dimen(s = mu glue, false, false); tail append (new kern(cur val )); subtype(tail ) ← s; end; 1062. Many of the actions related to box-making are triggered by the appearance of braces in the input. For example, when the user says ‘ ⟨ ⟩ ’ in vertical mode, the information about the box size (100pt, exactly ) is put onto save stack with a level boundary word just above it, and cur group ← adjusted hbox group; TEX enters restricted horizontal mode to process the hlist. The right brace eventually causes save stack to be restored to its former state, at which time the information about the box size (100pt, exactly ) is available once again; a box is packaged and we leave restricted horizontal mode, appending the new box to the current list of the enclosing mode (in this case to the current list of vertical mode), followed by any vertical adjustments that were removed from the box by hpack .

The next few sections of the program are therefore concerned with the treatment of left and right curly braces.

1063. If a left brace occurs in the middle of a page or paragraph, it simply introduces a new level of grouping, and the matching right brace will not have such a drastic effect. Such grouping affects neither the mode nor the current list. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ non math(left brace): new save level (simple group); any mode(begin group): new save level (semi simple group); any mode(end group): if cur group = semi simple group then unsave

else off save;

1064. We have to deal with errors in which braces and such things are not properly nested. Sometimes the user makes an error of commission by inserting an extra symbol, but sometimes the user makes an error of omission. TEX can’t always tell one from the other, so it makes a guess and tries to avoid getting into a loop.

The off save routine is called when the current group code is wrong. It tries to insert something into the user’s input that will help clean off the top level. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure off save;

var p: pointer ; { inserted token }

begin if cur group = bottom level then ⟨ Drop current token and complain that it was unmatched 1066 ⟩ else begin back input ; p ← get avail ; link (temp head ) ← p; print err ( ␣ );

⟨ Prepare to insert a token that matches cur group, and print what it is 1065 ⟩; print ( ␣ ); ins list (link (temp head ));

    help5 ( ´ ␣        ␣         ␣    ␣   ␣   ␣    ␣  )
    (  ␣   ␣         ␣     ␣  )
    (  ␣     ␣    ␣    ␣   ␣  ␣         ␣   ␣  ␣  )
    (  ␣    ´ ␣      ␣         ␣   ␣      ␣`  ´ ␣    ␣  )

( ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ); error ; end;

end; 1065. At this point, link (temp head ) = p, a pointer to an empty one-word node. ⟨ Prepare to insert a token that matches cur group, and print what it is 1065 ⟩ ≡

case cur group of

semi simple group: begin info(p) ← cs token flag + frozen end group; print esc( );

end; math shift group: begin info(p) ← math shift token + $ ; print char ( $ );

end; math left group: begin info(p) ← cs token flag + frozen right ; link (p) ← get avail ; p ← link (p);

info(p) ← other token + ; print esc( );

end; othercases begin info(p) ← right brace token + ; print char ( );

end endcases

This code is used in section 1064.

1066. ⟨ Drop current token and complain that it was unmatched 1066 ⟩ ≡ begin print err ( ␣ ); print cmd chr (cur cmd , cur chr );

help1 (  ␣   ␣      ␣     ␣   ␣   ␣ ␣     ␣   ␣     ␣  ␣  );

error ; end

This code is used in section 1064.

1067. The routine for a right brace character branches into many subcases, since a variety of things may happen, depending on cur group. Some types of groups are not supposed to be ended by a right brace; error messages are given in hopes of pinpointing the problem. Most branches of this routine will be filled in later, when we are ready to understand them; meanwhile, we must prepare ourselves to deal with such errors. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ any mode(right brace): handle right brace;

1068. ⟨ Declare the procedure called handle right brace 1068 ⟩ ≡ procedure handle right brace;

var p, q: pointer ; { for short-term use } d: scaled ; { holds split max depth in insert group } f: integer ; { holds floating penalty in insert group }

begin case cur group of

simple group: unsave; bottom level : begin print err ( ␣ ␣ ´ );

  help2 (  ´ ␣      ␣    ␣      ␣    ␣   ␣  )

(  ␣       ␣   ␣         ␣         ␣  ␣    ␣  ); error ;

end; semi simple group, math shift group, math left group: extra right brace; ⟨ Cases of handle right brace where a right brace triggers a delayed action 1085 ⟩ othercases confusion( ) endcases; end;

This code is used in section 1030.

1069. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure extra right brace;

begin print err ( ␣ ␣ ␣ ␣ );

case cur group of

semi simple group: print esc( ); math shift group: print char ( $ ); math left group: print esc( ); end;

help5 ( ´ ␣       ␣ ␣            ␣      ␣       ␣  ␣     ␣  ␣  )
(  ␣  ␣  ␣` $  $´ ␣   ␣       ␣   ␣ ␣  ␣          ␣  )
(  ␣      ␣         ␣     ␣  ␣  ␣` $  ´ ␣  ␣    ␣  )
(  ␣   ␣  ␣       ␣  ␣  ␣      ␣    ␣   ␣         ␣   ␣  )

(  ␣         ␣     ␣  ␣      ␣` $ ´  ); error ; incr (align state);

end;

1070. Here is where we clear the parameters that are supposed to revert to their default values after every paragraph and when internal vertical mode is entered. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure normal paragraph;

begin if looseness ≠ 0 then eq word define(int base + looseness code, 0); if hang indent ≠ 0 then eq word define(dimen base + hang indent code, 0); if hang after ≠ 1 then eq word define(int base + hang after code, 1); if par shape ptr ≠ null then eq define(par shape loc, shape ref , null ); end; 1071. Now let’s turn to the question of how is treated. We actually need to consider also a slightly larger context, since constructions like ‘ ’ and ‘ ’ and ‘ ’ are supposed to invoke quite different actions after the box has been packaged. Conversely, constructions like ‘ ’ can be followed by a variety of different kinds of boxes, and we would like to encode such things in an efficient way.

In other words, there are two problems: To represent the context of a box, and to represent its type. The first problem is solved by putting a “context code” on the save stack , just below the two entries

that give the dimensions produced by scan spec. The context code is either a (signed) shift amount, or it is a large integer ≥ box flag , where box flag = 2^{30}. Codes box flag through box flag + 255 represent

‘  ’ through ‘  ’; codes box flag + 256 through box flag + 511 represent ‘  ’
through ‘  ’; code box flag + 512 represents ‘  ’; and codes box flag + 513 through
box flag + 515 represent ‘  ’, ‘  ’, and ‘  ’.

The second problem is solved by giving the command code make box to all control sequences that produce a box, and by using the following chr code values to distinguish between them: box code, copy code, last box code, vsplit code, vtop code, vtop code + vmode, and vtop code + hmode, where the latter two are used denote and , respectively.

define box flag ≡ ´ 10000000000 { context code for ‘ ’ } define ship out flag ≡ box flag + 512 { context code for ‘ ’ } define leader flag ≡ box flag + 513 { context code for ‘ ’ } define box code = 0 { chr code for ‘ ’ } define copy code = 1 { chr code for ‘ ’ } define last box code = 2 { chr code for ‘ ’ } define vsplit code = 3 { chr code for ‘ ’ } define vtop code = 4 { chr code for ‘ ’ }

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

primitive(  , hmove, 1); primitive(  , hmove, 0);
primitive(  , vmove, 1); primitive(  , vmove, 0);
primitive(  , make box , box code); primitive(  , make box , copy code);
primitive(  , make box , last box code); primitive(  , make box , vsplit code);

primitive( , make box , vtop code); primitive( , make box , vtop code + vmode); primitive( , make box , vtop code + hmode); primitive( , leader ship, a leaders − 1); { ship out flag = leader flag − 1 } primitive( , leader ship, a leaders ); primitive( , leader ship, c leaders ); primitive( , leader ship, x leaders );

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

hmove: if chr code = 1 then print esc(  ) else print esc(  );
vmove: if chr code = 1 then print esc(  ) else print esc(  );

make box : case chr code of box code: print esc( ); copy code: print esc( ); last box code: print esc( ); vsplit code: print esc( ); vtop code: print esc( ); vtop code + vmode: print esc( ); othercases print esc( ) endcases;

leader ship: if chr code = a leaders then print esc( )

else if chr code = c leaders then print esc( )

else if chr code = x leaders then print esc( ) else print esc( ); 1073. Constructions that require a box are started by calling scan box with a specified context code. The scan box routine verifies that a make box command comes next and then it calls begin box . ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ vmode + hmove, hmode + vmove, mmode + vmove: begin t ← cur chr ; scan normal dimen;

if t = 0 then scan box (cur val ) else scan box (−cur val );

end; any mode(leader ship): scan box (leader flag − a leaders + cur chr ); any mode(make box ): begin box (0);

1074. The global variable cur box will point to a newly-made box. If the box is void, we will have cur box = null . Otherwise we will have type(cur box ) = hlist node or vlist node or rule node; the rule node case can occur only with leaders. ⟨ Global variables 13 ⟩ +≡ cur box : pointer ; { box to be placed into its context }

1075. The box end procedure does the right thing with cur box , if box context represents the context as explained above. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure box end (box context : integer );

var p: pointer ; { ord noad for new box in math mode }

begin if box context < box flag then

⟨ Append box cur box to the current list, shifted by box context 1076 ⟩ else if box context < ship out flag then ⟨ Store cur box in a box register 1077 ⟩

else if cur box ≠ null then if box context > ship out flag then ⟨ Append a new leader node that uses cur box 1078 ⟩ else ship out (cur box );

end; 1076. The global variable adjust tail will be non-null if and only if the current box might include adjustments that should be appended to the current vertical list. ⟨ Append box cur box to the current list, shifted by box context 1076 ⟩ ≡

begin if cur box ≠ null then begin shift amount (cur box ) ← box context ; if abs (mode) = vmode then

begin append to vlist (cur box );

if adjust tail ≠ null then

begin if adjust head ≠ adjust tail then begin link (tail ) ← link (adjust head ); tail ← adjust tail ; end;

adjust tail ← null ;

end; if mode > 0 then build page; end

else begin if abs (mode) = hmode then space factor ← 1000

else begin p ← new noad ; math type(nucleus (p)) ← sub box ; info(nucleus (p)) ← cur box ; cur box ← p; end;

link (tail ) ← cur box ; tail ← cur box ;

end; end;

end

This code is used in section 1075.

1077. ⟨ Store cur box in a box register 1077 ⟩ ≡ if box context < box flag + 256 then eq define(box base − box flag + box context , box ref , cur box ) else geq define(box base − box flag − 256 + box context , box ref , cur box )

This code is used in section 1075.

1078. ⟨ Append a new leader node that uses cur box 1078 ⟩ ≡ begin ⟨ Get the next non-blank non-relax non-call token 404 ⟩; if ((cur cmd = hskip) ∧ (abs (mode) ≠ vmode)) ∨ ((cur cmd = vskip) ∧ (abs (mode) = vmode)) ∨

((cur cmd = mskip) ∧ (abs (mode) = mmode)) then begin append glue; subtype(tail ) ← box context − (leader flag − a leaders ); leader ptr (tail ) ← cur box ; end

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

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

(  ␣  ␣       ␣  ␣ ´ ␣        ␣     ␣  ); back error ; flush node list (cur box );

end; end

This code is used in section 1075.

1079. Now that we can see what eventually happens to boxes, we can consider the first steps in their creation. The begin box routine is called when box context is a context specification, cur chr specifies the type of box desired, and cur cmd = make box . ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure begin box (box context : integer );

label exit , done; var p, q: pointer ; { run through the current list }

m: quarterword ; { the length of a replacement list }

k: halfword ; { 0 or vmode or hmode }

n: eight bits ; { a box number }

begin case cur chr of

box code: begin scan eight bit int ; cur box ← box (cur val ); box (cur val ) ← null ;

{ the box becomes void, at the same level } end;

copy code: begin scan eight bit int ; cur box ← copy node list (box (cur val ));

end; last box code: ⟨ If the current list ends with a box node, delete it from the list and make cur box point to

it; otherwise set cur box ← null 1080 ⟩; vsplit code: ⟨ Split off part of a vertical box, make cur box point to it 1082 ⟩; othercases ⟨ Initiate the construction of an hbox or vbox, then return 1083 ⟩ endcases; box end (box context ); { in simple cases, we use the box immediately }

exit : end;

1080. Note that the condition ¬is char node(tail ) implies that head ≠ tail , since head is a one-word node. ⟨ If the current list ends with a box node, delete it from the list and make cur box point to it; otherwise set

cur box ← null 1080 ⟩ ≡

begin cur box ← null ; if abs (mode) = mmode then

begin you cant ; help1 ( ␣ ␣ ␣ ␣ ␣ ); error ; end

else if (mode = vmode) ∧ (head = tail ) then

begin you cant ; help2 (  ␣       ␣   ´ ␣    ␣      ␣    ␣   ␣       ␣  )
(  ␣        ␣    ␣         ␣  ␣  ); error ;

end else begin if ¬is char node(tail ) then

if (type(tail ) = hlist node) ∨ (type(tail ) = vlist node) then

⟨ Remove the last box, unless it’s part of a discretionary 1081 ⟩; end;

end

This code is used in section 1079.

1081. ⟨ Remove the last box, unless it’s part of a discretionary 1081 ⟩ ≡ begin q ← head ; repeat p ← q;

if ¬is char node(q) then

if type(q) = disc node then begin for m ← 1 to replace count (q) do p ← link (p); if p = tail then goto done; end;

q ← link (p); until q = tail ; cur box ← tail ; shift amount (cur box ) ← 0; tail ← p; link (p) ← null ;

done: end

This code is used in section 1080.

1082. Here we deal with things like ‘ ’. ⟨ Split off part of a vertical box, make cur box point to it 1082 ⟩ ≡

begin scan eight bit int ; n ← cur val ; if ¬scan keyword ( ) then

begin print err (  ␣`   ´ ␣  );

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

(  ␣    ␣   ␣   ␣       ␣  ); error ;

end; scan normal dimen; cur box ← vsplit (n, cur val ); end

This code is used in section 1079.

1083. Here is where we enter restricted horizontal mode or internal vertical mode, in order to make a box. ⟨ Initiate the construction of an hbox or vbox, then return 1083 ⟩ ≡

begin k ← cur chr − vtop code; saved (0) ← box context ; if k = hmode then

if (box context < box flag ) ∧ (abs (mode) = vmode) then scan spec(adjusted hbox group, true)

else scan spec(hbox group, true) else begin if k = vmode then scan spec(vbox group, true)

else begin scan spec(vtop group, true); k ← vmode;

end; normal paragraph; end;

push nest ; mode ← −k; if k = vmode then

begin prev depth ← ignore depth;

if every vbox ≠ null then begin token list (every vbox , every vbox text );

end else begin space factor ← 1000;

if every hbox ≠ null then begin token list (every hbox , every hbox text );

end; return; end

This code is used in section 1079.

1084. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure scan box (box context : integer ); { the next input should specify a box or perhaps a rule }

begin ⟨ Get the next non-blank non-relax non-call token 404 ⟩; if cur cmd = make box then begin box (box context ) else if (box context ≥ leader flag ) ∧ ((cur cmd = hrule) ∨ (cur cmd = vrule)) then

begin cur box ← scan rule spec; box end (box context );

end

else begin

print err ( ␣ ␣ ␣ ␣ ␣ ␣ );

    help3 ( ␣   ␣         ␣  ␣   ␣     ␣  ␣     ␣  ␣     ␣  ␣    ␣  )
    (  ␣    ␣     ␣  ␣   ␣     ␣    ␣         ␣       ␣  )

(  ␣       ␣   ␣    ␣       ␣   ␣   ␣   ␣    ␣  ); back error ;

end; end;

1085. When the right brace occurs at the end of an  or  or  construction, the package

routine comes into action. We might also have to finish a paragraph that hasn’t ended. ⟨ Cases of handle right brace where a right brace triggers a delayed action 1085 ⟩ ≡ hbox group: package(0); adjusted hbox group: begin adjust tail ← adjust head ; package(0);

end; vbox group: begin end graf ; package(0);

end; vtop group: begin end graf ; package(vtop code);

end;

See also sections 1100, 1118, 1132, 1133, 1168, 1173, and 1186. This code is used in section 1068.

1086. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure package(c : small number );

var h: scaled ; { height of box } p: pointer ; { first node in a box } d: scaled ; { max depth }

begin d ← box max depth; unsave; save ptr ← save ptr − 3; if mode = −hmode then cur box ← hpack (link (head ), saved (2), saved (1)) else begin cur box ← vpackage(link (head ), saved (2), saved (1), d);

if c = vtop code then ⟨ Readjust the height and depth of cur box , for 1087 ⟩; end;

pop nest ; box end (saved (0)); end;

1087. The height of a ‘ ’ box is inherited from the first item on its list, if that item is an hlist node, vlist node, or rule node; otherwise the height is zero. ⟨ Readjust the height and depth of cur box , for 1087 ⟩ ≡

begin h ← 0; p ← list ptr (cur box ); if p ≠ null then

if type(p) ≤ rule node then h ← height (p); depth(cur box ) ← depth(cur box ) − h + height (cur box ); height (cur box ) ← h; end

This code is used in section 1086.

1088. A paragraph begins when horizontal-mode material occurs in vertical mode, or when the paragraph is explicitly started by ‘ ’ or ‘ ’. ⟨ Put each of TEX’s primitives into the hash table 226 ⟩ +≡

primitive(  , start par , 1); primitive(  , start par , 0);

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

start par : if chr code = 0 then print esc(  ) else print esc(  );

1090. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ vmode + start par : new graf (cur chr > 0); vmode + letter , vmode + other char , vmode + char num, vmode + char given, vmode + math shift ,

vmode + un hbox , vmode + vrule, vmode + accent , vmode + discretionary , vmode + hskip, vmode + valign, vmode + ex space, vmode + no boundary :

begin back input ; new graf (true); end;

1091. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ function norm min(h : integer ): small number ;

begin if h ≤ 0 then norm min ← 1 else if h ≥ 63 then norm min ← 63 else norm min ← h; end;

procedure new graf (indented : boolean); begin prev graf ← 0; if (mode = vmode) ∨ (head ≠ tail ) then tail append (new param glue(par skip code)); push nest ; mode ← hmode; space factor ← 1000; set cur lang ; clang ← cur lang ; prev graf ← (norm min(left hyphen min) ∗ ´ 100 + norm min(right hyphen min)) ∗ ´ 200000 + cur lang ;

if indented then

begin tail ← new null box ; link (head ) ← tail ; width(tail ) ← par indent ; end; if every par ≠ null then begin token list (every par , every par text ); if nest ptr = 1 then build page; { put par skip glue on current page } end;

1092. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ hmode + start par , mmode + start par : indent in hmode;

1093. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure indent in hmode;

var p, q: pointer ; begin if cur chr > 0 then { }

begin p ← new null box ; width(p) ← par indent ;

if abs (mode) = hmode then space factor ← 1000

else begin q ← new noad ; math type(nucleus (q)) ← sub box ; info(nucleus (q)) ← p; p ← q;

end; tail append (p); end;

end; 1094. A paragraph ends when a par end command is sensed, or when we are in horizontal mode when reaching the right brace of vertical-mode routines like , , or . ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ vmode + par end : begin normal paragraph;

if mode > 0 then build page;

end; hmode + par end : begin if align state < 0 then off save;

{ this tries to recover from an alignment that didn’t end properly } end graf ; { this takes us to the enclosing mode, if mode > 0 } if mode = vmode then build page; end;

hmode + stop, hmode + vskip, hmode + hrule, hmode + un vbox , hmode + halign: head for vmode;

1095. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure head for vmode;

begin if mode < 0 then if cur cmd ≠ hrule then off save else begin print err ( ␣ ´ ␣ ␣` ); print esc( );

print ( ´ ␣ ␣ ␣ ␣ );

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

(  ␣      ␣   ␣        ␣  ␣          ␣    ␣   ␣  ); error ;

end else begin back input ; cur tok ← par token; back input ; token type ← inserted ;

end; end;

1096. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure end graf ;

begin if mode = hmode then begin if head = tail then pop nest { null paragraphs are ignored } else line break (widow penalty ); normal paragraph; error count ← 0; end;

end;

1097. Insertion and adjustment and mark nodes are constructed by the following pieces of the program. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ any mode(insert ), hmode + vadjust , mmode + vadjust : begin insert or adjust ; any mode(mark ): make mark ;

1098. ⟨ Forbidden cases detected in main control 1048 ⟩ +≡ vmode + vadjust , 1099. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure begin insert or adjust ;

begin if cur cmd = vadjust then cur val ← 255 else begin scan eight bit int ;

if cur val = 255 then begin print err ( ␣ ´ ␣ ); print esc( ); print int (255); help1 ( ´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ); error ; cur val ← 0; end;

end; saved (0) ← cur val ; incr (save ptr ); new save level (insert group); scan left brace; normal paragraph; push nest ; mode ← −vmode; prev depth ← ignore depth; end;

1100. ⟨ Cases of handle right brace where a right brace triggers a delayed action 1085 ⟩ +≡ insert group: begin end graf ; q ← split top skip; add glue ref (q); d ← split max depth;

f ← floating penalty ; unsave; decr (save ptr );

{ now saved (0) is the insertion number, or 255 for vadjust } p ← vpack (link (head ), natural ); pop nest ; if saved (0) < 255 then

begin tail append (get node(ins node size)); type(tail ) ← ins node; subtype(tail ) ← qi (saved (0)); height (tail ) ← height (p) + depth(p); ins ptr (tail ) ← list ptr (p); split top ptr (tail ) ← q; depth(tail ) ← d; float cost (tail ) ← f; end

else begin tail append (get node(small node size)); type(tail ) ← adjust node; subtype(tail ) ← 0; { the subtype is not used } adjust ptr (tail ) ← list ptr (p); delete glue ref (q); end;

free node(p, box node size); if nest ptr = 0 then build page; end;

output group: ⟨ Resume the page builder after an output routine has come to an end 1026 ⟩;

1101. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure make mark ;

var p: pointer ; { new node } begin p ← scan toks (false, true); p ← get node(small node size); type(p) ← mark node; subtype(p) ← 0; { the subtype is not used } mark ptr (p) ← def ref ; link (tail ) ← p; tail ← p; end;

1102. Penalty nodes get into a list via the break penalty command. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ any mode(break penalty ): append penalty ;

1103. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure append penalty ;

begin scan int ; tail append (new penalty (cur val )); if mode = vmode then build page; end; 1104. The remove item command removes a penalty, kern, or glue node if it appears at the tail of the current list, using a brute-force linear scan. Like , this command is not allowed in vertical mode (except internal vertical mode), since the current list in vertical mode is sent to the page builder. But if we happen to be able to implement it in vertical mode, we do. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ any mode(remove item): delete last ;

1105. When delete last is called, cur chr is the type of node that will be deleted, if present. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure delete last ;

label exit ; var p, q: pointer ; { run through the current list }

m: quarterword ; { the length of a replacement list } begin if (mode = vmode) ∧ (tail = head ) then

⟨ Apologize for inability to do the operation now, unless follows non-glue 1106 ⟩ else begin if ¬is char node(tail ) then

if type(tail ) = cur chr then begin q ← head ; repeat p ← q;

if ¬is char node(q) then

if type(q) = disc node then begin for m ← 1 to replace count (q) do p ← link (p); if p = tail then return; end; q ← link (p); until q = tail ; link (p) ← null ; flush node list (tail ); tail ← p; end;

end; exit : end;

1106. ⟨ Apologize for inability to do the operation now, unless follows non-glue 1106 ⟩ ≡

begin if (cur chr ≠ glue node) ∨ (last glue ≠ max halfword ) then

begin you cant ; help2 (  ␣       ␣   ´ ␣    ␣      ␣    ␣   ␣       ␣  )

  (  ␣`                 ´ ␣  );

if cur chr = kern node then help line[0] ← (  ␣`                ´ ␣  )

else if cur chr ≠ glue node then

help line[0] ← ( ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ); error ; end;

end

This code is used in section 1105.

1107. ⟨ Put each of TEX’s primitives into the hash table 226 ⟩ +≡ primitive( , remove item, penalty node); primitive( , remove item, kern node); primitive( , remove item, glue node); primitive( , un hbox , box code); primitive( , un hbox , copy code); primitive( , un vbox , box code); primitive( , un vbox , copy code); 1108. ⟨ Cases of print cmd chr for symbolic printing of primitives 227 ⟩ +≡ remove item: if chr code = glue node then print esc( )

else if chr code = kern node then print esc( )

else print esc( ); un hbox : if chr code = copy code then print esc( )

else print esc( ); un vbox : if chr code = copy code then print esc( )

else print esc( );

1109. The un hbox and un vbox commands unwrap one of the 256 current boxes. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ vmode + un vbox , hmode + un hbox , mmode + un hbox : unpackage;

1110. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure unpackage;

label exit ;

var p: pointer ; { the box }

c: box code . . copy code; { should we copy? } begin c ← cur chr ; scan eight bit int ; p ← box (cur val ); if p = null then return; if (abs (mode) = mmode) ∨ ((abs (mode) = vmode) ∧ (type(p) ≠ vlist node)) ∨

((abs (mode) = hmode) ∧ (type(p) ≠ hlist node)) then

begin print err (  ␣    ␣   ´ ␣  ␣  );
help3 (  ␣        ␣    ␣      ␣  )

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

error ; return;

end; if c = copy code then link (tail ) ← copy node list (list ptr (p)) else begin link (tail ) ← list ptr (p); box (cur val ) ← null ; free node(p, box node size);

end; while link (tail ) ≠ null do tail ← link (tail );

exit : end;

1111. ⟨ Forbidden cases detected in main control 1048 ⟩ +≡ vmode + ital corr ,

1112. Italic corrections are converted to kern nodes when the ital corr command follows a character. In math mode the same effect is achieved by appending a kern of zero here, since italic corrections are supplied later. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ hmode + ital corr : append italic correction; mmode + ital corr : tail append (new kern(0)); 1113. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure append italic correction;

label exit ;

var p: pointer ; { char node at the tail of the current list }

f: internal font number ; { the font in the char node } begin if tail ≠ head then

begin if is char node(tail ) then p ← tail

else if type(tail ) = ligature node then p ← lig char (tail )

else return; f ← font (p); tail append (new kern(char italic(f)(char info(f)(character (p))))); subtype(tail ) ← explicit ; end;

exit : end;

1114. Discretionary nodes are easy in the common case ‘ ’, but in the general case we must process three braces full of items. ⟨ Put each of TEX’s primitives into the hash table 226 ⟩ +≡

primitive( , discretionary , 1); primitive( , discretionary , 0);

1115. ⟨ Cases of print cmd chr for symbolic printing of primitives 227 ⟩ +≡ discretionary : if chr code = 1 then print esc( ) else print esc( );

1116. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ hmode + discretionary , mmode + discretionary : append discretionary ;

1117. The space factor does not change when we append a discretionary node, but it starts out as 1000 in the subsidiary lists. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure append discretionary ;

var c: integer ; { hyphen character }

begin tail append (new disc);

if cur chr = 1 then begin c ← hyphen char [cur font ]; if c ≥ 0 then

if c < 256 then pre break (tail ) ← new character (cur font , c); end

else begin incr (save ptr ); saved (−1) ← 0; new save level (disc group); scan left brace; push nest ; mode ← −hmode; space factor ← 1000; end;

end;

1118. The three discretionary lists are constructed somewhat as if they were hboxes. A subroutine called build discretionary handles the transitions. (This is sort of fun.) ⟨ Cases of handle right brace where a right brace triggers a delayed action 1085 ⟩ +≡ disc group: build discretionary ; 1119. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure build discretionary ;

label done, exit ; var p, q: pointer ; { for link manipulation }

n: integer ; { length of discretionary list } begin unsave; ⟨ Prune the current list, if necessary, until it contains only char node, kern node, hlist node, vlist node,

rule node, and ligature node items; set n to the length of the list, and set q to the list’s tail 1121 ⟩; p ← link (head ); pop nest ; case saved (−1) of 0: pre break (tail ) ← p; 1: post break (tail ) ← p; 2: ⟨ Attach list p to the current list, and record its length; then finish up and return 1120 ⟩; end; { there are no other cases } incr (saved (−1)); new save level (disc group); scan left brace; push nest ; mode ← −hmode; space factor ← 1000;

exit : end;

1120. ⟨ Attach list p to the current list, and record its length; then finish up and return 1120 ⟩ ≡

begin if (n > 0) ∧ (abs (mode) = mmode) then

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

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

(  ␣  ␣    ␣         ␣ ␣   ␣  ␣      ␣    ␣     ␣  ); flush node list (p); n ← 0;

error ;

end else link (tail ) ← p; if n ≤ max quarterword then replace count (tail ) ← n

else begin print err (  ␣    ␣  ␣   ␣  );

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

end; if n > 0 then tail ← q; decr (save ptr ); return; end

This code is used in section 1119.

1121. During this loop, p = link (q) and there are n items preceding p. ⟨ Prune the current list, if necessary, until it contains only char node, kern node, hlist node, vlist node,

rule node, and ligature node items; set n to the length of the list, and set q to the list’s tail 1121 ⟩ ≡ q ← head ; p ← link (q); n ← 0; while p ≠ null do

begin if ¬is char node(p) then if type(p) > rule node then if type(p) ≠ kern node then

if type(p) ≠ ligature node then

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

error ; begin diagnostic;

print nl ( ␣ ␣ ␣ ␣ ␣ ␣ ); show box (p); end diagnostic(true); flush node list (p); link (q) ← null ; goto done; end;

q ← p; p ← link (q); incr (n);

end; done:

This code is used in section 1119.

1122. We need only one more thing to complete the horizontal mode routines, namely the primitive. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ hmode + accent : make accent ;

1123. The positioning of accents is straightforward but tedious. Given an accent of width a, designed for characters of height x and slant s; and given a character of width w, height h, and slant t: We will shift the accent down by x − h, and we will insert kern nodes that have the effect of centering the accent over the character and shifting the accent to the right by δ = \frac{1}{2}(w − a) + h · t − x · s. If either character is absent from the font, we will simply use the other, without shifting. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure make accent ;

var s, t: real ; { amount of slant } p, q, r: pointer ; { character, box, and kern nodes } f: internal font number ; { relevant font } a, h, x, w, delta: scaled ; { heights and widths, as explained above } i: four quarters ; { character information }

begin scan char num; f ← cur font ; p ← new character (f, cur val ); if p ≠ null then

begin x ← x height (f); s ← slant (f)/float constant (65536);

a ← char width(f)(char info(f)(character (p)));

do assignments ;

⟨ Create a character node q for the next character, but set q ← null if problems arise 1124 ⟩; if q ≠ null then ⟨ Append the accent with appropriate kerns, then set p ← q 1125 ⟩; link (tail ) ← p; tail ← p; space factor ← 1000; end;

end; 1124. ⟨ Create a character node q for the next character, but set q ← null if problems arise 1124 ⟩ ≡

q ← null ; f ← cur font ;

if (cur cmd = letter ) ∨ (cur cmd = other char ) ∨ (cur cmd = char given) then

q ← new character (f, cur chr ) else if cur cmd = char num then

begin scan char num; q ← new character (f, cur val ); end

else back input

This code is used in section 1123.

1125. The kern nodes appended here must be distinguished from other kerns, lest they be wiped away by the hyphenation algorithm or by a previous line break.

The two kerns are computed with (machine-dependent) real arithmetic, but their sum is machine-independent; the net effect is machine-independent, because the user cannot remove these nodes nor access them via . ⟨ Append the accent with appropriate kerns, then set p ← q 1125 ⟩ ≡

begin t ← slant (f)/float constant (65536); i ← char info(f)(character (q)); w ← char width(f)(i); h ← char height (f)(height depth(i)); if h ≠ x then { the accent must be shifted up or down }

begin p ← hpack (p, natural ); shift amount (p) ← x − h; end;

delta ← round ((w − a)/float constant (2) + h ∗ t − x ∗ s); r ← new kern(delta); subtype(r) ← acc kern; link (tail ) ← r; link (r) ← p; tail ← new kern(−a − delta); subtype(tail ) ← acc kern; link (p) ← tail ; p ← q; end

This code is used in section 1123.

1126. When ‘ ’ or ‘ ’ or a tab mark comes through the scanner into main control , it might be that the user has foolishly inserted one of them into something that has nothing to do with alignment. But it is far more likely that a left brace or right brace has been omitted, since get next takes actions appropriate to alignment only when ‘ ’ or ‘ ’ or tab marks occur with align state = 0. The following program attempts to make an appropriate recovery. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ any mode(car ret ), any mode(tab mark ): align error ; any mode(no align): no align error ; any mode(omit ): omit error ; 1127. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure align error ;

begin if abs (align state) > 2 then

⟨ Express consternation over the fact that no alignment is in progress 1128 ⟩ else begin back input ;

if align state < 0 then begin print err ( ␣ ␣ ); incr (align state); cur tok ← left brace token + ; end

else begin print err ( ␣ ␣ ); decr (align state); cur tok ← right brace token + ;

end;

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

(  ␣  ␣  ␣   ␣     ␣    ␣     ␣      ␣  ); ins error ;

end; end;

1128. ⟨ Express consternation over the fact that no alignment is in progress 1128 ⟩ ≡ begin print err ( ␣ ); print cmd chr (cur cmd , cur chr ); if cur tok = tab token + then

begin help6 ( ␣   ´ ␣      ␣   ␣   ␣   ␣     ␣    ␣  ␣   ␣ ␣   ␣  )
(  ␣  ␣   ␣    ␣    ␣  ␣          ␣   ␣      ␣  )
(  ␣    ␣    ␣`    ´ ␣    ␣   ␣  ␣    ␣     ␣  )
( ␣     ␣   ␣     ␣ ␣        ␣         ␣  )
(  ´ ␣        ␣   ␣   ␣    ␣     ␣         ␣   ␣  )
(  ␣   ␣      ␣`  ´ ␣   ␣    ␣  ␣   ␣    ␣  ␣  );

end else begin help5 ( ␣ ´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ )

( ␣   ␣  ␣     ␣    ␣    ␣  ␣         ␣    ␣ ␣     ␣  )
( ␣     ␣   ␣     ␣ ␣        ␣         ␣  )
(  ´ ␣        ␣   ␣   ␣    ␣     ␣         ␣   ␣  )
(  ␣   ␣      ␣`  ´ ␣   ␣    ␣  ␣   ␣    ␣  ␣  );

end; error ; end

This code is used in section 1127.

1129. The help messages here contain a little white lie, since  and  are allowed also after

‘ ’. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure no align error ;

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

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

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

procedure omit error ;

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

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

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

1130. We’ve now covered most of the abuses of  and  . Let’s take a look at what happens

when they are used correctly. ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ vmode + halign, hmode + valign: init align; mmode + halign: if privileged then

if cur group = math shift group then init align else off save;

vmode + endv , hmode + endv : do endv ;

1131. An align group code is supposed to remain on the save stack during an entire alignment, until fin align removes it. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure do endv ;

begin if cur group = align group then begin end graf ; if fin col then fin row ; end

else off save;

end;

1132. ⟨ Cases of handle right brace where a right brace triggers a delayed action 1085 ⟩ +≡ align group: begin back input ; cur tok ← cs token flag + frozen cr ; print err ( ␣ );

print esc( ); print ( ␣ );

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

1133. ⟨ Cases of handle right brace where a right brace triggers a delayed action 1085 ⟩ +≡ no align group: begin end graf ; unsave; align peek ;

end;

1134. Finally, is not supposed to get through to main control . ⟨ Cases of main control that build boxes and lists 1056 ⟩ +≡ any mode(end cs name): cs error ;

1135. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ procedure cs error ;

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

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