diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/nesting.w')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/nesting.w | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/nesting.w b/Build/source/texk/web2c/luatexdir/tex/nesting.w new file mode 100644 index 00000000000..28ae086d461 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/nesting.w @@ -0,0 +1,447 @@ +% nesting.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: nesting.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/nesting.w $"; + + +@ these are for |show_activities| +@c +#define page_goal page_so_far[0] +#define pdf_ignored_dimen dimen_par(pdf_ignored_dimen_code) +#define count(A) eqtb[count_base+(A)].cint + + +@ \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 +\.{\\vbox} occurs inside of an \.{\\hbox}, \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: + +\yskip\hang|vmode| stands for vertical mode (the page builder); + +\hang|hmode| stands for horizontal mode (the paragraph builder); + +\hang|mmode| stands for displayed formula mode; + +\hang|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox}); + +\hang|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox}); + +\hang|-mmode| stands for math formula mode (not displayed). + +\yskip\noindent The mode is temporarily set to zero while processing \.{\\write} +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. + +@c +const char *string_mode(int m) +{ /* prints the mode represented by |m| */ + if (m > 0) { + switch (m / (max_command_cmd + 1)) { + case 0: + return "vertical mode"; + break; + case 1: + return "horizontal mode"; + break; + case 2: + return "display math mode"; + break; + default: + break; + } + } else if (m == 0) { + return "no mode"; + } else { + switch ((-m) / (max_command_cmd + 1)) { + case 0: + return "internal vertical mode"; + break; + case 1: + return "restricted horizontal mode"; + break; + case 2: + return "math mode"; + break; + default: + break; + } + } + return "unknown mode"; +} + +@ @c +void print_mode(int m) +{ /* prints the mode represented by |m| */ + tprint(string_mode(m)); +} + +@ @c +void print_in_mode(int m) +{ /* prints the mode represented by |m| */ + tprint("' in "); + tprint(string_mode(m)); +} + +@ @c +int get_mode_id(void) +{ /* returns the mode represented by |m| */ + int m = cur_list.mode_field; + if (m > 0) { + switch (m / (max_command_cmd + 1)) { + case 0: + return 'v'; + break; + case 1: + return 'h'; + break; + case 2: + return 'm'; + break; + default: + return '\0'; + break; + } + } else if (m == 0) { + return 'n';; + } else { + switch ((-m) / (max_command_cmd + 1)) { + case 0: + return 'V'; + break; + case 1: + return 'H'; + break; + case 2: + return 'M'; + break; + default: + return '\0'; + break; + } + } +} + + +@ The state of affairs at any semantic level can be represented by +five values: + +\yskip\hang|mode| is the number representing the semantic mode, as +just explained. + +\yskip\hang|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. + +\yskip\hang|tail| is a |pointer| to the final node of the list being +built; thus, |tail=head| if and only if the list is empty. + +\yskip\hang|prev_graf| is the number of lines of the current paragraph that +have already been put into the present vertical list. + +\yskip\hang|aux| is an auxiliary |memory_word| that gives further information +that is needed to characterize the situation. + +\yskip\noindent +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 |<=-1000|pt 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|; it holds the current space factor used in +spacing calculations. 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. + +A seventh quantity, |eTeX_aux|, is used by the extended features eTeX. +In math mode it is known as |delim_ptr| and points to the most +recent |fence_noad| of a |math_left_group|. + +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 struct that is ready to +be pushed onto |nest| if necessary. + +The math field is used by various bits and pieces in |texmath.w| + +@ This implementation of +\TeX\ uses two different conventions for representing sequential stacks. +@^stack conventions@>@^conventions for representing stacks@> + +\yskip\hang 1) If there is frequent access to the top entry, and if the +stack is essentially never empty, then the top entry is kept in a global +variable (even better would be a machine register), and the other entries +appear in the array $\\{stack}[0\to(\\{ptr}-1)]$. The +semantic stack is handled this way. + +\yskip\hang 2) If there is infrequent top access, the entire stack contents +are in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack| +is treated this way, as we have seen. + +@c +list_state_record *nest; +int nest_ptr; /* first unused location of |nest| */ +int max_nest_stack; /* maximum of |nest_ptr| when pushing */ +int shown_mode; /* most recent mode shown by \.{\\tracingcommands} */ +halfword save_tail; /* save |tail| so we can examine whether we have an auto + kern before a glue */ + +@ 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. + +@c +void initialize_nesting(void) +{ + nest_ptr = 0; + max_nest_stack = 0; + shown_mode = 0; + cur_list.mode_field = vmode; + cur_list.head_field = contrib_head; + cur_list.tail_field = contrib_head; + cur_list.eTeX_aux_field = null; + cur_list.prev_depth_field = ignore_depth; + cur_list.space_factor_field = 1000; + cur_list.incompleat_noad_field = null; + cur_list.ml_field = 0; + cur_list.pg_field = 0; + cur_list.dirs_field = null; + init_math_fields(); +} + + + +@ Here is a common way to make the current list grow: + +@c +void tail_append(halfword p) +{ + couple_nodes(cur_list.tail_field, p); + cur_list.tail_field = vlink(cur_list.tail_field); +} + + +@ @c +halfword pop_tail(void) +{ + halfword n, r; + if (cur_list.tail_field != cur_list.head_field) { + r = cur_list.tail_field; + if (vlink(alink(cur_list.tail_field)) == cur_list.tail_field) { + n = alink(cur_list.tail_field); + } else { + n = cur_list.head_field; + while (vlink(n) != cur_list.tail_field) + n = vlink(n); + } + flush_node(cur_list.tail_field); + cur_list.tail_field = n; + vlink(n) = null; + return r; + } else { + return null; + } +} + +@ 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|. + +@c +void push_nest(void) +{ /* enter a new semantic level, save the old */ + if (nest_ptr > max_nest_stack) { + max_nest_stack = nest_ptr; + if (nest_ptr == nest_size) + overflow("semantic nest size", (unsigned) nest_size); + } + incr(nest_ptr); + cur_list.mode_field = nest[nest_ptr - 1].mode_field; + cur_list.head_field = new_node(temp_node, 0); + cur_list.tail_field = cur_list.head_field; + cur_list.eTeX_aux_field = null; + cur_list.ml_field = line; + cur_list.pg_field = 0; + cur_list.dirs_field = null; + cur_list.prev_depth_field = nest[nest_ptr - 1].prev_depth_field; + cur_list.space_factor_field = nest[nest_ptr - 1].space_factor_field; + cur_list.incompleat_noad_field = nest[nest_ptr - 1].incompleat_noad_field; + init_math_fields(); +} + + +@ 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. + +@c +void pop_nest(void) +{ /* leave a semantic level, re-enter the old */ + flush_node(cur_list.head_field); + decr(nest_ptr); +} + +@ Here is a procedure that displays what \TeX\ is working on, at all levels. + +@c +void show_activities(void) +{ + int p; /* index into |nest| */ + int m; /* mode */ + halfword q, r; /* for showing the current page */ + int t; /* ditto */ + tprint_nl(""); + print_ln(); + for (p = nest_ptr; p >= 0; p--) { + m = nest[p].mode_field; + tprint_nl("### "); + print_mode(m); + tprint(" entered at line "); + print_int(abs(nest[p].ml_field)); + /* we dont do this any more */ +#if 0 + + if (m == hmode) + if (nest[p].pg_field != 040600000) { + tprint(" (language"); + print_int(nest[p].pg_field % 0200000); + tprint(":hyphenmin"); + print_int(nest[p].pg_field / 020000000); + print_char(','); + print_int((nest[p].pg_field / 0200000) % 0100); + print_char(')'); + } +#endif + if (nest[p].ml_field < 0) + tprint(" (\\output routine)"); + if (p == 0) { + /* Show the status of the current page */ + if (page_head != page_tail) { + tprint_nl("### current page:"); + if (output_active) + tprint(" (held over for next output)"); + show_box(vlink(page_head)); + if (page_contents > empty) { + tprint_nl("total height "); + print_totals(); + tprint_nl(" goal height "); + print_scaled(page_goal); + r = vlink(page_ins_head); + while (r != page_ins_head) { + print_ln(); + tprint_esc("insert"); + t = subtype(r); + print_int(t); + tprint(" adds "); + if (count(t) == 1000) + t = height(r); + else + t = x_over_n(height(r), 1000) * count(t); + print_scaled(t); + if (type(r) == split_up_node) { + q = page_head; + t = 0; + do { + q = vlink(q); + if ((type(q) == ins_node) + && (subtype(q) == subtype(r))) + incr(t); + } while (q != broken_ins(r)); + tprint(", #"); + print_int(t); + tprint(" might split"); + } + r = vlink(r); + } + } + } + if (vlink(contrib_head) != null) + tprint_nl("### recent contributions:"); + } + show_box(vlink(nest[p].head_field)); + /* Show the auxiliary field, |a| */ + switch (abs(m) / (max_command_cmd + 1)) { + case 0: + tprint_nl("prevdepth "); + if (nest[p].prev_depth_field <= pdf_ignored_dimen) + tprint("ignored"); + else + print_scaled(nest[p].prev_depth_field); + if (nest[p].pg_field != 0) { + tprint(", prevgraf "); + print_int(nest[p].pg_field); + if (nest[p].pg_field != 1) + tprint(" lines"); + else + tprint(" line"); + } + break; + case 1: + tprint_nl("spacefactor "); + print_int(nest[p].space_factor_field); + /* we dont do this any more, this was aux.rh originally */ +#if 0 + if (m > 0) { + if (nest[p].current_language_field > 0) { + tprint(", current language "); + print_int(nest[p].current_language_field); + } + } +#endif + break; + case 2: + if (nest[p].incompleat_noad_field != null) { + tprint("this will be denominator of:"); + show_box(nest[p].incompleat_noad_field); + } + } /* there are no other cases */ + + } +} |