summaryrefslogtreecommitdiff
path: root/source/luametatex/source/tex/texnesting.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/tex/texnesting.c')
-rw-r--r--source/luametatex/source/tex/texnesting.c432
1 files changed, 432 insertions, 0 deletions
diff --git a/source/luametatex/source/tex/texnesting.c b/source/luametatex/source/tex/texnesting.c
new file mode 100644
index 000000000..d699d58fc
--- /dev/null
+++ b/source/luametatex/source/tex/texnesting.c
@@ -0,0 +1,432 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# include "luametatex.h"
+
+/*tex These are for |show_activities|: */
+
+# define page_goal lmt_page_builder_state.goal
+
+/*tex
+
+ \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 \quote {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:
+
+ \startitemize[n]
+ \startitem
+ |vmode| stands for vertical mode (the page builder);
+ \stopitem
+ \startitem
+ |hmode| stands for horizontal mode (the paragraph builder);
+ \stopitem
+ \startitem
+ |mmode| stands for displayed formula mode;
+ \stopitem
+ \startitem
+ |-vmode| stands for internal vertical mode (e.g., in a |\vbox|);
+ \stopitem
+ \startitem
+ |-hmode| stands for restricted horizontal mode (e.g., in an |\hbox|);
+ \stopitem
+ \startitem
+ |-mmode| stands for math formula mode (not displayed).
+ \stopitem
+ \stopitemize
+
+ 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 \quote {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.
+
+*/
+
+# if main_control_mode == 0
+
+const char *tex_string_mode(int m)
+{
+ if (m > 0) {
+ switch (m / (max_command_cmd + 1)) {
+ case 0: return "vertical mode";
+ case 1: return "horizontal mode";
+ case 2: return "display math mode";
+ }
+ } else if (m == 0) {
+ return "no mode";
+ } else {
+ switch ((-m) / (max_command_cmd + 1)) {
+ case 0: return "internal vertical mode";
+ case 1: return "restricted horizontal mode";
+ case 2: return "math mode";
+ }
+ }
+ return "unknown mode";
+}
+
+# else
+
+const char *tex_string_mode(int m)
+{
+ switch (m) {
+ case nomode: return "no mode";
+ case vmode : return "vertical mode";
+ case hmode : return "horizontal mode";
+ case mmode : return "display math mode";
+ case -vmode : return "internal vertical mode";
+ case -hmode : return "restricted horizontal mode";
+ case -mmode : return "math mode";
+ default : return "unknown mode";
+ }
+}
+
+# endif
+
+/*tex
+
+ The state of affairs at any semantic level can be represented by five values:
+
+ \startitemize
+ \startitem
+ |mode| is the number representing the semantic mode, as just explained.
+ \stopitem
+ \startitem
+ |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.
+ \stopitem
+ \startitem
+ |tail| is a |pointer| to the final node of the list being built; thus, |tail=head| if
+ and only if the list is empty.
+ \stopitem
+ \startitem
+ |prev_graf| is the number of lines of the current paragraph that have already been put
+ into the present vertical list.
+ \stopitem
+ \startitem
+ |aux| is an auxiliary |memoryword| that gives further information that is needed to
+ characterize the situation.
+ \stopitem
+ \stopitemize
+
+ 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|; 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.
+
+ \startitemize[n]
+
+ \startitem
+ 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 .. (ptr-1)]|. The semantic
+ stack is handled this way.
+ \stopitem
+
+ \startitem
+ If there is infrequent top access, the entire stack contents are in the array |stack[0
+ .. (ptr - 1)]|. For example, the |save_stack| is treated this way, as we have seen.
+ \stopitem
+
+ \stopitemize
+
+ In |nest_ptr| we have the first unused location of |nest|, and |max_nest_stack| has the maximum
+ of |nest_ptr| when pushing. In |shown_mode| we store the most recent mode shown by
+ |\tracingcommands| and with |save_tail| we can examine whether we have an auto kern before a
+ glue.
+
+*/
+
+nest_state_info lmt_nest_state = {
+ .nest = NULL,
+ .nest_data = {
+ .minimum = min_nest_size,
+ .maximum = max_nest_size,
+ .size = siz_nest_size,
+ .step = stp_nest_size,
+ .allocated = 0,
+ .itemsize = sizeof(list_state_record),
+ .top = 0,
+ .ptr = 0,
+ .initial = memory_data_unset,
+ .offset = 0,
+ },
+ .shown_mode = 0,
+ .padding = 0,
+};
+
+/*tex
+
+ We will see later that the vertical list at the bottom semantic level is split into two parts;
+ the \quote {current page} runs from |page_head| to |page_tail|, and the \quote {contribution
+ list} runs from |contribute_head| to |tail| of semantic level zero. The idea is that contributions
+ are first formed in vertical mode, then \quote {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.
+
+*/
+
+# define reserved_nest_slots 0
+
+void tex_initialize_nest_state(void)
+{
+ int size = lmt_nest_state.nest_data.minimum;
+ lmt_nest_state.nest = aux_allocate_clear_array(sizeof(list_state_record), size, reserved_nest_slots);
+ if (lmt_nest_state.nest) {
+ lmt_nest_state.nest_data.allocated = size;
+ } else {
+ tex_overflow_error("nest", size);
+ }
+}
+
+static int tex_aux_room_on_nest_stack(void) /* quite similar to save_stack checker so maybe share */
+{
+ int top = lmt_nest_state.nest_data.ptr;
+ if (top > lmt_nest_state.nest_data.top) {
+ lmt_nest_state.nest_data.top = top;
+ if (top > lmt_nest_state.nest_data.allocated) {
+ list_state_record *tmp = NULL;
+ top = lmt_nest_state.nest_data.allocated + lmt_nest_state.nest_data.step;
+ if (top > lmt_nest_state.nest_data.size) {
+ top = lmt_nest_state.nest_data.size;
+ }
+ if (top > lmt_nest_state.nest_data.allocated) {
+ lmt_nest_state.nest_data.allocated = top;
+ tmp = aux_reallocate_array(lmt_nest_state.nest, sizeof(list_state_record), top, reserved_nest_slots);
+ lmt_nest_state.nest = tmp;
+ }
+ lmt_run_memory_callback("nest", tmp ? 1 : 0);
+ if (! tmp) {
+ tex_overflow_error("nest", top);
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+void tex_initialize_nesting(void)
+{
+ lmt_nest_state.nest_data.ptr = 0;
+ lmt_nest_state.nest_data.top = 0;
+ lmt_nest_state.shown_mode = 0;
+ cur_list.mode = vmode;
+ cur_list.head = contribute_head;
+ cur_list.tail = contribute_head;
+ cur_list.delim = null;
+ cur_list.prev_graf = 0;
+ cur_list.mode_line = 0;
+ cur_list.prev_depth = ignore_depth;
+ cur_list.space_factor = 1000;
+ cur_list.incomplete_noad = null;
+ cur_list.direction_stack = null;
+ cur_list.math_dir = 0;
+ cur_list.math_style = -1;
+ cur_list.math_flatten = 1;
+ cur_list.math_begin = unset_noad_class;
+ cur_list.math_end = unset_noad_class;
+ cur_list.math_mode = 0;
+}
+
+halfword tex_pop_tail(void)
+{
+ if (cur_list.tail != cur_list.head) {
+ halfword r = cur_list.tail;
+ halfword n = node_prev(r);
+ if (node_next(n) != r) {
+ n = cur_list.head;
+ while (node_next(n) != r) {
+ n = node_next(n);
+ }
+ }
+ cur_list.tail = n;
+ node_prev(r) = null;
+ node_next(n) = null;
+ return r;
+ } else {
+ return null;
+ }
+}
+
+/*tex
+
+ 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|.
+
+*/
+
+void tex_push_nest(void)
+{
+ list_state_record *top = &lmt_nest_state.nest[lmt_nest_state.nest_data.ptr];
+ lmt_nest_state.nest_data.ptr += 1;
+ if (tex_aux_room_on_nest_stack()) {
+ cur_list.mode = top->mode;
+ cur_list.head = tex_new_temp_node();
+ cur_list.tail = cur_list.head;
+ cur_list.delim = null;
+ cur_list.prev_graf = 0;
+ cur_list.mode_line = lmt_input_state.input_line;
+ cur_list.prev_depth = top->prev_depth;
+ cur_list.space_factor = top->space_factor;
+ cur_list.incomplete_noad = top->incomplete_noad;
+ cur_list.direction_stack = null;
+ cur_list.math_dir = 0;
+ cur_list.math_style = -1;
+ cur_list.math_flatten = 1;
+ cur_list.math_begin = unset_noad_class;
+ cur_list.math_end = unset_noad_class;
+ // cur_list.math_begin = top->math_begin;
+ // cur_list.math_end = top->math_end;
+ cur_list.math_mode = 0;
+ } else {
+ tex_overflow_error("semantic nest size", lmt_nest_state.nest_data.size);
+ }
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void tex_pop_nest(void)
+{
+ if (cur_list.head) {
+ /* tex_free_node(cur_list.head, temp_node_size); */ /* looks fragile */
+ tex_flush_node(cur_list.head);
+ /*tex Just to be sure, in case we access from \LUA: */
+ // cur_list.head = null;
+ // cur_list.tail = null;
+ }
+ --lmt_nest_state.nest_data.ptr;
+}
+
+/*tex Here is a procedure that displays what \TEX\ is working on, at all levels. */
+
+void tex_show_activities(void)
+{
+ tex_print_nlp();
+ for (int p = lmt_nest_state.nest_data.ptr; p >= 0; p--) {
+ list_state_record n = lmt_nest_state.nest[p];
+ tex_print_format("%l[%M entered at line %i%s]", n.mode, abs(n.mode_line), n.mode_line < 0 ? " (output routine)" : ""); // %L
+ if (p == 0) {
+ /*tex Show the status of the current page */
+ if (page_head != lmt_page_builder_state.page_tail) {
+ tex_print_format("%l[current page:%s]", lmt_page_builder_state.output_active ? " (held over for next output)" : "");
+ tex_show_box(node_next(page_head));
+ if (lmt_page_builder_state.contents != contribute_nothing) {
+ halfword r;
+ tex_print_format("%l[total height %P, goal height %D]",
+ page_total, page_stretch, page_filstretch, page_fillstretch, page_filllstretch, page_shrink,
+ page_goal, pt_unit
+ );
+ r = node_next(page_insert_head);
+ while (r != page_insert_head) {
+ halfword index = insert_index(r);
+ halfword multiplier = tex_get_insert_multiplier(index);
+ halfword size = multiplier == 1000 ? insert_total_height(r) : tex_x_over_n(insert_total_height(r), 1000) * multiplier;
+ if (node_type(r) == split_node && node_subtype(r) == insert_split_subtype) {
+ halfword q = page_head;
+ halfword n = 0;
+ do {
+ q = node_next(q);
+ if (node_type(q) == insert_node && split_insert_index(q) == insert_index(r)) {
+ ++n;
+ }
+ } while (q != split_broken_insert(r));
+ tex_print_format("%l[insert %i adds %D, might split to %i]", index, size, pt_unit, n);
+ } else {
+ tex_print_format("%l[insert %i adds %D]", index, size, pt_unit);
+ }
+ r = node_next(r);
+ }
+ }
+ }
+ if (node_next(contribute_head)) {
+ tex_print_format("%l[recent contributions:]");
+ }
+ }
+ tex_print_format("%l[begin list]");
+ tex_show_box(node_next(n.head));
+ tex_print_format("%l[end list]");
+ /*tex Show the auxiliary field, |a|. */
+ switch (abs(n.mode) / (max_command_cmd + 1)) {
+ case 0:
+ {
+ if (n.prev_depth <= ignore_depth) {
+ tex_print_format("%l[prevdepth ignored");
+ } else {
+ tex_print_format("%l[prevdepth %D", n.prev_depth, pt_unit);
+ }
+ if (n.prev_graf != 0) {
+ tex_print_format(", prevgraf %i line%s", n.prev_graf, n.prev_graf == 1 ? "" : "s");
+ }
+ tex_print_char(']');
+ break;
+ }
+ case 1:
+ {
+ break;
+ }
+ case 2:
+ {
+ if (n.incomplete_noad) {
+ tex_print_format("%l[this will be denominator of:]");
+ tex_print_format("%l[begin list]");
+ tex_show_box(n.incomplete_noad);
+ tex_print_format("%l[end list]");
+ }
+ break;
+ }
+ }
+ }
+}
+
+int tex_vmode_nest_index(void)
+{
+ int p = lmt_nest_state.nest_data.ptr; /* index into |nest| */
+ while (abs(lmt_nest_state.nest[p].mode) != vmode) {
+ --p;
+ }
+ return p;
+}
+
+void tex_tail_append(halfword p)
+{
+ node_next(cur_list.tail) = p;
+ node_prev(p) = cur_list.tail;
+ cur_list.tail = p;
+}