1% nesting.w 2% 3% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> 4% 5% This file is part of LuaTeX. 6% 7% LuaTeX is free software; you can redistribute it and/or modify it under 8% the terms of the GNU General Public License as published by the Free 9% Software Foundation; either version 2 of the License, or (at your 10% option) any later version. 11% 12% LuaTeX is distributed in the hope that it will be useful, but WITHOUT 13% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15% License for more details. 16% 17% You should have received a copy of the GNU General Public License along 18% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. 19 20@ @c 21 22 23#include "ptexlib.h" 24 25@ these are for |show_activities| 26@c 27#define page_goal page_so_far[0] 28#define pdf_ignored_dimen dimen_par(pdf_ignored_dimen_code) 29#define count(A) eqtb[count_base+(A)].cint 30 31 32@ \TeX\ is typically in the midst of building many lists at once. For example, 33when a math formula is being processed, \TeX\ is in math mode and 34working on an mlist; this formula has temporarily interrupted \TeX\ from 35being in horizontal mode and building the hlist of a paragraph; and this 36paragraph has temporarily interrupted \TeX\ from being in vertical mode 37and building the vlist for the next page of a document. Similarly, when a 38\.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is temporarily 39interrupted from working in restricted horizontal mode, and it enters 40internal vertical mode. The ``semantic nest'' is a stack that 41keeps track of what lists and modes are currently suspended. 42 43At each level of processing we are in one of six modes: 44 45\yskip\hang|vmode| stands for vertical mode (the page builder); 46 47\hang|hmode| stands for horizontal mode (the paragraph builder); 48 49\hang|mmode| stands for displayed formula mode; 50 51\hang|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox}); 52 53\hang|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox}); 54 55\hang|-mmode| stands for math formula mode (not displayed). 56 57\yskip\noindent The mode is temporarily set to zero while processing \.{\\write} 58texts in the |ship_out| routine. 59 60Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that 61\TeX's ``big semantic switch'' can select the appropriate thing to 62do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current 63mode and |cur_cmd| is the current command code. 64 65@c 66static const char *string_mode(int m) 67{ /* prints the mode represented by |m| */ 68 if (m > 0) { 69 switch (m / (max_command_cmd + 1)) { 70 case 0: 71 return "vertical mode"; 72 break; 73 case 1: 74 return "horizontal mode"; 75 break; 76 case 2: 77 return "display math mode"; 78 break; 79 default: 80 break; 81 } 82 } else if (m == 0) { 83 return "no mode"; 84 } else { 85 switch ((-m) / (max_command_cmd + 1)) { 86 case 0: 87 return "internal vertical mode"; 88 break; 89 case 1: 90 return "restricted horizontal mode"; 91 break; 92 case 2: 93 return "math mode"; 94 break; 95 default: 96 break; 97 } 98 } 99 return "unknown mode"; 100} 101 102@ @c 103void print_mode(int m) 104{ /* prints the mode represented by |m| */ 105 tprint(string_mode(m)); 106} 107 108@ @c 109void print_in_mode(int m) 110{ /* prints the mode represented by |m| */ 111 tprint("' in "); 112 tprint(string_mode(m)); 113} 114 115@ @c 116int get_mode_id(void) 117{ /* returns the mode represented by |m| */ 118 int m = cur_list.mode_field; 119 if (m > 0) { 120 switch (m / (max_command_cmd + 1)) { 121 case 0: 122 return 'v'; 123 break; 124 case 1: 125 return 'h'; 126 break; 127 case 2: 128 return 'm'; 129 break; 130 default: 131 return '\0'; 132 break; 133 } 134 } else if (m == 0) { 135 return 'n';; 136 } else { 137 switch ((-m) / (max_command_cmd + 1)) { 138 case 0: 139 return 'V'; 140 break; 141 case 1: 142 return 'H'; 143 break; 144 case 2: 145 return 'M'; 146 break; 147 default: 148 return '\0'; 149 break; 150 } 151 } 152} 153 154 155@ The state of affairs at any semantic level can be represented by 156five values: 157 158\yskip\hang|mode| is the number representing the semantic mode, as 159just explained. 160 161\yskip\hang|head| is a |pointer| to a list head for the list being built; 162|link(head)| therefore points to the first element of the list, or 163to |null| if the list is empty. 164 165\yskip\hang|tail| is a |pointer| to the final node of the list being 166built; thus, |tail=head| if and only if the list is empty. 167 168\yskip\hang|prev_graf| is the number of lines of the current paragraph that 169have already been put into the present vertical list. 170 171\yskip\hang|aux| is an auxiliary |memory_word| that gives further information 172that is needed to characterize the situation. 173 174\yskip\noindent 175In vertical mode, |aux| is also known as |prev_depth|; it is the scaled 176value representing the depth of the previous box, for use in baseline 177calculations, or it is |<=-1000|pt if the next box on the vertical list is to 178be exempt from baseline calculations. In horizontal mode, |aux| is also 179known as |space_factor|; it holds the current space factor used in 180spacing calculations. In math mode, |aux| is also known as |incompleat_noad|; 181if not |null|, it points to a record that represents the numerator of a 182generalized fraction for which the denominator is currently being formed 183in the current list. 184 185There is also a sixth quantity, |mode_line|, which correlates 186the semantic nest with the user's input; |mode_line| contains the source 187line number at which the current level of nesting was entered. The negative 188of this line number is the |mode_line| at the level of the 189user's output routine. 190 191A seventh quantity, |eTeX_aux|, is used by the extended features eTeX. 192In math mode it is known as |delim_ptr| and points to the most 193recent |fence_noad| of a |math_left_group|. 194 195In horizontal mode, the |prev_graf| field is used for initial language data. 196 197The semantic nest is an array called |nest| that holds the |mode|, |head|, 198|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels 199below the currently active one. Information about the currently active 200level is kept in the global quantities |mode|, |head|, |tail|, |prev_graf|, 201|aux|, and |mode_line|, which live in a struct that is ready to 202be pushed onto |nest| if necessary. 203 204The math field is used by various bits and pieces in |texmath.w| 205 206@ This implementation of 207\TeX\ uses two different conventions for representing sequential stacks. 208@^stack conventions@>@^conventions for representing stacks@> 209 210\yskip\hang 1) If there is frequent access to the top entry, and if the 211stack is essentially never empty, then the top entry is kept in a global 212variable (even better would be a machine register), and the other entries 213appear in the array $\\{stack}[0\to(\\{ptr}-1)]$. The 214semantic stack is handled this way. 215 216\yskip\hang 2) If there is infrequent top access, the entire stack contents 217are in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack| 218is treated this way, as we have seen. 219 220@c 221list_state_record *nest; 222int nest_ptr; /* first unused location of |nest| */ 223int max_nest_stack; /* maximum of |nest_ptr| when pushing */ 224int shown_mode; /* most recent mode shown by \.{\\tracingcommands} */ 225halfword save_tail; /* save |tail| so we can examine whether we have an auto 226 kern before a glue */ 227 228@ We will see later that the vertical list at the bottom semantic level is split 229into two parts; the ``current page'' runs from |page_head| to |page_tail|, 230and the ``contribution list'' runs from |contrib_head| to |tail| of 231semantic level zero. The idea is that contributions are first formed in 232vertical mode, then ``contributed'' to the current page (during which time 233the page-breaking decisions are made). For now, we don't need to know 234any more details about the page-building process. 235 236@c 237void initialize_nesting(void) 238{ 239 nest_ptr = 0; 240 max_nest_stack = 0; 241 shown_mode = 0; 242 cur_list.mode_field = vmode; 243 cur_list.head_field = contrib_head; 244 cur_list.tail_field = contrib_head; 245 cur_list.eTeX_aux_field = null; 246 cur_list.prev_depth_field = ignore_depth; 247 cur_list.space_factor_field = 1000; 248 cur_list.incompleat_noad_field = null; 249 cur_list.ml_field = 0; 250 cur_list.pg_field = 0; 251 cur_list.dirs_field = null; 252 init_math_fields(); 253} 254 255 256 257@ Here is a common way to make the current list grow: 258 259@c 260void tail_append(halfword p) 261{ 262 couple_nodes(cur_list.tail_field, p); 263 cur_list.tail_field = vlink(cur_list.tail_field); 264} 265 266 267@ @c 268halfword pop_tail(void) 269{ 270 halfword n, r; 271 if (cur_list.tail_field != cur_list.head_field) { 272 r = cur_list.tail_field; 273 if (vlink(alink(cur_list.tail_field)) == cur_list.tail_field) { 274 n = alink(cur_list.tail_field); 275 } else { 276 n = cur_list.head_field; 277 while (vlink(n) != cur_list.tail_field) 278 n = vlink(n); 279 } 280 flush_node(cur_list.tail_field); 281 cur_list.tail_field = n; 282 vlink(n) = null; 283 return r; 284 } else { 285 return null; 286 } 287} 288 289@ When \TeX's work on one level is interrupted, the state is saved by 290calling |push_nest|. This routine changes |head| and |tail| so that 291a new (empty) list is begun; it does not change |mode| or |aux|. 292 293@c 294void push_nest(void) 295{ /* enter a new semantic level, save the old */ 296 if (nest_ptr > max_nest_stack) { 297 max_nest_stack = nest_ptr; 298 if (nest_ptr == nest_size) 299 overflow("semantic nest size", (unsigned) nest_size); 300 } 301 incr(nest_ptr); 302 cur_list.mode_field = nest[nest_ptr - 1].mode_field; 303 cur_list.head_field = new_node(temp_node, 0); 304 cur_list.tail_field = cur_list.head_field; 305 cur_list.eTeX_aux_field = null; 306 cur_list.ml_field = line; 307 cur_list.pg_field = 0; 308 cur_list.dirs_field = null; 309 cur_list.prev_depth_field = nest[nest_ptr - 1].prev_depth_field; 310 cur_list.space_factor_field = nest[nest_ptr - 1].space_factor_field; 311 cur_list.incompleat_noad_field = nest[nest_ptr - 1].incompleat_noad_field; 312 init_math_fields(); 313} 314 315 316@ Conversely, when \TeX\ is finished on the current level, the former 317state is restored by calling |pop_nest|. This routine will never be 318called at the lowest semantic level, nor will it be called unless |head| 319is a node that should be returned to free memory. 320 321@c 322void pop_nest(void) 323{ /* leave a semantic level, re-enter the old */ 324 flush_node(cur_list.head_field); 325 decr(nest_ptr); 326} 327 328@ Here is a procedure that displays what \TeX\ is working on, at all levels. 329 330@c 331void show_activities(void) 332{ 333 int p; /* index into |nest| */ 334 int m; /* mode */ 335 halfword q, r; /* for showing the current page */ 336 int t; /* ditto */ 337 tprint_nl(""); 338 print_ln(); 339 for (p = nest_ptr; p >= 0; p--) { 340 m = nest[p].mode_field; 341 tprint_nl("### "); 342 print_mode(m); 343 tprint(" entered at line "); 344 print_int(abs(nest[p].ml_field)); 345 /* we dont do this any more */ 346#if 0 347 348 if (m == hmode) 349 if (nest[p].pg_field != 040600000) { 350 tprint(" (language"); 351 print_int(nest[p].pg_field % 0200000); 352 tprint(":hyphenmin"); 353 print_int(nest[p].pg_field / 020000000); 354 print_char(','); 355 print_int((nest[p].pg_field / 0200000) % 0100); 356 print_char(')'); 357 } 358#endif 359 if (nest[p].ml_field < 0) 360 tprint(" (\\output routine)"); 361 if (p == 0) { 362 /* Show the status of the current page */ 363 if (page_head != page_tail) { 364 tprint_nl("### current page:"); 365 if (output_active) 366 tprint(" (held over for next output)"); 367 show_box(vlink(page_head)); 368 if (page_contents > empty) { 369 tprint_nl("total height "); 370 print_totals(); 371 tprint_nl(" goal height "); 372 print_scaled(page_goal); 373 r = vlink(page_ins_head); 374 while (r != page_ins_head) { 375 print_ln(); 376 tprint_esc("insert"); 377 t = subtype(r); 378 print_int(t); 379 tprint(" adds "); 380 if (count(t) == 1000) 381 t = height(r); 382 else 383 t = x_over_n(height(r), 1000) * count(t); 384 print_scaled(t); 385 if (type(r) == split_up_node) { 386 q = page_head; 387 t = 0; 388 do { 389 q = vlink(q); 390 if ((type(q) == ins_node) 391 && (subtype(q) == subtype(r))) 392 incr(t); 393 } while (q != broken_ins(r)); 394 tprint(", #"); 395 print_int(t); 396 tprint(" might split"); 397 } 398 r = vlink(r); 399 } 400 } 401 } 402 if (vlink(contrib_head) != null) 403 tprint_nl("### recent contributions:"); 404 } 405 show_box(vlink(nest[p].head_field)); 406 /* Show the auxiliary field, |a| */ 407 switch (abs(m) / (max_command_cmd + 1)) { 408 case 0: 409 tprint_nl("prevdepth "); 410 if (nest[p].prev_depth_field <= pdf_ignored_dimen) 411 tprint("ignored"); 412 else 413 print_scaled(nest[p].prev_depth_field); 414 if (nest[p].pg_field != 0) { 415 tprint(", prevgraf "); 416 print_int(nest[p].pg_field); 417 if (nest[p].pg_field != 1) 418 tprint(" lines"); 419 else 420 tprint(" line"); 421 } 422 break; 423 case 1: 424 tprint_nl("spacefactor "); 425 print_int(nest[p].space_factor_field); 426 /* we dont do this any more, this was aux.rh originally */ 427#if 0 428 if (m > 0) { 429 if (nest[p].current_language_field > 0) { 430 tprint(", current language "); 431 print_int(nest[p].current_language_field); 432 } 433 } 434#endif 435 break; 436 case 2: 437 if (nest[p].incompleat_noad_field != null) { 438 tprint("this will be denominator of:"); 439 show_box(nest[p].incompleat_noad_field); 440 } 441 } /* there are no other cases */ 442 443 } 444} 445