§211  TEX82  PART 16: THE SEMANTIC NEST 77

211. The semantic nest. TEX is typically in the midst of building many lists at once. For example, when a math formula is being processed, TEX is in math mode and working on an mlist; this formula has temporarily interrupted TEX from being in horizontal mode and building the hlist of a paragraph; and this paragraph has temporarily interrupted TEX from being in vertical mode and building the vlist for the next page of a document. Similarly, when a occurs inside of an , TEX is temporarily interrupted from working in restricted horizontal mode, and it enters internal vertical mode. The “semantic nest” is a stack that keeps track of what lists and modes are currently suspended.

At each level of processing we are in one of six modes: vmode stands for vertical mode (the page builder); hmode stands for horizontal mode (the paragraph builder); mmode stands for displayed formula mode; −vmode stands for internal vertical mode (e.g., in a ); −hmode stands for restricted horizontal mode (e.g., in an ); −mmode stands for math formula mode (not displayed). The mode is temporarily set to zero while processing texts in the ship out routine.

Numeric values are assigned to vmode, hmode, and mmode so that TEX’s “big semantic switch” can select the appropriate thing to do by computing the value abs (mode) + cur cmd , where mode is the current mode and cur cmd is the current command code.

define vmode = 1 { vertical mode }

define hmode = vmode + max command + 1 { horizontal mode }

define mmode = hmode + max command + 1 { math mode } procedure print mode(m : integer ); { prints the mode represented by m }

begin if m > 0 then case m div (max command + 1) of 0: print ( ); 1: print ( ); 2: print ( ␣ ); end

else if m = 0 then print ( )

else case (−m) div (max command + 1) of

0: print (  ␣  );
1: print (  ␣  );

2: print ( );

78  PART 16: THE SEMANTIC NEST  TEX82 §212

end; print ( ␣ ); end; 212. The state of affairs at any semantic level can be represented by five values: mode is the number representing the semantic mode, as just explained. head is a pointer to a list head for the list being built; link (head ) therefore points to the first element of the

list, or to null if the list is empty. tail is a pointer to the final node of the list being built; thus, tail = head if and only if the list is empty. prev graf is the number of lines of the current paragraph that have already been put into the present vertical

list. aux is an auxiliary memory word that gives further information that is needed to characterize the situation. In vertical mode, aux is also known as prev depth; it is the scaled value representing the depth of the previous box, for use in baseline calculations, or it is ≤ −1000pt if the next box on the vertical list is to be exempt from baseline calculations. In horizontal mode, aux is also known as space factor and clang ; it holds the current space factor used in spacing calculations, and the current language used for hyphenation. (The value of clang is undefined in restricted horizontal mode.) In math mode, aux is also known as incompleat noad ; if not null , it points to a record that represents the numerator of a generalized fraction for which the denominator is currently being formed in the current list.

There is also a sixth quantity, mode line, which correlates the semantic nest with the user’s input; mode line contains the source line number at which the current level of nesting was entered. The negative of this line number is the mode line at the level of the user’s output routine.

In horizontal mode, the prev graf field is used for initial language data.

The semantic nest is an array called nest that holds the mode, head , tail , prev graf , aux , and mode line values for all semantic levels below the currently active one. Information about the currently active level is kept in the global quantities mode, head , tail , prev graf , aux , and mode line, which live in a Pascal record that is ready to be pushed onto nest if necessary.

define ignore depth ≡ −65536000 { prev depth value that is ignored } ⟨ Types in the outer block 18 ⟩ +≡

list state record = record mode field : −mmode . . mmode; head field , tail field : pointer ; pg field , ml field : integer ; aux field : memory word ; end;

213. define mode ≡ cur list .mode field { current mode } define head ≡ cur list .head field { header node of current list } define tail ≡ cur list .tail field { final node on current list } define prev graf ≡ cur list .pg field { number of paragraph lines accumulated } define aux ≡ cur list .aux field { auxiliary data about the current list } define prev depth ≡ aux .sc { the name of aux in vertical mode } define space factor ≡ aux .hh.lh { part of aux in horizontal mode } define clang ≡ aux .hh.rh { the other part of aux in horizontal mode } define incompleat noad ≡ aux .int { the name of aux in math mode } define mode line ≡ cur list .ml field { source file line number at beginning of list }

⟨ Global variables 13 ⟩ +≡ nest : array [0 . . nest size] of list state record ; nest ptr : 0 . . nest size; { first unused location of nest } max nest stack : 0 . . nest size; { maximum of nest ptr when pushing } cur list : list state record ; { the “top” semantic state } shown mode: −mmode . . mmode; { most recent mode shown by }

214. Here is a common way to make the current list grow:

define tail append (#) ≡ begin link (tail ) ← #; tail ← link (tail ); end 215. We will see later that the vertical list at the bottom semantic level is split into two parts; the “current page” runs from page head to page tail , and the “contribution list” runs from contrib head to tail of semantic level zero. The idea is that contributions are first formed in vertical mode, then “contributed” to the current page (during which time the page-breaking decisions are made). For now, we don’t need to know any more details about the page-building process. ⟨ Set initial values of key variables 21 ⟩ +≡

nest ptr ← 0; max nest stack ← 0; mode ← vmode; head ← contrib head ; tail ← contrib head ; prev depth ← ignore depth; mode line ← 0; prev graf ← 0; shown mode ← 0; ⟨ Start a new current page 991 ⟩;

216. When TEX’s work on one level is interrupted, the state is saved by calling push nest . This routine changes head and tail so that a new (empty) list is begun; it does not change mode or aux . procedure push nest ; { enter a new semantic level, save the old }

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

nest [nest ptr ] ← cur list ; { stack the record }

incr (nest ptr ); head ← get avail ; tail ← head ; prev graf ← 0; mode line ← line; end;

217. Conversely, when TEX is finished on the current level, the former state is restored by calling pop nest . This routine will never be called at the lowest semantic level, nor will it be called unless head is a node that should be returned to free memory. procedure pop nest ; { leave a semantic level, re-enter the old }

begin free avail (head ); decr (nest ptr ); cur list ← nest [nest ptr ];

end; 218. Here is a procedure that displays what TEX is working on, at all levels. procedure print totals ; forward ; procedure show activities ;

var p: 0 . . nest size; { index into nest } m: −mmode . . mmode; { mode } a: memory word ; { auxiliary } q, r: pointer ; { for showing the current page } t: integer ; { ditto }

begin nest [nest ptr ] ← cur list ; { put the top level into the array } print nl ( ); print ln; for p ← nest ptr downto 0 do

begin m ← nest [p].mode field ; a ← nest [p].aux field ; print nl ( ###␣ ); print mode(m); print ( ␣ ␣ ␣ ␣ ); print int (abs (nest [p].ml field )); if m = hmode then

if nest [p].pg field ≠ ´ 40600000 then begin print ( ␣ ); print int (nest [p].pg field mod ´ 200000 ); print ( ); print int (nest [p].pg field div ´ 20000000 ); print char ( ); print int ((nest [p].pg field div ´ 200000 ) mod ´ 100 ); print char ( ); end;

if nest [p].ml field < 0 then print ( ␣ ␣ ); if p = 0 then

begin ⟨ Show the status of the current page 986 ⟩;

if link (contrib head ) ≠ null then print nl ( ###␣ ␣ ); end;

show box (link (nest [p].head field )); ⟨ Show the auxiliary field, a 219 ⟩; end;

end;

219. ⟨ Show the auxiliary field, a 219 ⟩ ≡ case abs (m) div (max command + 1) of 0: begin print nl ( ␣ );

if a.sc ≤ ignore depth then print ( ) else print scaled (a.sc); if nest [p].pg field ≠ 0 then

begin print ( ␣ ␣ ); print int (nest [p].pg field ); print ( ␣ ); if nest [p].pg field ≠ 1 then print char ( ); end;

end; 1: begin print nl ( ␣ ); print int (a.hh.lh);

if m > 0 then if a.hh.rh > 0 then

begin print ( ␣ ␣ ␣ ); print int (a.hh.rh); end; end;

2: if a.int ≠ null then

begin print ( ␣ ␣ ␣ ␣ ); show box (a.int ); end; end { there are no other cases }

This code is used in section 218.