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