texnesting.c /size: 16 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex These are for |show_activities|: */
8
9# define page_goal lmt_page_builder_state.goal
10
11/*tex
12
13    \TEX\ is typically in the midst of building many lists at once. For example, when a math formula
14    is being processed, \TEX\ is in math mode and working on an mlist; this formula has temporarily
15    interrupted \TEX\ from being in horizontal mode and building the hlist of a paragraph; and this
16    paragraph has temporarily interrupted \TEX\ from being in vertical mode and building the vlist
17    for the next page of a document. Similarly, when a |\vbox| occurs inside of an |\hbox|, \TEX\ is
18    temporarily interrupted from working in restricted horizontal mode, and it enters internal
19    vertical mode. The \quote {semantic nest} is a stack that keeps track of what lists and modes
20    are currently suspended.
21
22    At each level of processing we are in one of six modes:
23
24    \startitemize[n]
25        \startitem
26            |vmode| stands for vertical mode (the page builder);
27        \stopitem
28        \startitem
29            |hmode| stands for horizontal mode (the paragraph builder);
30        \stopitem
31        \startitem
32            |mmode| stands for displayed formula mode;
33        \stopitem
34        \startitem
35            |-vmode| stands for internal vertical mode (e.g., in a |\vbox|);
36        \stopitem
37        \startitem
38            |-hmode| stands for restricted horizontal mode (e.g., in an |\hbox|);
39        \stopitem
40        \startitem
41            |-mmode| stands for math formula mode (not displayed).
42        \stopitem
43    \stopitemize
44
45    The mode is temporarily set to zero while processing |\write| texts in the |ship_out| routine.
46
47    Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that \TEX's \quote {big semantic
48    switch} can select the appropriate thing to do by computing the value |abs(mode) + cur_cmd|,
49    where |mode| is the current mode and |cur_cmd| is the current command code.
50
51    Per end December 2022 we no longer use the larg emode numbers that also encode the command at 
52    hand. That code is in the archive. 
53
54*/
55
56const char *tex_string_mode(int m)
57{
58    switch (m) {
59        case nomode          : return "no mode";
60        case vmode           : return "vertical mode";
61        case hmode           : return "horizontal mode";
62        case mmode           : return "display math mode";
63        case internal_vmode  : return "internal vertical mode";
64        case restricted_hmode: return "restricted horizontal mode";
65        case inline_mmode    : return "inline math mode";
66        default              : return "unknown mode";
67    }
68}
69
70/*tex
71
72    The state of affairs at any semantic level can be represented by five values:
73
74    \startitemize
75        \startitem
76            |mode| is the number representing the semantic mode, as just explained.
77        \stopitem
78        \startitem
79            |head| is a |pointer| to a list head for the list being built; |link(head)| therefore
80            points to the first element of the list, or to |null| if the list is empty.
81        \stopitem
82        \startitem
83            |tail| is a |pointer| to the final node of the list being built; thus, |tail=head| if
84            and only if the list is empty.
85        \stopitem
86        \startitem
87            |prev_graf| is the number of lines of the current paragraph that have already been put
88            into the present vertical list.
89        \stopitem
90        \startitem
91            |aux| is an auxiliary |memoryword| that gives further information that is needed to
92            characterize the situation.
93        \stopitem
94    \stopitemize
95
96    In vertical mode, |aux| is also known as |prev_depth|; it is the scaled value representing the
97    depth of the previous box, for use in baseline calculations, or it is |<= -1000pt| if the next
98    box on the vertical list is to be exempt from baseline calculations. In horizontal mode, |aux|
99    is also known as |space_factor|; it holds the current space factor used in spacing calculations.
100    In math mode, |aux| is also known as |incompleat_noad|; if not |null|, it points to a record
101    that represents the numerator of a generalized fraction for which the denominator is currently
102    being formed in the current list.
103
104    There is also a sixth quantity, |mode_line|, which correlates the semantic nest with the
105    user's input; |mode_line| contains the source line number at which the current level of nesting
106    was entered. The negative of this line number is the |mode_line| at the level of the user's
107    output routine.
108
109    A seventh quantity, |eTeX_aux|, is used by the extended features eTeX. In math mode it is known
110    as |delim_ptr| and points to the most recent |fence_noad| of a |math_left_group|.
111
112    In horizontal mode, the |prev_graf| field is used for initial language data.
113
114    The semantic nest is an array called |nest| that holds the |mode|, |head|, |tail|, |prev_graf|,
115    |aux|, and |mode_line| values for all semantic levels below the currently active one.
116    Information about the currently active level is kept in the global quantities |mode|, |head|,
117    |tail|, |prev_graf|, |aux|, and |mode_line|, which live in a struct that is ready to be pushed
118    onto |nest| if necessary.
119
120    The math field is used by various bits and pieces in |texmath.w|
121
122    This implementation of \TEX\ uses two different conventions for representing sequential stacks.
123
124    \startitemize[n]
125
126        \startitem
127            If there is frequent access to the top entry, and if the stack is essentially never
128            empty, then the top entry is kept in a global variable (even better would be a machine
129            register), and the other entries appear in the array |stack[0 .. (ptr-1)]|. The semantic
130            stack is handled this way.
131        \stopitem
132
133        \startitem
134            If there is infrequent top access, the entire stack contents are in the array |stack[0
135            .. (ptr - 1)]|. For example, the |save_stack| is treated this way, as we have seen.
136        \stopitem
137
138    \stopitemize
139
140    In |nest_ptr| we have the first unused location of |nest|, and |max_nest_stack| has the maximum
141    of |nest_ptr| when pushing. In |shown_mode| we store the most recent mode shown by
142    |\tracingcommands| and with |save_tail| we can examine whether we have an auto kern before a
143    glue.
144
145*/
146
147nest_state_info lmt_nest_state = {
148    .nest       = NULL,
149    .nest_data  = {
150        .minimum   = min_nest_size,
151        .maximum   = max_nest_size,
152        .size      = siz_nest_size,
153        .step      = stp_nest_size,
154        .allocated = 0,
155        .itemsize  = sizeof(list_state_record),
156        .top       = 0,
157        .ptr       = 0,
158        .initial   = memory_data_unset,
159        .offset    = 0,
160    },
161    .shown_mode = 0,
162    .math_mode  = 0,
163};
164
165/*tex
166
167    We will see later that the vertical list at the bottom semantic level is split into two parts;
168    the \quote {current page} runs from |page_head| to |page_tail|, and the \quote {contribution
169    list} runs from |contribute_head| to |tail| of semantic level zero. The idea is that contributions
170    are first formed in vertical mode, then \quote {contributed} to the current page (during which
171    time the page|-|breaking decisions are made). For now, we don't need to know any more details
172    about the page-building process.
173
174*/
175
176# define reserved_nest_slots 0
177
178void tex_initialize_nest_state(void)
179{
180    int size = lmt_nest_state.nest_data.minimum;
181    lmt_nest_state.nest = aux_allocate_clear_array(sizeof(list_state_record), size, reserved_nest_slots);
182    if (lmt_nest_state.nest) {
183        lmt_nest_state.nest_data.allocated = size;
184    } else {
185        tex_overflow_error("nest",  size);
186    }
187}
188
189static int tex_aux_room_on_nest_stack(void) /* quite similar to save_stack checker so maybe share */
190{
191    int top = lmt_nest_state.nest_data.ptr;
192    if (top > lmt_nest_state.nest_data.top) {
193        lmt_nest_state.nest_data.top = top;
194        if (top > lmt_nest_state.nest_data.allocated) {
195            list_state_record *tmp = NULL;
196            top = lmt_nest_state.nest_data.allocated + lmt_nest_state.nest_data.step;
197            if (top > lmt_nest_state.nest_data.size) {
198                top = lmt_nest_state.nest_data.size;
199            }
200            if (top > lmt_nest_state.nest_data.allocated) {
201                lmt_nest_state.nest_data.allocated = top;
202                tmp = aux_reallocate_array(lmt_nest_state.nest, sizeof(list_state_record), top, reserved_nest_slots);
203                lmt_nest_state.nest = tmp;
204            }
205            lmt_run_memory_callback("nest", tmp ? 1 : 0);
206            if (! tmp) {
207                tex_overflow_error("nest", top);
208                return 0;
209            }
210        }
211    }
212    return 1;
213}
214
215void tex_initialize_nesting(void)
216{
217    lmt_nest_state.nest_data.ptr = 0;
218    lmt_nest_state.nest_data.top = 0;
219 // lmt_nest_state.shown_mode = 0;
220 // lmt_nest_state.math_mode = 0;
221    cur_list.mode = vmode;
222    cur_list.head = contribute_head;
223    cur_list.tail = contribute_head;
224    cur_list.delimiter = null;
225    cur_list.prev_graf = 0;
226    cur_list.mode_line = 0;
227    cur_list.prev_depth = ignore_depth; /*tex |ignore_depth_criterion_par| is not yet available! */
228    cur_list.space_factor = default_space_factor;
229    cur_list.incomplete_noad = null;
230    cur_list.direction_stack = null;
231    cur_list.math_dir = 0;
232    cur_list.math_style = -1;
233    cur_list.math_flatten = 1;
234    cur_list.math_begin = unset_noad_class;
235    cur_list.math_end = unset_noad_class;
236    cur_list.math_mode = 0;
237}
238
239halfword tex_pop_tail(void)
240{
241    if (cur_list.tail != cur_list.head) {
242        halfword r = cur_list.tail;
243        halfword n = node_prev(r);
244        if (node_next(n) != r) {
245            n = cur_list.head;
246            while (node_next(n) != r) {
247                n = node_next(n);
248            }
249        }
250        cur_list.tail = n;
251        node_prev(r) = null;
252        node_next(n) = null;
253        return r;
254    } else {
255        return null;
256    }
257}
258
259/*tex
260
261    When \TEX's work on one level is interrupted, the state is saved by calling |push_nest|. This
262    routine changes |head| and |tail| so that a new (empty) list is begun; it does not change
263    |mode| or |aux|.
264
265*/
266
267void tex_push_nest(void)
268{
269    list_state_record *top = &lmt_nest_state.nest[lmt_nest_state.nest_data.ptr];
270    lmt_nest_state.nest_data.ptr += 1;
271 // lmt_nest_state.shown_mode = 0; // needs checking 
272    lmt_nest_state.math_mode = 0;
273    if (tex_aux_room_on_nest_stack()) {
274        cur_list.mode = top->mode;
275        cur_list.head = tex_new_temp_node();
276        cur_list.tail = cur_list.head;
277        cur_list.delimiter = null;
278        cur_list.prev_graf = 0;
279        cur_list.mode_line = lmt_input_state.input_line;
280        cur_list.prev_depth = top->prev_depth;
281        cur_list.space_factor = top->space_factor;
282        cur_list.incomplete_noad = top->incomplete_noad;
283        cur_list.direction_stack = null;
284        cur_list.math_dir = 0;
285        cur_list.math_style = -1;
286        cur_list.math_flatten = 1;
287        cur_list.math_begin = unset_noad_class;
288        cur_list.math_end = unset_noad_class;
289     // cur_list.math_begin = top->math_begin;
290     // cur_list.math_end = top->math_end;
291        cur_list.math_mode = 0;
292    } else {
293        tex_overflow_error("semantic nest size", lmt_nest_state.nest_data.size);
294    }
295}
296
297/*tex
298
299    Conversely, when \TEX\ is finished on the current level, the former state is restored by
300    calling |pop_nest|. This routine will never be called at the lowest semantic level, nor will
301    it be called unless |head| is a node that should be returned to free memory.
302
303*/
304
305void tex_pop_nest(void)
306{
307    if (cur_list.head) {
308        /* tex_free_node(cur_list.head, temp_node_size); */ /* looks fragile */
309        tex_flush_node(cur_list.head);
310        /*tex Just to be sure, in case we access from \LUA: */
311     // cur_list.head = null;
312     // cur_list.tail = null;
313    }
314    --lmt_nest_state.nest_data.ptr;
315}
316
317/*tex Here is a procedure that displays what \TEX\ is working on, at all levels. */
318
319void tex_show_activities(void)
320{
321    tex_print_nlp();
322    for (int p = lmt_nest_state.nest_data.ptr; p >= 0; p--) {
323        list_state_record n = lmt_nest_state.nest[p];
324        tex_print_format("%l[%M entered at line %i%s]", n.mode, abs(n.mode_line), n.mode_line < 0 ? " (output routine)" : ""); // %L
325        if (p == 0) {
326            /*tex Show the status of the current page */
327            if (page_head != lmt_page_builder_state.page_tail) {
328                tex_print_format("%l[current page:%s]", lmt_page_builder_state.output_active ? " (held over for next output)" : "");
329                tex_show_box(node_next(page_head));
330                if (lmt_page_builder_state.contents != contribute_nothing) {
331                    halfword r;
332                    tex_print_format("%l[total height %P, goal height %p]",
333                        page_total, page_stretch, page_filstretch, page_fillstretch, page_filllstretch, page_shrink,
334                        page_goal
335                    );
336                    r = node_next(page_insert_head);
337                    while (r != page_insert_head) {
338                        halfword index = insert_index(r);
339                        halfword multiplier = tex_get_insert_multiplier(index);
340                        halfword size = multiplier == scaling_factor ? insert_total_height(r) : tex_x_over_n(insert_total_height(r), scaling_factor) * multiplier;
341                        if (node_type(r) == split_node && node_subtype(r) == insert_split_subtype) {
342                            halfword q = page_head;
343                            halfword n = 0;
344                            do {
345                                q = node_next(q);
346                                if (node_type(q) == insert_node && split_insert_index(q) == insert_index(r)) {
347                                    ++n;
348                                }
349                            } while (q != split_broken_insert(r));
350                            tex_print_format("%l[insert %i adds %p, might split to %i]", index, size, n);
351                        } else {
352                            tex_print_format("%l[insert %i adds %p]", index, size);
353                        }
354                        r = node_next(r);
355                    }
356                }
357            }
358            if (node_next(contribute_head)) {
359                tex_print_format("%l[recent contributions:]");
360            }
361        }
362        tex_print_format("%l[begin list]");
363        tex_show_box(node_next(n.head));
364        tex_print_format("%l[end list]");
365        /*tex Show the auxiliary field, |a|. */
366        switch (n.mode) {
367            case vmode:
368            case internal_vmode:
369                {
370                    if (n.prev_depth <= ignore_depth_criterion_par) {
371                        tex_print_format("%l[prevdepth ignored");
372                    } else {
373                        tex_print_format("%l[prevdepth %p", n.prev_depth);
374                    }
375                    if (n.prev_graf != 0) {
376                        tex_print_format(", prevgraf %i line%s", n.prev_graf, n.prev_graf == 1 ? "" : "s");
377                    }
378                    tex_print_char(']');
379                    break;
380                }
381            case mmode:
382            case inline_mmode:
383                {
384                    if (n.incomplete_noad) {
385                        tex_print_format("%l[this will be denominator of:]");
386                        tex_print_format("%l[begin list]");
387                        tex_show_box(n.incomplete_noad);
388                        tex_print_format("%l[end list]");
389                    }
390                    break;
391                }
392        }
393    }
394}
395
396int tex_vmode_nest_index(void)
397{
398    int p = lmt_nest_state.nest_data.ptr; /* index into |nest| */
399    while (! is_v_mode(lmt_nest_state.nest[p].mode)) {
400        --p;
401    }
402    return p;
403}
404
405void tex_tail_prepend(halfword n) 
406{
407    tex_couple_nodes(node_prev(cur_list.tail), n);
408    tex_couple_nodes(n, cur_list.tail);
409    if (cur_list.tail == cur_list.head) {
410        cur_list.head = n;
411    }
412}
413
414void tex_tail_append(halfword p)
415{
416    node_next(cur_list.tail) = p;
417    node_prev(p) = cur_list.tail;
418    cur_list.tail = p;
419}
420
421void tex_tail_append_list(halfword p)
422{
423    node_next(cur_list.tail) = p;
424    node_prev(p) = cur_list.tail;
425    cur_list.tail = tex_tail_of_node_list(p);
426}
427
428