texequivalents.c /size: 87 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
8
9    The nested structure provided by |\char'173| \unknown\ |\char'175| groups in \TEX\ means that
10    |eqtb| entries valid in outer groups should be saved and restored later if they are overridden
11    inside the braces. When a new |eqtb| value is being assigned, the program therefore checks to
12    see if the previous entry belongs to an outer level. In such a case, the old value is placed on
13    the |save_stack| just before the new value enters |eqtb|. At the end of a grouping level, i.e.,
14    when the right brace is sensed, the |save_stack| is used to restore the outer values, and the
15    inner ones are destroyed.
16
17    Entries on the |save_stack| are of type |save_record|. The top item on this stack is
18    |save_stack[p]|, where |p=save_ptr-1|; it contains three fields called |save_type|, |save_level|,
19    and |save_value|, and it is interpreted in one of four ways:
20
21    \startitemize[n]
22
23    \startitem
24        If |save_type(p) = restore_old_value|, then |save_value(p)| is a location in |eqtb| whose
25        current value should be destroyed at the end of the current group and replaced by
26        |save_word(p-1)| (|save_type(p-1) == saved_eqtb|). Furthermore if |save_value(p) >= int_base|,
27        then |save_level(p)| should replace the corresponding entry in |xeq_level| (if |save_value(p)
28        < int_base|, then the level is part of |save_word(p-1)|).
29    \stopitem
30
31    \startitem
32        If |save_type(p) = restore_zero|, then |save_value(p)| is a location in |eqtb| whose current
33        value should be destroyed at the end of the current group, when it should be replaced by the
34        current value of |eqtb[undefined_control_sequence]|.
35    \stopitem
36
37    \startitem
38        If |save_type(p) = insert_token|, then |save_value(p)| is a token that should be inserted
39        into \TeX's input when the current group ends.
40    \stopitem
41
42    \startitem
43        If |save_type(p) = level_boundary|, then |save_level(p)| is a code explaining what kind of
44        group we were previously in, and |save_value(p)| points to the level boundary word at the
45        bottom of the entries for that group. Furthermore, |save_value(p-1)| contains the source
46        line number at which the current level of grouping was entered, this field has itself a
47        type: |save_type(p-1) == saved_line|.
48    \stopitem
49
50    \stopitemize
51
52    Besides this \quote {official} use, various subroutines push temporary variables on the save
53    stack when it is handy to do so. These all have an explicit |save_type|, and they are:
54
55    \starttabulate
56        \NC |saved_adjust|     \NC signifies an adjustment is beging scanned \NC\NR
57        \NC |saved_insert|     \NC an insertion is being scanned \NC\NR
58        \NC |saved_disc|       \NC the |\discretionary| sublist we are working on right now \NC\NR
59        \NC |saved_boxtype|    \NC whether a |\localbox| is |\left| or |\right| \NC\NR
60        \NC |saved_textdir|    \NC a text direction to be restored \NC\NR
61        \NC |saved_eqno|       \NC diffentiates between |\eqno| and |\leqno| \NC\NR
62        \NC |saved_choices|    \NC the |\mathchoices| sublist we are working on right now \NC\NR
63        \NC |saved_above|      \NC used for the \LUAMETATEX\ above variants \NC\NR
64        \NC |saved_math|       \NC and interrupted math list \NC\NR
65        \NC |saved_boxcontext| \NC the box context value \NC\NR
66        \NC |saved_boxspec|    \NC the box |to| or |spread| specification \NC\NR
67        \NC |saved_boxdir|     \NC the box |dir| specification \NC\NR
68        \NC |saved_boxattr|    \NC the box |attr| specification \NC\NR
69        \NC |saved_boxpack|    \NC the box |pack| specification \NC\NR
70        \NC |...|              \NC some more in \LUATEX\ and \LUAMETATEX \NC\NR
71    \stoptabulate
72
73    The global variable |cur_group| keeps track of what sort of group we are currently in. Another
74    global variable, |cur_boundary|, points to the topmost |level_boundary| word. And |cur_level|
75    is the current depth of nesting. The routines are designed to preserve the condition that no
76    entry in the |save_stack| or in |eqtb| ever has a level greater than |cur_level|.
77
78*/
79
80save_state_info lmt_save_state = {
81    .save_stack       = NULL,
82    .save_stack_data  = {
83        .minimum   = min_save_size,
84        .maximum   = max_save_size,
85        .size      = siz_save_size,
86        .step      = stp_save_size,
87        .allocated = 0,
88        .itemsize  = sizeof(save_record),
89        .top       = 0,
90        .ptr       = 0,
91        .initial   = memory_data_unset,
92        .offset    = 0,
93    },
94    .current_level    = 0,
95    .current_group    = 0,
96    .current_boundary = 0,
97 // .padding          = 0,
98};
99
100/*tex
101
102    The comments below are (of course) coming from \LUATEX's ancestor and are still valid! However,
103    in \LUATEX\ we use \UTF\ instead of \ASCII, have attributes, have more primites, etc. But the
104    principles remain the same. We are not 100\% compatible in output and will never be.
105
106*/
107
108static void tex_aux_show_eqtb(halfword n);
109
110static void tex_aux_diagnostic_trace(halfword p, const char *s)
111{
112    tex_begin_diagnostic();
113    tex_print_format("{%s ", s);
114    tex_aux_show_eqtb(p);
115    tex_print_char('}');
116    tex_end_diagnostic();
117}
118
119/*tex
120
121    Now that we have studied the data structures for \TEX's semantic routines (in other modules),
122    we ought to consider the data structures used by its syntactic routines. In other words, our
123    next concern will be the tables that \TEX\ looks at when it is scanning what the user has
124    written.
125
126    The biggest and most important such table is called |eqtb|. It holds the current \quote
127    {equivalents} of things; i.e., it explains what things mean or what their current values are,
128    for all quantities that are subject to the nesting structure provided by \TEX's grouping
129    mechanism. There are six parts to |eqtb|:
130
131    \startitemize[n]
132
133    \startitem
134        |eqtb[null_cs]| holds the current equivalent of the zero-length control sequence.
135    \stopitem
136
137    \startitem
138        |eqtb[hash_base..(glue_base-1)]| holds the current equivalents of single- and multiletter
139        control sequences.
140    \stopitem
141
142    \startitem
143        |eqtb[glue_base..(local_base-1)]| holds the current equivalents of glue parameters like
144        the current baselineskip.
145    \stopitem
146
147    \startitem
148        |eqtb[local_base..(int_base-1)]| holds the current equivalents of local halfword
149        quantities like the current box registers, the current \quote {catcodes}, the current font,
150        and a pointer to the current paragraph shape.
151    \stopitem
152
153    \startitem
154        |eqtb[int_base .. (dimen_base-1)]| holds the current equivalents of fullword integer
155        parameters like the current hyphenation penalty.
156    \stopitem
157
158    \startitem
159        |eqtb[dimen_base .. eqtb_size]| holds the current equivalents of fullword dimension
160        parameters like the current hsize or amount of hanging indentation.
161    \stopitem
162
163    \stopitemize
164
165    Note that, for example, the current amount of baselineskip glue is determined by the setting of
166    a particular location in region~3 of |eqtb|, while the current meaning of the control sequence
167    |\baselineskip| (which might have been changed by |\def| or |\let|) appears in region~2.
168
169    The last two regions of |eqtb| have fullword values instead of the three fields |eq_level|,
170    |eq_type|, and |equiv|. An |eq_type| is unnecessary, but \TEX\ needs to store the |eq_level|
171    information in another array called |xeq_level|.
172
173    The last statement is no longer true. We have plenty of room in the 64 bit memory words now so
174    we no longer need the parallel |x| array. For the moment we keep the commented code.
175
176*/
177
178// equivalents_state_info lmt_equivalents_state = {
179// };
180
181void tex_initialize_levels(void)
182{
183    cur_level = level_one;
184    cur_group = bottom_level_group;
185    cur_boundary = 0;
186    lmt_scanner_state.last_cs_name = null_cs;
187}
188
189void tex_initialize_undefined_cs(void)
190{
191    set_eq_type(undefined_control_sequence, undefined_cs_cmd);
192    set_eq_flag(undefined_control_sequence, 0);
193    set_eq_value(undefined_control_sequence, null);
194    set_eq_level(undefined_control_sequence, level_zero);
195}
196
197void tex_dump_equivalents_mem(dumpstream f)
198{
199    /*tex
200        Dump regions 1 to 4 of |eqtb|, the table of equivalents: glue muglue toks boxes. The table
201        of equivalents usually contains repeated information, so we dump it in compressed form: The
202        sequence of $n + 2$ values $(n, x_1, \ldots, x_n, m)$ in the format file represents $n+m$
203        consecutive entries of |eqtb|, with |m| extra copies of $x_n$, namely $(x_1, \ldots, x_n,
204        x_n, \ldots, x_n)$.
205    */
206    int index = null_cs;
207    do {
208        int different = 1;
209        int equivalent = 0;
210        int j = index;
211        while (j < eqtb_size - 1) {
212            if (equal_eqtb_entries(j, j + 1)) {
213                ++equivalent;
214                goto FOUND1;
215            } else {
216                ++different;
217            }
218            ++j;
219        }
220        /*tex |j = int_base-1| */
221        goto DONE1;
222      FOUND1:
223        j++;
224        while (j < eqtb_size - 1) {
225            if (equal_eqtb_entries(j, j + 1)) {
226                ++equivalent;
227            } else {
228                goto DONE1;
229            }
230            ++j;
231        }
232      DONE1:
233     // printf("index %i, different %i, equivalent %i\n",index,different,equivalent);
234        dump_int(f, different);
235        dump_things(f, lmt_hash_state.eqtb[index], different);
236        dump_int(f, equivalent);
237        index = index + different + equivalent;
238    } while (index <= eqtb_size);
239    /*tex Dump the |hash_extra| part: */
240    dump_int(f, lmt_hash_state.hash_data.ptr);
241    if (lmt_hash_state.hash_data.ptr > 0) {
242        dump_things(f, lmt_hash_state.eqtb[eqtb_size + 1], lmt_hash_state.hash_data.ptr);
243    }
244    /*tex A special register. */
245    dump_int(f, lmt_token_state.par_loc);
246 /* dump_int(f, lmt_token_state.line_par_loc); */ /*tex See note in textoken.c|. */
247    dump_int(f, lmt_token_state.empty);
248}
249
250void tex_undump_equivalents_mem(dumpstream f)
251{
252    /*tex Undump regions 1 to 6 of the table of equivalents |eqtb|. */
253    int index = null_cs;
254    do {
255        int different;
256        int equivalent;
257        undump_int(f, different);
258        if (different > 0) {
259            undump_things(f, lmt_hash_state.eqtb[index], different);
260        }
261        undump_int(f, equivalent);
262     // printf("index %i, different %i, equivalent %i\n",index,different,equivalent);
263        if (equivalent > 0) {
264            int last = index + different - 1;
265            for (int i = 1; i <= equivalent; i++) {
266                lmt_hash_state.eqtb[last + i] = lmt_hash_state.eqtb[last];
267            }
268        }
269        index = index + different + equivalent;
270    } while (index <= eqtb_size);
271    /*tex Undump |hash_extra| part. */
272    undump_int(f, lmt_hash_state.hash_data.ptr);
273    if (lmt_hash_state.hash_data.ptr > 0) {
274        /* we get a warning on possible overrun here */
275        undump_things(f, lmt_hash_state.eqtb[eqtb_size + 1], lmt_hash_state.hash_data.ptr);
276    }
277    undump_int(f, lmt_token_state.par_loc);
278    if (lmt_token_state.par_loc >= hash_base && lmt_token_state.par_loc <= lmt_hash_state.hash_data.top) {
279        lmt_token_state.par_token = cs_token_flag + lmt_token_state.par_loc;
280    } else {
281        tex_fatal_undump_error("parloc");
282    }
283 /* undump_int(f, lmt_token_state.line_par_loc); */
284 /* if (lmt_token_state.line_par_loc >= hash_base && lmt_token_state.line_par_loc <= lmt_hash_state.hash_data.top) { */
285 /*     lmt_token_state.line_par_token = cs_token_flag + lmt_token_state.line_par_loc; */
286 /* } else { */
287 /*     tex_fatal_undump_error("lineparloc"); */
288 /* } */
289    undump_int(f, lmt_token_state.empty);
290    return;
291}
292
293/*tex
294
295    At this time it might be a good idea for the reader to review the introduction to |eqtb| that
296    was given above just before the long lists of parameter names. Recall that the \quote {outer
297    level} of the program is |level_one|, since undefined control sequences are assumed to be \quote
298    {defined} at |level_zero|.
299
300    The following function is used to test if there is room for up to eight more entries on
301    |save_stack|. By making a conservative test like this, we can get by with testing for overflow
302    in only a few places.
303
304    We now let the save stack dynamically grow. In practice the stack is small but when a large one
305    is needed, the overhead is probably neglectable compared to what the macro need.
306
307*/
308
309# define reserved_save_stack_slots 32 /* We need quite some for boxes so we bump it. */
310
311void tex_initialize_save_stack(void)
312{
313    int size = lmt_save_state.save_stack_data.minimum;
314    lmt_save_state.save_stack = aux_allocate_clear_array(sizeof(save_record), lmt_save_state.save_stack_data.step, reserved_save_stack_slots);
315    if (lmt_save_state.save_stack) {
316        lmt_save_state.save_stack_data.allocated = lmt_save_state.save_stack_data.step;
317    } else {
318        tex_overflow_error("save", size);
319    }
320}
321
322static int tex_room_on_save_stack(void)
323{
324    int top = lmt_save_state.save_stack_data.ptr;
325    if (top > lmt_save_state.save_stack_data.top) {
326        lmt_save_state.save_stack_data.top = top;
327        if (top > lmt_save_state.save_stack_data.allocated) {
328            save_record *tmp = NULL;
329            top = lmt_save_state.save_stack_data.allocated + lmt_save_state.save_stack_data.step;
330            if (top > lmt_save_state.save_stack_data.size) {
331                top = lmt_save_state.save_stack_data.size;
332            }
333            if (top > lmt_save_state.save_stack_data.allocated) {
334                top = lmt_save_state.save_stack_data.allocated + lmt_save_state.save_stack_data.step;
335                tmp = aux_reallocate_array(lmt_save_state.save_stack, sizeof(save_record), top, reserved_save_stack_slots);
336                lmt_save_state.save_stack = tmp;
337            }
338            lmt_run_memory_callback("save", tmp ? 1 : 0);
339            if (! tmp) {
340                tex_overflow_error("save", top);
341                return 0;
342            }
343         // memset((void *) (lmt_save_state.save_stack + lmt_save_state.save_stack_data.allocated + 1), 0, ((size_t) lmt_save_state.save_stack_data.step + reserved_save_stack_slots) * sizeof(save_record));
344            lmt_save_state.save_stack_data.allocated = top;
345        }
346    }
347    return 1;
348}
349
350void tex_save_halfword_on_stack(quarterword t, halfword v) /* todo: also value_2 and value_3 */
351{
352    if (tex_room_on_save_stack()) {
353        saved_type(0) = t;
354        saved_record(0) = 0;
355        saved_value_1(0) = v;
356        ++lmt_save_state.save_stack_data.ptr;
357    }
358}
359
360/*tex
361
362    Procedure |new_save_level| is called when a group begins. The argument is a group identification
363    code like |hbox_group|. After calling this routine, it is safe to put six more entries on
364    |save_stack|.
365
366    In some cases integer-valued items are placed onto the |save_stack| just below a |level_boundary|
367    word, because this is a convenient place to keep information that is supposed to \quote {pop up}
368    just when the group has finished. For example, when |\hbox to 100pt| is being treated, the 100pt
369    dimension is stored on |save_stack| just before |new_save_level| is called.
370
371    The |group_trace| procedure is called when a new level of grouping begins (|e=false|) or ends
372    (|e = true|) with |saved_value (-1)| containing the line number.
373
374*/
375
376static void tex_aux_group_trace(int g)
377{
378    tex_begin_diagnostic();
379    tex_print_format(g ? "{leaving %G}" : "{entering %G}", g);
380    tex_end_diagnostic();
381}
382
383/*tex
384
385    A group entered (or a conditional started) in one file may end in a different file. Such
386    slight anomalies, although perfectly legitimate, may cause errors that are difficult to
387    locate. In order to be able to give a warning message when such anomalies occur, \ETEX\
388    uses the |grp_stack| and |if_stack| arrays to record the initial |cur_boundary| and
389    |condition_ptr| values for each input file.
390
391    When a group ends that was apparently entered in a different input file, the |group_warning|
392    procedure is invoked in order to update the |grp_stack|. If moreover |\tracingnesting| is
393    positive we want to give a warning message. The situation is, however, somewhat complicated
394    by two facts:
395
396    \startitemize[n,packed]
397        \startitem
398            There may be |grp_stack| elements without a corresponding |\input| file or
399            |\scantokens| pseudo file (e.g., error insertions from the terminal); and
400        \stopitem
401        \startitem
402            the relevant information is recorded in the |name_field| of the |input_stack| only
403            loosely synchronized with the |in_open| variable indexing |grp_stack|.
404        \stopitem
405    \stopitemize
406
407    Per end 2023 the decision was made to use the two halfwords in the memory word that is used for
408    saving eq values for other purposes too. It meant for instance that instead if every new group 
409    needing 3 stack entries, it now needs just one. Actually there are not that meny mechanism that 
410    need the stack. For instance alignments have their own stack, math needs it for multi argument 
411    mechanism and the most demanding one is box handling. In addition to the two extra |value_2| and 
412    |value_3| fields we also use |options| and |extra| as alias for |level| (these are quarterwords). 
413
414*/
415
416static void tex_aux_group_warning(void)
417{
418    /*tex do we need a warning? */
419    bool warning = false;
420    /*tex index into |grp_stack| */
421    int index = lmt_input_state.in_stack_data.ptr;
422    lmt_input_state.base_ptr = lmt_input_state.input_stack_data.ptr;
423    /*tex store current state */
424    lmt_input_state.input_stack[lmt_input_state.base_ptr] = lmt_input_state.cur_input;
425    while ((lmt_input_state.in_stack[index].group == cur_boundary) && (index > 0)) {
426        /*tex
427
428            Set variable |w| to indicate if this case should be reported. This code scans the input
429            stack in order to determine the type of the current input file.
430
431        */
432        if (tracing_nesting_par > 0) {
433            while ((lmt_input_state.input_stack[lmt_input_state.base_ptr].state == token_list_state) || (lmt_input_state.input_stack[lmt_input_state.base_ptr].index > index)) {
434                --lmt_input_state.base_ptr;
435            }
436            if (lmt_input_state.input_stack[lmt_input_state.base_ptr].name > 17) {
437                /*tex |>  max_file_input_code| .. hm */
438                warning = true;
439            }
440        }
441        lmt_input_state.in_stack[index].group = save_value(lmt_save_state.save_stack_data.ptr);
442        --index;
443    }
444    if (warning) {
445        tex_begin_diagnostic();
446        tex_print_format("[warning: end of %G of a different file]", 1);
447        tex_end_diagnostic();
448        if (tracing_nesting_par > 1) {
449            tex_show_context();
450        }
451        if (lmt_error_state.history == spotless) {
452            lmt_error_state.history = warning_issued;
453        }
454    }
455}
456
457/*tex 
458    We store the line number and attribute state in the memory word part for the save entry and  
459    then we need only one slot instead of three. Each slot is 16 bytes so we're also a bit nicer to 
460    caching memory (if it happens at all). Because in practice we don't group that much (400.000 
461    times in the luametatex manual and a similar amount in the primitives manual) there is no real  
462    significant (positive) impact on performance. It's all about abstraction. 
463*/
464
465typedef enum saved_group_entries {
466    saved_group_group_entry           = 0,
467    saved_group_boundary_entry        = 0,
468    saved_group_attribute_state_entry = 0,
469    saved_group_input_line_entry      = 0,
470    saved_group_n_records             = 1,
471} saved_group_entries;
472
473# define saved_group_group           saved_group(saved_group_group_entry)
474# define saved_group_boundary        saved_value_1(saved_group_boundary_entry)
475# define saved_group_attribute_state saved_value_2(saved_group_attribute_state_entry)
476# define saved_group_input_line      saved_value_3(saved_group_input_line_entry)
477
478inline static void saved_group_initialize(void)
479{
480    saved_type(0) = level_boundary_save_type;
481    /* here level is really level */
482}
483
484void tex_new_save_level(quarterword group)
485{
486    /*tex We begin a new level of grouping. This will cost us three entries. */
487    if (tex_room_on_save_stack()) {
488        add_attribute_reference(current_attribute_state); 
489        saved_group_initialize();
490        saved_group_group = cur_group;
491        saved_group_boundary = cur_boundary;
492        saved_group_attribute_state = current_attribute_state;
493        saved_group_input_line = lmt_input_state.input_line;
494        if (cur_level == max_quarterword) {
495            /*tex We quit if |(cur_level+1)| is too big to be stored in |eqtb|. */
496            tex_overflow_error("grouping levels", max_quarterword - min_quarterword);
497        }
498        cur_boundary = lmt_save_state.save_stack_data.ptr;
499        cur_group = group;
500        if (tracing_groups_par > 0) {
501            tex_aux_group_trace(0);
502        }
503        ++cur_level;
504        lmt_save_state.save_stack_data.ptr += saved_group_n_records;
505        if (end_of_group_par) {
506            update_tex_end_of_group(null);
507        }
508    }
509}
510
511int tex_saved_line_at_level(void)
512{
513    return lmt_save_state.save_stack_data.ptr > 0 ? (saved_group_input_line > 0 ? saved_group_input_line : 0) : 0;
514}
515
516/*tex
517
518    The |\showgroups| command displays all currently active grouping levels. The modifications of
519    \TEX\ required for the display produced by the |show_save_groups| procedure were first discussed
520    by Donald~E. Knuth in {\em TUGboat} {\bf 11}, 165--170 and 499--511, 1990.
521
522    In order to understand a group type we also have to know its mode. Since unrestricted horizontal
523    modes are not associated with grouping, they are skipped when traversing the semantic nest.
524
525    I have to admit that I never used (or needed) this so we might as well drop it from \LUAMETATEX\
526    and given the already extensive tracing we can decide to drop it.
527
528    The output is not (entirely) downward compatible which is no big deal because we output some more
529    details anyway.
530*/
531
532void tex_show_save_groups(void)
533{
534    int pointer = lmt_nest_state.nest_data.ptr;
535    int saved_pointer = lmt_save_state.save_stack_data.ptr;
536    quarterword saved_level = cur_level;
537    quarterword saved_group = cur_group;
538    halfword saved_tracing = tracing_levels_par;
539    int alignmentstate = 1; /* to keep track of alignments */
540    const char *package = NULL;
541    lmt_save_state.save_stack_data.ptr = cur_boundary;
542    --cur_level;
543    tracing_levels_par |= tracing_levels_group;
544    while (1) {
545        int mode;
546        tex_print_levels();
547        tex_print_group(1);
548        if (cur_group == bottom_level_group) {
549            goto DONE;
550        }
551        do {
552            mode = lmt_nest_state.nest[pointer].mode;
553            if (pointer > 0) {
554                --pointer;
555            } else {
556                mode = vmode;
557            }
558        } while (mode == hmode);
559        tex_print_str(": ");
560        switch (cur_group) {
561            case simple_group:
562                ++pointer;
563                goto FOUND2;
564            case hbox_group:
565            case adjusted_hbox_group:
566                package = "hbox";
567                break;
568            case vbox_group:
569                package = "vbox";
570                break;
571            case vtop_group:
572                package = "vtop";
573                break;
574            case dbox_group:
575                package = "dbox";
576                break;
577            case align_group:
578                if (alignmentstate == 0) {
579                    package = (mode == internal_vmode) ? "halign" : "valign";
580                    alignmentstate = 1;
581                    goto FOUND1;
582                } else {
583                    if (alignmentstate == 1) {
584                        tex_print_str("align entry");
585                    } else {
586                        tex_print_str_esc("cr");
587                    }
588                    if (pointer >= alignmentstate) {
589                        pointer -= alignmentstate;
590                    }
591                    alignmentstate = 0;
592                    goto FOUND2;
593                }
594            case no_align_group:
595                ++pointer;
596                alignmentstate = -1;
597                tex_print_str_esc("noalign");
598                goto FOUND2;
599            case output_group:
600                tex_print_str_esc("output");
601                goto FOUND2;
602         // maybe: 
603         //
604         // case math_group:
605         // case math_component_group:
606         // case math_stack_group:
607         //      tex_print_str_esc(lmt_interface.group_code_values[cur_group].name);
608         //      goto FOUND2;
609            case math_group:
610                tex_print_str_esc("mathsubformula");
611                goto FOUND2;
612            case math_component_group:
613                tex_print_str_esc("mathcomponent");
614                goto FOUND2;
615            case math_stack_group:
616                tex_print_str_esc("mathstack");
617                goto FOUND2;
618            case discretionary_group:
619                tex_show_discretionary_group();
620                goto FOUND2;
621            case math_fraction_group:
622                tex_show_math_fraction_group();
623                goto FOUND2;
624            case math_radical_group:
625                tex_show_math_radical_group();
626                goto FOUND2;
627            case math_operator_group:
628                tex_show_math_operator_group();
629                goto FOUND2;
630            case math_choice_group:
631                tex_show_math_choice_group();
632                goto FOUND2;
633            case insert_group:
634                tex_show_insert_group();
635                goto FOUND2;
636            case vadjust_group:
637                tex_show_adjust_group();
638                goto FOUND2;
639            case vcenter_group:
640                package = "vcenter";
641                goto FOUND1;
642            case also_simple_group:
643            case semi_simple_group:
644                ++pointer;
645                tex_print_str_esc("begingroup");
646                goto FOUND2;
647         // case math_simple_group:
648         //     ++pointer;
649         //     tex_print_str_esc("beginmathgroup");
650         //     goto FOUND2;
651            case math_inline_group:
652                tex_print_char('$');
653            case math_display_group:
654                tex_print_char('$');
655                goto FOUND2;
656            case math_number_group:
657                tex_show_math_number_group();
658                goto FOUND2;
659            case math_fence_group:
660                /* kind of ugly ... maybe also save that one */ /* todo: operator */
661                tex_print_str_esc((node_subtype(lmt_nest_state.nest[pointer + 1].delimiter) == left_fence_side) ? "left" : "middle");
662                goto FOUND2;
663            default:
664                tex_confusion("show groups");
665                break;
666        }
667        /*tex 
668            Show the box context. In traditional \TEX\ the shift is encoded in the context which is 
669            why it had such a large offset for the other context value. That somewhat dirty trick 
670            was has stepwise been removed.
671        */
672        switch (tex_get_packaging_context()) {
673            case direct_box_flag:
674                {
675                    scaled shift = tex_get_packaging_shift();
676                    if (shift != null_flag) { 
677                        /*tex We passed the safeguard. */
678                        singleword cmd = is_v_mode(lmt_nest_state.nest[pointer].mode) ? hmove_cmd : vmove_cmd;
679                        tex_print_cmd_chr(cmd, (shift > 0) ? move_forward_code : move_backward_code);
680                        tex_print_dimension(abs(shift), pt_unit);
681                    }
682                }
683                break;
684            case global_box_flag:
685                tex_print_str_esc("global");
686            case box_flag:
687                {
688                    tex_print_str_esc("setbox");
689                    tex_print_int(tex_get_packaging_context());
690                    tex_print_char('=');
691                }
692                break;
693            case a_leaders_flag:
694                tex_print_cmd_chr(leader_cmd, a_leaders);
695                break;
696            case c_leaders_flag:
697                tex_print_cmd_chr(leader_cmd, c_leaders);
698                break;
699            case x_leaders_flag:
700                tex_print_cmd_chr(leader_cmd, x_leaders);
701                break;
702            case g_leaders_flag:
703                tex_print_cmd_chr(leader_cmd, g_leaders);
704                break;
705            case u_leaders_flag:
706                tex_print_cmd_chr(leader_cmd, u_leaders);
707                break;
708        }
709      FOUND1:
710        tex_show_packaging_group(package);
711      FOUND2:
712        --cur_level;
713        cur_group = saved_group_group;
714        lmt_save_state.save_stack_data.ptr = saved_group_boundary;
715    }
716  DONE:
717    lmt_save_state.save_stack_data.ptr = saved_pointer;
718    cur_level = saved_level;
719    cur_group = saved_group;
720    tracing_levels_par = saved_tracing;
721}
722
723void tex_show_save_stack(void)
724{
725    halfword savedptr = lmt_save_state.save_stack_data.ptr;
726    tex_print_format("%l[savestack size %i]\n", lmt_save_state.save_stack_data.ptr);
727    while (lmt_save_state.save_stack_data.ptr) {
728        --lmt_save_state.save_stack_data.ptr;
729        tex_print_nlp();
730        tex_print_levels();
731        tex_print_format("[%i: ", lmt_save_state.save_stack_data.ptr);
732        if (save_type(lmt_save_state.save_stack_data.ptr) >= saved_record_0 && save_type(lmt_save_state.save_stack_data.ptr) <= saved_record_9) {
733            tex_print_format("save record %i, ", save_type(lmt_save_state.save_stack_data.ptr) - saved_record_0 + 1, save_record(lmt_save_state.save_stack_data.ptr));
734            /* these have to be provided in the modules */
735            switch (save_record(lmt_save_state.save_stack_data.ptr)) {
736                case unknown_save_type:
737                    break;
738                case box_save_type:
739                    if (tex_show_packaging_record()) {
740                        break;
741                    } else { 
742                        goto INVALID_TYPE;
743                    }
744                case local_box_save_type:
745                    if (tex_show_localbox_record()) {
746                        break;
747                    } else { 
748                        goto INVALID_TYPE;
749                    }
750                case alignment_save_type:
751                    if (tex_show_alignment_record()) {
752                        break;
753                    } else { 
754                        goto INVALID_TYPE;
755                    }
756                case adjust_save_type:
757                    if (tex_show_adjust_record()) {
758                        break;
759                    } else { 
760                        goto INVALID_TYPE;
761                    }
762                case math_save_type:
763                    if (tex_show_math_record()) {
764                        break;
765                    } else { 
766                        goto INVALID_TYPE;
767                    }
768                case fraction_save_type:
769                    if (tex_show_math_fraction_record()) {
770                        break;
771                    } else { 
772                        goto INVALID_TYPE;
773                    }
774                case radical_save_type:
775                    if (tex_show_math_radical_record()) {
776                        break;
777                    } else { 
778                        goto INVALID_TYPE;
779                    }
780                case operator_save_type:
781                    if (tex_show_math_operator_record()) {
782                        break;
783                    } else { 
784                        goto INVALID_TYPE;
785                    }
786                case math_group_save_type:
787                    if (tex_show_math_group_record()) {
788                        break;
789                    } else { 
790                        goto INVALID_TYPE;
791                    }
792                case choice_save_type:
793                    if (tex_show_math_choice_record()) {
794                        break;
795                    } else { 
796                        goto INVALID_TYPE;
797                    }
798                case number_save_type:
799                    if (tex_show_math_number_record()) {
800                        break;
801                    } else { 
802                        goto INVALID_TYPE;
803                    }
804                case insert_save_type:
805                    if (tex_show_insert_record()) {
806                        break;
807                    } else { 
808                        goto INVALID_TYPE;
809                    }
810                case discretionary_save_type:
811                    if (tex_show_discretionary_record()) {
812                        break;
813                    } else { 
814                        goto INVALID_TYPE;
815                    }
816                default: 
817                    goto INVALID_RECORD;
818            }
819        } else {
820            switch (save_type(lmt_save_state.save_stack_data.ptr)) {
821                case level_boundary_save_type:
822                    tex_print_format("boundary, group '%s', boundary %i, attrlist %i, line %i", lmt_interface.group_code_values[saved_group_group].name, saved_group_boundary, saved_group_attribute_state, tex_saved_line_at_level());
823                    break;
824                case restore_old_value_save_type:
825                    tex_print_format("restore, level %i, cs ", save_level(lmt_save_state.save_stack_data.ptr));
826                    tex_aux_show_eqtb(save_value(lmt_save_state.save_stack_data.ptr));
827                    break;
828                case insert_tokens_save_type:
829                    tex_print_format("insert, pointer %i", save_value(lmt_save_state.save_stack_data.ptr));
830                    break;
831                case restore_lua_save_type:
832                    tex_print_format("restore lua, level %i, function %i", save_level(lmt_save_state.save_stack_data.ptr), save_value(lmt_save_state.save_stack_data.ptr));
833                    break;
834                case restore_zero_save_type:
835                    tex_print_format("restore zero, level %i, cs ", save_level(lmt_save_state.save_stack_data.ptr));
836                    tex_aux_show_eqtb(save_value(lmt_save_state.save_stack_data.ptr));
837                    break;
838                default:
839                    goto INVALID_TYPE;
840            }
841        }
842        goto DONE;
843      INVALID_RECORD:
844        tex_print_format("invalid record %i", save_record(lmt_save_state.save_stack_data.ptr));
845        goto DONE;
846      INVALID_TYPE:
847        tex_print_format("invalid type %i", save_type(lmt_save_state.save_stack_data.ptr));
848        goto DONE;
849      DONE:
850        tex_print_char(']');
851        tex_print_nlp();
852    }
853    tex_print_format("%l[savestack bottom]\n");
854    lmt_save_state.save_stack_data.ptr = savedptr;
855}
856
857/*tex
858    This is an experiment. The |handle_overload| function can either go on or quit, depending on
859    how strong one wants to check for overloads.
860
861    \starttabulate[||||||||]
862        \NC   \NC         \NC immutable \NC permanent \NC primitive \NC frozen \NC instance \NC \NR
863        \NC 1 \NC warning \NC +         \NC +         \NC +         \NC        \NC          \NC \NR
864        \NC 2 \NC error   \NC +         \NC +         \NC +         \NC        \NC          \NC \NR
865        \NC 3 \NC warning \NC +         \NC +         \NC +         \NC +      \NC          \NC \NR
866        \NC 4 \NC error   \NC +         \NC +         \NC +         \NC +      \NC          \NC \NR
867        \NC 5 \NC warning \NC +         \NC +         \NC +         \NC +      \NC +        \NC \NR
868        \NC 6 \NC error   \NC +         \NC +         \NC +         \NC +      \NC +        \NC \NR
869    \stoptabulate
870
871    The overload callback gets passed:
872        (boolean) error,
873        (integer) overload,
874        (string)  csname,
875        (integer) flags.
876
877    See January 2020 files for an alternative implementation.
878*/
879
880static void tex_aux_handle_overload(const char *s, halfword cs, int overload, int error_type)
881{
882    int callback_id = lmt_callback_defined(handle_overload_callback);
883    if (callback_id > 0) {
884        lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "bdsd->", error_type == normal_error_type, overload, cs_text(cs), eq_flag(cs));
885    } else {
886        tex_handle_error(
887            error_type,
888            "You can't redefine %s %S.",
889            s, cs,
890            NULL
891        );
892    }
893}
894
895static int tex_aux_report_overload(halfword cs, int overload)
896{
897    int error_type = overload & 1 ? warning_error_type : normal_error_type;
898    if (has_eq_flag_bits(cs, immutable_flag_bit)) {
899        tex_aux_handle_overload("immutable", cs, overload, error_type);
900    } else if (has_eq_flag_bits(cs, primitive_flag_bit)) {
901        tex_aux_handle_overload("primitive", cs, overload, error_type);
902    } else if (has_eq_flag_bits(cs, permanent_flag_bit)) {
903        tex_aux_handle_overload("permanent", cs, overload, error_type);
904    } else if (has_eq_flag_bits(cs, frozen_flag_bit)) {
905        tex_aux_handle_overload("frozen", cs, overload, error_type);
906    } else if (has_eq_flag_bits(cs, instance_flag_bit)) {
907        tex_aux_handle_overload("instance", cs, overload, warning_error_type);
908        return 1;
909    }
910    return error_type == warning_error_type;
911}
912
913# define overload_error_type(overload) (overload & 1 ? warning_error_type : normal_error_type)
914
915int tex_define_permitted(halfword cs, halfword prefixes)
916{
917    halfword overload = overload_mode_par;
918    if (! cs || ! overload || has_eq_flag_bits(cs, mutable_flag_bit)) {
919        return 1;
920    } else if (is_overloaded(prefixes)) {
921        if (overload > 2 && has_eq_flag_bits(cs, immutable_flag_bit | permanent_flag_bit | primitive_flag_bit)) {
922            return tex_aux_report_overload(cs, overload);
923        }
924    } else if (overload > 4) {
925        if (has_eq_flag_bits(cs, immutable_flag_bit | permanent_flag_bit | primitive_flag_bit | frozen_flag_bit | instance_flag_bit)) {
926            return tex_aux_report_overload(cs, overload);
927        }
928    } else if (overload > 2) {
929        if (has_eq_flag_bits(cs, immutable_flag_bit | permanent_flag_bit | primitive_flag_bit | frozen_flag_bit)) {
930            return tex_aux_report_overload(cs, overload);
931        }
932    } else if (has_eq_flag_bits(cs, immutable_flag_bit)) {
933        return tex_aux_report_overload(cs, overload);
934    }
935    return 1;
936}
937
938static int tex_aux_mutation_permitted(halfword cs)
939{
940    halfword overload = overload_mode_par;
941    if (cs && overload && has_eq_flag_bits(cs, immutable_flag_bit)) {
942        return tex_aux_report_overload(cs, overload);
943    } else {
944        return 1;
945    }
946}
947
948/*tex
949
950    Just before an entry of |eqtb| is changed, the following procedure should be called to update
951    the other data structures properly. It is important to keep in mind that reference counts in
952    |mem| include references from within |save_stack|, so these counts must be handled carefully.
953
954*/
955
956static void tex_aux_eq_destroy(memoryword w)
957{
958    halfword p = eq_value_field(w);
959    if (p) { 
960        switch (eq_type_field(w)) {
961            case call_cmd:
962            case protected_call_cmd:
963            case semi_protected_call_cmd:
964            case constant_call_cmd:
965            case tolerant_call_cmd:
966            case tolerant_protected_call_cmd:
967            case tolerant_semi_protected_call_cmd:
968            case register_toks_reference_cmd:
969            case internal_toks_reference_cmd:
970                tex_delete_token_reference(p);
971                break;
972            case internal_glue_reference_cmd:
973            case register_glue_reference_cmd:
974            case internal_muglue_reference_cmd:
975            case register_muglue_reference_cmd:
976            case gluespec_cmd:
977            case mugluespec_cmd:
978            case mathspec_cmd:
979            case fontspec_cmd:
980            case specification_reference_cmd:
981                tex_flush_node(p);
982                break;
983            case internal_box_reference_cmd:
984            case register_box_reference_cmd:
985                tex_flush_node_list(p);
986                break;
987            default:
988                break;
989        }
990    }
991}
992
993/*tex
994
995    To save a value of |eqtb[p]| that was established at level |l|, we can use the following
996    subroutine. This code could be simplified after the xeq cleanup so we actually use one slot
997    less per saved value.
998
999*/
1000
1001static void tex_aux_eq_save(halfword p, quarterword l)
1002{
1003    if (tex_room_on_save_stack()) {
1004        if (l == level_zero) {
1005            save_type(lmt_save_state.save_stack_data.ptr) = restore_zero_save_type;
1006        } else {
1007            save_type(lmt_save_state.save_stack_data.ptr) = restore_old_value_save_type;
1008            save_word(lmt_save_state.save_stack_data.ptr) = lmt_hash_state.eqtb[p];
1009        }
1010        save_level(lmt_save_state.save_stack_data.ptr) = l;
1011        save_value(lmt_save_state.save_stack_data.ptr) = p;
1012        ++lmt_save_state.save_stack_data.ptr;
1013    }
1014}
1015
1016/*tex
1017
1018    The procedure |eq_define| defines an |eqtb| entry having specified |eq_type| and |equiv| fields,
1019    and saves the former value if appropriate. This procedure is used only for entries in the first
1020    four regions of |eqtb|, i.e., only for entries that have |eq_type| and |equiv| fields. After
1021    calling this routine, it is safe to put four more entries on |save_stack|, provided that there
1022    was room for four more entries before the call, since |eq_save| makes the necessary test.
1023
1024    The destroy if same branch comes from \ETEX\ but is it really right to destroy here if we
1025    actually want to keep the value? In practice we only come here with zero cases but even then,
1026    it looks like we can destroy the token list or node (list). Not, that might actually work ok in
1027    the case of glue refs that have work by ref count and token lists and node (lists) are always
1028    different so there we do no harm.
1029
1030    There is room for some optimization here. 
1031
1032*/
1033
1034inline static int tex_aux_equal_eq(halfword p, singleword cmd, singleword flag, halfword chr)
1035{
1036    /* maybe keep flag test at call end and then only flip flags */
1037    if (eq_flag(p) == flag) {
1038     // printf("eqtest> %03i %03i\n",eq_type(p),cmd);
1039        switch (eq_type(p)) {
1040            case internal_glue_reference_cmd:
1041            case register_glue_reference_cmd:
1042            case internal_muglue_reference_cmd:
1043            case register_muglue_reference_cmd:
1044            case gluespec_cmd:
1045            case mugluespec_cmd:
1046                /*tex We compare the pointer as well as the record. */
1047                if (tex_same_glue(eq_value(p), chr)) {
1048                    if (chr) {
1049                        tex_flush_node(chr);
1050                    }
1051                    return 1;
1052                } else {
1053                    return 0;
1054                }
1055            case mathspec_cmd:
1056                /*tex Idem here. */
1057                if (tex_same_mathspec(eq_value(p), chr)) {
1058                    if (chr) {
1059                        tex_flush_node(chr);
1060                    }
1061                    return 1;
1062                } else {
1063                    return 0;
1064                }
1065            case fontspec_cmd:
1066                /*tex And here. */
1067                if (tex_same_fontspec(eq_value(p), chr)) {
1068                    if (chr) {
1069                        tex_flush_node(chr);
1070                    }
1071                    return 1;
1072                } else {
1073                    return 0;
1074                }
1075            case call_cmd:
1076            case protected_call_cmd:
1077            case semi_protected_call_cmd:
1078            case constant_call_cmd:
1079            case tolerant_call_cmd:
1080            case tolerant_protected_call_cmd:
1081            case tolerant_semi_protected_call_cmd:
1082                /*tex The initial token reference will do as it is unique. */
1083             // if (eq_value(p) == chr) {
1084                if (eq_value(p) == chr && eq_level(p) == cur_level) {
1085                    tex_delete_token_reference(eq_value(p));
1086                    return 1;
1087                } else {
1088                    return 0;
1089                }
1090            case specification_reference_cmd:
1091            case unit_reference_cmd:
1092            case internal_box_reference_cmd:
1093            case register_box_reference_cmd:
1094                /*tex These are also references. */
1095                if (eq_type(p) == cmd && eq_value(p) == chr && ! chr) {
1096             // if (eq_type(p) == cmd && eq_value(p) == chr && ! chr && eq_level(p) == cur_level) {
1097                    return 1;
1098                } else {
1099                    /* play safe */
1100                    return 0;
1101                }
1102            case internal_toks_reference_cmd:
1103            case register_toks_reference_cmd:
1104                /*tex As are these. */
1105                if (p && chr && eq_value(p) == chr) {
1106                    tex_delete_token_reference(eq_value(p));
1107                    return 1;
1108                } else {
1109                    return 0;
1110                }
1111            case internal_toks_cmd:
1112            case register_toks_cmd:
1113                /*tex Again we have references. */
1114                if (eq_value(p) == chr) {
1115             // if (eq_value(p) == chr && eq_level(p) == cur_level) {
1116                    return 1;
1117                } else {
1118                    return 0;
1119                }
1120            case integer_cmd:
1121            case index_cmd:
1122            case dimension_cmd:
1123            case posit_cmd:
1124                if (eq_type(p) == cmd && eq_value(p) == chr) {
1125             // if (eq_type(p) == cmd && eq_value(p) == chr && eq_level(p) == cur_level) {
1126                    return 1;
1127                }
1128            default:
1129                /*tex
1130                    We can best also check the level because for integer defs etc we run into
1131                    issues otherwise (see testcase tests/luametatex/eqtest.tex based on MS's
1132                    math file).
1133                */
1134                if (eq_type(p) == cmd && eq_value(p) == chr) {
1135             // if (eq_type(p) == cmd && eq_value(p) == chr && eq_level(p) == cur_level) {
1136                    return 1;
1137                }
1138                return 0;
1139        }
1140    } else {
1141        return 0;
1142    }
1143}
1144
1145/*tex Used to define a not yet defined cs or box or ... */
1146
1147void tex_eq_define(halfword p, singleword cmd, halfword chr)
1148{
1149    int trace = tracing_assigns_par > 0;
1150    if (tex_aux_equal_eq(p, cmd, 0, chr)) {
1151        if (trace) {
1152            tex_aux_diagnostic_trace(p, "reassigning");
1153        }
1154    } else {
1155        if (trace) {
1156            tex_aux_diagnostic_trace(p, "changing");
1157        }
1158        if (eq_level(p) == cur_level) {
1159            tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1160        } else if (cur_level > level_one) {
1161            tex_aux_eq_save(p, eq_level(p));
1162        }
1163        set_eq_level(p, cur_level);
1164        set_eq_type(p, cmd);
1165        set_eq_flag(p, 0);
1166        set_eq_value(p, chr);
1167        if (trace) {
1168            tex_aux_diagnostic_trace(p, "into");
1169        }
1170    }
1171}
1172
1173/*tex
1174
1175    The counterpart of |eq_define| for the remaining (fullword) positions in |eqtb| is called
1176    |eq_word_define|. Since |xeq_level[p] >= level_one| for all |p|, a |restore_zero| will never
1177    be used in this case.
1178
1179*/
1180
1181void tex_eq_word_define(halfword p, int w) /* not used */
1182{
1183    bool trace = tracing_assigns_par > 0;
1184    if (eq_value(p) == w) {
1185        if (trace) {
1186            tex_aux_diagnostic_trace(p, "reassigning");
1187        }
1188    } else {
1189        if (trace) {
1190            tex_aux_diagnostic_trace(p, "changing");
1191        }
1192        if (eq_level(p) != cur_level) {
1193            tex_aux_eq_save(p, eq_level(p));
1194            set_eq_level(p, cur_level);
1195        }
1196        eq_value(p) = w;
1197        if (trace) {
1198            tex_aux_diagnostic_trace(p, "into");
1199        }
1200    }
1201}
1202
1203/*tex
1204
1205    The |eq_define| and |eq_word_define| routines take care of local definitions. Global definitions
1206    are done in almost the same way, but there is no need to save old values, and the new value is
1207    associated with |level_one|.
1208
1209*/
1210
1211void tex_geq_define(halfword p, singleword cmd, halfword chr) /* not used */
1212{
1213    bool trace = tracing_assigns_par > 0;
1214    if (trace) {
1215        tex_aux_diagnostic_trace(p, "globally changing");
1216    }
1217    tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1218    set_eq_level(p, level_one);
1219    set_eq_type(p, cmd);
1220    set_eq_flag(p, 0);
1221    set_eq_value(p, chr);
1222    if (trace) {
1223        tex_aux_diagnostic_trace(p, "into");
1224    }
1225}
1226
1227void tex_geq_word_define(halfword p, int w) /* not used */
1228{
1229    bool trace = tracing_assigns_par > 0;
1230    if (trace) {
1231        tex_aux_diagnostic_trace(p, "globally changing");
1232    }
1233    eq_value(p) = w;
1234    set_eq_level(p, level_one);
1235    if (trace) {
1236        tex_aux_diagnostic_trace(p, "into");
1237    }
1238}
1239
1240/*tex
1241    Instead of a macro that distinguishes between global or not we now use a few normal functions.
1242    That way we don't need to define a bogus variable |a| in some cases. This is typically one of
1243    those changes that happened after other bits and pieces got redone. (One can also consider it
1244    a side effect of looking at the code through a visual studio lense.)
1245*/
1246
1247inline static void tex_aux_set_eq_data(halfword p, singleword t, halfword e, singleword f, quarterword l)
1248{
1249    singleword flag = eq_flag(p);
1250    set_eq_level(p, l);
1251    set_eq_type(p, t);
1252    set_eq_value(p, e);
1253    if (is_mutable(f) || is_mutable(flag)) {
1254        set_eq_flag(p, (f | flag) & ~(noaligned_flag_bit | permanent_flag_bit | primitive_flag_bit | immutable_flag_bit));
1255    } else {
1256        set_eq_flag(p, f);
1257    }
1258}
1259
1260void tex_define(int g, halfword p, singleword t, halfword e) /* int g -> singleword g */
1261{
1262    bool trace = tracing_assigns_par > 0;
1263    singleword f = make_eq_flag_bits(g);
1264    if (is_global(g)) {
1265        /* what if already global */
1266        if (trace) {
1267            tex_aux_diagnostic_trace(p, "globally changing");
1268        }
1269     // if (tex_aux_equal_eq(p, t, f, e) && (eq_level(p) == level_one)) {
1270     //     return; /* we can save some stack */
1271     // }
1272        tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1273        tex_aux_set_eq_data(p, t, e, f, level_one);
1274    } else if (! is_constrained(g) && tex_aux_equal_eq(p, t, f, e)) {
1275        /* hm, we tweak the ref ! */
1276        if (trace) {
1277            tex_aux_diagnostic_trace(p, "reassigning");
1278            return;
1279        }
1280    } else {
1281        if (trace) {
1282            tex_aux_diagnostic_trace(p, "changing");
1283        }
1284        if (eq_level(p) == cur_level) {
1285            tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1286        } else if (is_retained(g)) {
1287            /* nothing */
1288        } else if (cur_level > level_one) {
1289            tex_aux_eq_save(p, eq_level(p));
1290        }
1291        tex_aux_set_eq_data(p, t, e, f, cur_level);
1292    }
1293    if (trace) {
1294        tex_aux_diagnostic_trace(p, "into");
1295    }
1296}
1297
1298/*tex 
1299    Used in |\dimendef| but also |\dimensiondef| and alike. Before it gets calles we already 
1300    redefined to |\relax| so we might have saved. 
1301*/
1302
1303void tex_define_again(int g, halfword p, singleword t, halfword e) /* int g -> singleword g */
1304{
1305    if (tracing_assigns_par > 0) { 
1306        singleword f = make_eq_flag_bits(g);
1307        if (is_global(g)) {
1308            /* what if already global */
1309            tex_aux_diagnostic_trace(p, "globally changing");
1310         // if (tex_aux_equal_eq(p, t, f, e) && (eq_level(p) == level_one)) {
1311         //     return; /* we can save some stack */
1312         // }
1313            tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1314            tex_aux_set_eq_data(p, t, e, f, level_one);
1315        } else if (! is_constrained(g) && tex_aux_equal_eq(p, t, f, e)) {
1316            /* hm, we tweak the ref ! */
1317            tex_aux_diagnostic_trace(p, "reassigning");
1318        } else {
1319            tex_aux_diagnostic_trace(p, is_retained(g) ? "retained changing": "changing");
1320            if (eq_level(p) == cur_level) {
1321                tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1322            } else if (is_retained(g)) { 
1323                /* nothing */
1324            } else if (cur_level > level_one) {
1325                tex_aux_eq_save(p, eq_level(p));
1326            }
1327            tex_aux_set_eq_data(p, t, e, f, cur_level);
1328        }
1329        tex_aux_diagnostic_trace(p, "into");
1330    } else { 
1331        set_eq_type(p, t);
1332        set_eq_value(p, e);    
1333    }
1334}
1335
1336/*tex
1337    The next one is used when we let something. 
1338*/
1339
1340void tex_define_inherit(int g, halfword p, singleword f, singleword t, halfword e)
1341{
1342    bool trace = tracing_assigns_par > 0;
1343    if (is_global(g)) {
1344        /* what if already global */
1345        if (trace) {
1346            tex_aux_diagnostic_trace(p, "globally changing");
1347        }
1348     // if (equal_eq(p, t, f, e) && (eq_level(p) == level_one)) {
1349     //    return; /* we can save some stack */
1350     // }
1351        tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1352        tex_aux_set_eq_data(p, t, e, f, level_one);
1353    } else if (! is_constrained(g) && tex_aux_equal_eq(p, t, f, e)) {
1354        if (trace) {
1355            tex_aux_diagnostic_trace(p, "reassigning");
1356            return;
1357        }
1358    } else {
1359        if (trace) {
1360            tex_aux_diagnostic_trace(p, is_retained(g) ? "retained changing" : "changing");
1361        }
1362        if (eq_level(p) == cur_level) {
1363            tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1364        } else if (is_retained(g)) {
1365            /* nothing */
1366        } else if (cur_level > level_one) {
1367            tex_aux_eq_save(p, eq_level(p));
1368        }
1369        tex_aux_set_eq_data(p, t, e, f, cur_level);
1370    }
1371    if (trace) {
1372        tex_aux_diagnostic_trace(p, "into");
1373    }
1374}
1375
1376/*tex
1377    Used in swapping but beware: when we swap a global vsize with a local ... we can get side 
1378    effect. No retain here. 
1379*/
1380
1381static void tex_aux_just_define(int g, halfword p, halfword e)
1382{
1383    bool trace = tracing_assigns_par > 0;
1384    if (is_global(g)) {
1385        if (trace) {
1386            tex_aux_diagnostic_trace(p, "globally changing");
1387        }
1388        tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1389    } else {
1390        if (trace) {
1391            tex_aux_diagnostic_trace(p, "changing");
1392        }
1393        if (eq_level(p) == cur_level) {
1394            tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1395        } else if (cur_level > level_one) {
1396            tex_aux_eq_save(p, eq_level(p));
1397        }
1398        set_eq_level(p, cur_level);
1399    }
1400    set_eq_value(p, e);
1401    if (trace) {
1402        tex_aux_diagnostic_trace(p, "into");
1403    }
1404}
1405
1406/* We can have a variant that doesn't save/restore so we just have to swap back then. */
1407
1408void tex_define_swapped(int g, halfword p1, halfword p2, int force)
1409{
1410    halfword t1 = eq_type(p1);
1411    halfword t2 = eq_type(p2);
1412    halfword l1 = eq_level(p1);
1413    halfword l2 = eq_level(p2);
1414    singleword f1 = eq_flag(p1);
1415    singleword f2 = eq_flag(p2);
1416    halfword v1 = eq_value(p1);
1417    halfword v2 = eq_value(p2);
1418    if (t1 == t2 && l1 == l2) {
1419        halfword overload = force ? 0 : overload_mode_par;
1420        if (overload) {
1421           if (f1 != f2) {
1422               goto NOTDONE;
1423           } else if (is_immutable(f1)) {
1424               goto NOTDONE;
1425           }
1426        }
1427        if (v1 == v2)  {
1428            return; 
1429        } else {
1430            switch (t1) {
1431                case register_posit_cmd:
1432                case register_integer_cmd:
1433                case register_attribute_cmd:
1434                case register_dimension_cmd:
1435                case register_glue_cmd:    /* unchecked */
1436                case register_muglue_cmd: /* unchecked */
1437                case internal_muglue_cmd: /* unchecked */
1438                case integer_cmd:
1439                case index_cmd:
1440                case dimension_cmd:
1441                case posit_cmd:
1442                    tex_aux_just_define(g, p1, v2);
1443                    tex_aux_just_define(g, p2, v1);
1444                    return;
1445                case register_toks_cmd:
1446                case internal_toks_cmd:
1447                    if (v1) tex_add_token_reference(v1);
1448                    if (v2) tex_add_token_reference(v2);
1449                    tex_aux_just_define(g, p1, v2);
1450                    tex_aux_just_define(g, p2, v1);
1451                    if (v1) tex_delete_token_reference(v1);
1452                    if (v2) tex_delete_token_reference(v2);
1453                    return;
1454                case internal_integer_cmd:
1455                    tex_assign_internal_integer_value(g, p1, v2);
1456                    tex_assign_internal_integer_value(g, p2, v1);
1457                    return;
1458                case internal_attribute_cmd:
1459                    tex_assign_internal_attribute_value(g, p1, v2);
1460                    tex_assign_internal_attribute_value(g, p2, v1);
1461                    return;
1462                case internal_posit_cmd:
1463                    tex_assign_internal_posit_value(g, p1, v2);
1464                    tex_assign_internal_posit_value(g, p2, v1);
1465                    return;
1466                case internal_dimension_cmd:
1467                    tex_assign_internal_dimension_value(g, p1, v2);
1468                    tex_assign_internal_dimension_value(g, p2, v1);
1469                    return;
1470                case internal_glue_cmd:
1471                    /* todo */
1472                    tex_assign_internal_skip_value(g, p1, v2);
1473                    tex_assign_internal_skip_value(g, p2, v1);
1474                    return;
1475                default:
1476                    if (overload > 2) {
1477                        if (has_flag_bits(f1, immutable_flag_bit | permanent_flag_bit | primitive_flag_bit)) {
1478                            if (overload > 3) {
1479                                goto NOTDONE;
1480                            }
1481                        }
1482                    }
1483                    if (is_call_cmd(t1)) {
1484                        if (v1) tex_add_token_reference(v1);
1485                        if (v2) tex_add_token_reference(v2);
1486                        tex_aux_just_define(g, p1, v2);
1487                        tex_aux_just_define(g, p2, v1);
1488                        /* no delete here .. hm */
1489                    } else {
1490                        tex_handle_error(
1491                            normal_error_type,
1492                            "\\swapcsvalues not (yet) implemented for commands (%C, %C)",
1493                            t1, v1, t2, v2, NULL
1494                        );
1495
1496                    }
1497                    return;
1498            }
1499        }
1500    }
1501  NOTDONE:
1502    tex_handle_error(
1503        normal_error_type,
1504        "\\swapcsvalues requires equal commands (%C, %C), levels (%i, %i) and flags (%i, %i)",
1505        t1, v1, t2, v2, l1, l2, f1, f2, NULL
1506    );
1507}
1508
1509/*tex 
1510    Used in pushing and popping. No retain here. 
1511*/
1512
1513void tex_forced_define(int g, halfword p, singleword f, singleword t, halfword e)
1514{
1515    bool trace = tracing_assigns_par > 0;
1516    if (is_global(g)) {
1517        if (trace) {
1518            tex_aux_diagnostic_trace(p, "globally changing");
1519        }
1520        tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1521        set_eq_level(p, level_one);
1522    } else {
1523        if (trace) {
1524            tex_aux_diagnostic_trace(p, "changing");
1525        }
1526        if (eq_level(p) == cur_level) {
1527            tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1528        } else if (cur_level > level_one) {
1529            tex_aux_eq_save(p, eq_level(p));
1530        }
1531        set_eq_level(p, cur_level);
1532    }
1533    set_eq_type(p, t);
1534    set_eq_flag(p, f);
1535    set_eq_value(p, e);
1536    if (trace) {
1537        tex_aux_diagnostic_trace(p, "into");
1538    }
1539}
1540
1541/*tex 
1542    Registers and other values that are stored directly without reference.
1543*/
1544
1545void tex_word_define(int g, halfword p, halfword w)
1546{
1547    if (tex_aux_mutation_permitted(p)) {
1548        bool trace = tracing_assigns_par > 0;
1549        if (is_global(g)) {
1550            if (trace) {
1551                tex_aux_diagnostic_trace(p, "globally changing");
1552            }
1553            eq_value(p) = w;
1554            set_eq_level(p, level_one);
1555        } else if (! is_constrained(g) && eq_value(p) == w) {
1556            if (trace) {
1557                tex_aux_diagnostic_trace(p, "reassigning");
1558                return;
1559            }
1560        } else if (is_retained(g)) {
1561            if (trace) {
1562                tex_aux_diagnostic_trace(p, "retained changing");
1563                set_eq_level(p, cur_level);
1564            }
1565            eq_value(p) = w;
1566        } else {
1567            if (trace) {
1568                tex_aux_diagnostic_trace(p, "changing");
1569            }
1570            if (eq_level(p) != cur_level) {
1571                tex_aux_eq_save(p, eq_level(p));
1572                set_eq_level(p, cur_level);
1573            }
1574            eq_value(p) = w;
1575        }
1576        if (trace) {
1577            tex_aux_diagnostic_trace(p, "into");
1578        }
1579        if (is_immutable(g)) {
1580            eq_flag(p) |= immutable_flag_bit;
1581        } else if (is_mutable(g)) {
1582            eq_flag(p) |= mutable_flag_bit;
1583        }
1584    }
1585}
1586
1587/*
1588void tex_forced_word_define(int g, halfword p, singleword f, halfword w)
1589{
1590    if (tex_aux_mutation_permitted(p)) {
1591        bool trace = tracing_assigns_par > 0;
1592        if (is_global(g)) {
1593            if (trace) {
1594                tex_aux_diagnostic_trace(p, "globally changing");
1595            }
1596            eq_value(p) = w;
1597            set_eq_level(p, level_one);
1598        } else if (eq_value(p) == w) {
1599            if (trace) {
1600                tex_aux_diagnostic_trace(p, "reassigning");
1601                return;
1602            }
1603        } else if (is_retained(g)) {
1604            if (trace) {
1605                tex_aux_diagnostic_trace(p, "retained changing");
1606            }
1607            eq_value(p) = w;
1608        } else {
1609            if (trace) {
1610                tex_aux_diagnostic_trace(p, "changing");
1611            }
1612            if (eq_level(p) != cur_level) {
1613                tex_aux_eq_save(p, eq_level(p));
1614                set_eq_level(p, cur_level);
1615            }
1616            eq_value(p) = w;
1617        }
1618        if (trace) {
1619            tex_aux_diagnostic_trace(p, "into");
1620        }
1621        eq_flag(p) = f;
1622    }
1623}
1624*/
1625
1626/*tex
1627
1628    Subroutine |save_for_after_group| puts a token on the stack for save-keeping.
1629
1630*/
1631
1632void tex_save_for_after_group(halfword t)
1633{
1634    if (t && cur_level > level_one && tex_room_on_save_stack()) {
1635        save_type(lmt_save_state.save_stack_data.ptr) = insert_tokens_save_type;
1636        save_level(lmt_save_state.save_stack_data.ptr) = level_zero;
1637        save_value(lmt_save_state.save_stack_data.ptr) = t;
1638        ++lmt_save_state.save_stack_data.ptr;
1639    }
1640}
1641
1642/*tex
1643
1644    The |unsave| routine goes the other way, taking items off of |save_stack|. This routine takes
1645    care of restoration when a level ends. Here, everything belonging to the topmost group is
1646    cleared off of the save stack.
1647
1648    In \TEX\ there are a few |\after...| commands, like |\aftergroup| and |\afterassignment| while
1649    |\futurelet| also has this property of postponed actions. The |\every...| token registers do
1650    the opposite and do stuff up front. In addition to |\aftergrouped| we have a variant that
1651    accepts a token list, as does |\afterassigned|. These items are saved on the stack.
1652
1653    In \LUAMETATEX\ we can also do things just before a group ends as well as just before the
1654    paragraph finishes. In the end it was not that hard to implement in the \LUATEX\ concept,
1655    although it adds a little overhead, but the benefits compensate that. Because we can use some
1656    mechanisms used in other extensions only a few extra lines are needed. All are accumulative
1657    but the paragraph bound one is special in the sense that is is bound to the current paragraph,
1658    so the actual implementation of that one happens elsewhere and differently.
1659
1660    Side note: when |\par| overloading was introduced in \PDFTEX\ and per request also added to
1661    |\LUATEX| it made no sense to add that to \LUAMETATEX\ too. We already have callbacks, and
1662    there is information available about what triggered a |\par|. Another argument against
1663    supporting this is that overloading |\par| is messy and unreliable (macro package and user
1664    demand and actions can badly interfere). The mentioned hooks already give more than enough
1665    opportunities. One doesn't expect users to overload |\relax| either.
1666
1667    Side note: at some point I will look into |\after| hooks in for instance alignments and maybe
1668    something nicer that |\afterassignment| can be used for pushing stuff into boxes (|\everybox|
1669    is not that helpful). But again avoiding extra overhead might is a very good be a reason to
1670    not do that at all.
1671
1672*/
1673
1674void tex_unsave(void)
1675{
1676    if (end_of_group_par) {
1677        /*tex 
1678            This is not yet always ok, and looks like we can get weird commands (in some group 
1679            ending situations)! But I need a better example of a failure. (low priority) 
1680        */
1681        tex_begin_inserted_list(tex_get_available_token(token_val(end_local_cmd, 0)));
1682        tex_begin_token_list(end_of_group_par, end_of_group_text);
1683        if (tracing_nesting_par > 2) {
1684            tex_local_control_message("entering token scanner via endgroup");
1685        }
1686        tex_local_control(1);
1687    }
1688    delete_attribute_reference(current_attribute_state); 
1689    tex_unsave_math_codes(cur_level);
1690    tex_unsave_cat_codes(cat_code_table_par, cur_level);
1691    tex_unsave_text_codes(cur_level);
1692    tex_unsave_math_data(cur_level);
1693    if (cur_level > level_one) {
1694        /*tex
1695            Variable |a| registers if we already have processed an |\aftergroup|. We append when
1696            >= 1.
1697        */
1698        bool append = false;
1699        bool trace = tracing_restores_par > 0;
1700        --cur_level;
1701        /*tex Clear off top level from |save_stack|. */
1702        while (1) {
1703            --lmt_save_state.save_stack_data.ptr;
1704            switch (save_type(lmt_save_state.save_stack_data.ptr)) {
1705                case level_boundary_save_type:
1706                    goto DONE;
1707                case restore_old_value_save_type:
1708                    {
1709                        halfword p = save_value(lmt_save_state.save_stack_data.ptr);
1710                        /*tex
1711                            Store |save_stack[save_ptr]| in |eqtb[p]|, unless |eqtb[p]| holds a global
1712                            value A global definition, which sets the level to |level_one|, will not be
1713                            undone by |unsave|. If at least one global definition of |eqtb[p]| has been
1714                            carried out within the group that just ended, the last such definition will
1715                            therefore survive.
1716                        */
1717                        if (p < internal_integer_base || p > eqtb_size) {
1718                            if (eq_level(p) == level_one) {
1719                                tex_aux_eq_destroy(save_word(lmt_save_state.save_stack_data.ptr));
1720                                if (trace) {
1721                                    tex_aux_diagnostic_trace(p, "retaining");
1722                                }
1723                            } else {
1724                                tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1725                                lmt_hash_state.eqtb[p] = save_word(lmt_save_state.save_stack_data.ptr);
1726                                if (trace) {
1727                                    tex_aux_diagnostic_trace(p, "restoring");
1728                                }
1729                            }
1730                        } else if (eq_level(p) == level_one) {
1731                            if (trace) {
1732                                tex_aux_diagnostic_trace(p, "retaining");
1733                            }
1734                        } else {
1735                            lmt_hash_state.eqtb[p] = save_word(lmt_save_state.save_stack_data.ptr);
1736                            if (trace) {
1737                                tex_aux_diagnostic_trace(p, "restoring");
1738                            }
1739                        }
1740                        break;
1741                    }
1742                case insert_tokens_save_type:
1743                    {
1744                        /*tex A list starts a new input level (for now). */
1745                        halfword p = save_value(lmt_save_state.save_stack_data.ptr);
1746                        if (append) {
1747                            /*tex We stay at the same input level (an \ETEX\ feature). */
1748                            tex_append_input(p);
1749                        } else {
1750                            tex_insert_input(p);
1751                            append = true;
1752                        }
1753                        break;
1754                    }
1755                case restore_lua_save_type:
1756                    {
1757                        /* The same as lua_function_code in |textoken.c|. */
1758                        halfword p = save_value(lmt_save_state.save_stack_data.ptr);
1759                        if (p > 0) {
1760                            strnumber u = tex_save_cur_string();
1761                            lmt_token_state.luacstrings = 0;
1762                            lmt_function_call(p, 0);
1763                            tex_restore_cur_string(u);
1764                            if (lmt_token_state.luacstrings > 0) {
1765                                tex_lua_string_start();
1766                            }
1767                        } else {
1768                            tex_normal_error("lua restore", "invalid number");
1769                        }
1770                        append = true;
1771                        break;
1772                    }
1773                case restore_zero_save_type:
1774                    {
1775                        halfword p = save_value(lmt_save_state.save_stack_data.ptr);
1776                        if (eq_level(p) == level_one) {
1777                            if (trace) {
1778                                tex_aux_diagnostic_trace(p, "retaining");
1779                            }
1780                        } else {
1781                            if (p < internal_integer_base || p > eqtb_size) {
1782                                tex_aux_eq_destroy(lmt_hash_state.eqtb[p]);
1783                            }
1784                            lmt_hash_state.eqtb[p] = lmt_hash_state.eqtb[undefined_control_sequence];
1785                            if (trace) {
1786                                tex_aux_diagnostic_trace(p, "restoring");
1787                            }
1788                        }
1789                        break;
1790                    }
1791                default:
1792                    /* we have a messed up save pointer */
1793                    tex_formatted_error("tex unsave", "bad save type case %d, probably a stack pointer issue", save_type(lmt_save_state.save_stack_data.ptr));
1794                    break;
1795            }
1796        }
1797      DONE:
1798        if (tracing_groups_par > 0) {
1799            tex_aux_group_trace(1);
1800        }
1801        if (lmt_input_state.in_stack[lmt_input_state.in_stack_data.ptr].group == cur_boundary) {
1802            /*tex Groups are possibly not properly nested with files. */
1803            tex_aux_group_warning();
1804        }
1805        cur_group = saved_group_group;
1806        cur_boundary = saved_group_boundary;
1807        set_current_attribute_state(saved_group_attribute_state); 
1808    } else {
1809        /*tex |unsave| is not used when |cur_group=bottom_level| */
1810        tex_confusion("current level");
1811    }
1812}
1813
1814/*tex
1815
1816    Most of the parameters kept in |eqtb| can be changed freely, but there's an exception: The
1817    magnification should not be used with two different values during any \TEX\ job, since a
1818    single magnification is applied to an entire run. The global variable |mag_set| is set to the
1819    current magnification whenever it becomes necessary to \quote {freeze} it at a particular value.
1820
1821    The |prepare_mag| subroutine is called whenever \TEX\ wants to use |mag| for magnification. If
1822    nonzero, this magnification should be used henceforth. We might drop magnifaction at some point.
1823
1824    {\em NB: As we delegate the backend to \LUA\ we have no mag.}
1825
1826    Let's pause a moment now and try to look at the Big Picture. The \TEX\ program consists of three
1827    main parts: syntactic routines, semantic routines, and output routines. The chief purpose of the
1828    syntactic routines is to deliver the user's input to the semantic routines, one token at a time.
1829    The semantic routines act as an interpreter responding to these tokens, which may be regarded as
1830    commands. And the output routines are periodically called on to convert box-and-glue lists into a
1831    compact set of instructions that will be sent to a typesetter. We have discussed the basic data
1832    structures and utility routines of \TEX, so we are good and ready to plunge into the real activity
1833    by considering the syntactic routines.
1834
1835    Our current goal is to come to grips with the |get_next| procedure, which is the keystone of
1836    \TEX's input mechanism. Each call of |get_next| sets the value of three variables |cur_cmd|,
1837    |cur_chr|, and |cur_cs|, representing the next input token.
1838
1839    \startitemize
1840        \startitem
1841            |cur_cmd| denotes a command code from the long list of codes given above;
1842        \stopitem
1843        \startitem
1844            |cur_chr| denotes a character code or other modifier of the command code;
1845        \stopitem
1846        \startitem
1847            |cur_cs| is the |eqtb| location of the current control sequence, if the current token
1848            was a control sequence, otherwise it's zero.
1849        \stopitem
1850    \stopitemize
1851
1852    Underlying this external behavior of |get_next| is all the machinery necessary to convert from
1853    character files to tokens. At a given time we may be only partially finished with the reading of
1854    several files (for which |\input| was specified), and partially finished with the expansion of
1855    some user-defined macros and/or some macro parameters, and partially finished with the generation
1856    of some text in a template for |\halign|, and so on. When reading a character file, special
1857    characters must be classified as math delimiters, etc.; comments and extra blank spaces must be
1858    removed, paragraphs must be recognized, and control sequences must be found in the hash table.
1859    Furthermore there are occasions in which the scanning routines have looked ahead for a word like
1860    |plus| but only part of that word was found, hence a few characters must be put back into the input
1861    and scanned again.
1862
1863    To handle these situations, which might all be present simultaneously, \TEX\ uses various stacks
1864    that hold information about the incomplete activities, and there is a finite state control for each
1865    level of the input mechanism. These stacks record the current state of an implicitly recursive
1866    process, but the |get_next| procedure is not recursive. Therefore it will not be difficult to
1867    translate these algorithms into low-level languages that do not support recursion.
1868
1869    In general, |cur_cmd| is the current command as set by |get_next|, while |cur_chr| is the operand
1870    of the current command. The control sequence found here is registsred in |cur_cs| and is zero if
1871    none found. The |cur_tok| variable contains the packed representative of |cur_cmd| and |cur_chr|
1872    and like the other ones is global.
1873
1874    Here is a procedure that displays the current command. The variable |n| holds the level of |\if ...
1875    \fi| nesting and |l| the line where |\if| started.
1876
1877*/
1878
1879void tex_show_cmd_chr(halfword cmd, halfword chr)
1880{
1881    tex_begin_diagnostic();
1882    if (cur_list.mode != lmt_nest_state.shown_mode) {
1883        if (tracing_commands_par >= 4) {
1884            /*tex So, larger than \ETEX's extra info 3 value. We might just always do this. */
1885            tex_print_format("[mode: entering %M]", cur_list.mode);
1886            tex_print_nlp();
1887            tex_print_levels();
1888            tex_print_str("{");
1889        } else {
1890            tex_print_format("{%M: ", cur_list.mode);
1891        }
1892        lmt_nest_state.shown_mode = cur_list.mode;
1893    } else {
1894        tex_print_str("{");
1895    }
1896    tex_print_cmd_chr((singleword) cmd, chr);
1897    if (cmd == if_test_cmd && tracing_ifs_par > 0) {
1898        halfword p;
1899        int n, l;
1900        if (tracing_commands_par >= 4) {
1901            tex_print_str(": ");
1902        } else {
1903            tex_print_char(' ');
1904        }
1905        if (cur_chr >= first_real_if_test_code || cur_chr == or_else_code || cur_chr == or_unless_code) { /* can be other >= test */
1906            n = 1;
1907            l = lmt_input_state.input_line;
1908        } else {
1909            tex_print_cmd_chr(if_test_cmd, lmt_condition_state.cur_if);
1910            tex_print_char(' ');
1911            n = 0;
1912            l = lmt_condition_state.if_line;
1913        }
1914        /*tex
1915            We now also have a proper counter but this is a check for a potential mess up. If
1916            als is right, |lmt_condition_state.if_nesting| often should match |n|.
1917        */
1918        p = lmt_condition_state.cond_ptr;
1919        while (p) {
1920            ++n;
1921            p = node_next(p);
1922        }
1923        if (l) {
1924            if (tracing_commands_par >= 4) {
1925                tex_print_format("(level %i, line %i, nesting %i)", n, l, lmt_condition_state.if_nesting);
1926            } else {
1927             // tex_print_format("(level %i) entered on line %i", n, l);
1928                tex_print_format("(level %i, line %i)", n, l);
1929            }
1930        } else {
1931            tex_print_format("(level %i)", n);
1932        }
1933    }
1934    tex_print_char('}');
1935    tex_end_diagnostic();
1936}
1937
1938/*tex
1939
1940    Here is a procedure that displays the contents of |eqtb[n]| symbolically.
1941
1942    We're now at equivalent |n| in region 4. First we initialize most things to null or undefined
1943    values. An undefined font is represented by the internal code |font_base|.
1944
1945    However, the character code tables are given initial values based on the conventional
1946    interpretation of \ASCII\ code. These initial values should not be changed when \TEX\ is
1947    adapted for use with non-English languages; all changes to the initialization conventions
1948    should be made in format packages, not in \TEX\ itself, so that global interchange of formats
1949    is possible.
1950
1951    The reorganization was done because I wanted a cleaner token interface at the \LUA\ end. So
1952    we also do some more checking. The order differs from traditional \TEX\ but of course the
1953    approach is similar.
1954
1955    The regions in \LUAMETATEX\ are a bit adapted as a side effect of the \ETEX\ extensions as
1956    well as our own. For instance, we tag all regions because we also need a consistent token
1957    interface to \LUA. We also dropped fonts and some more from the table.
1958
1959    A previous, efficient, still range based variant can be found in the my archive but it makes
1960    no sense to keep it commented here (apart from sentimental reasons) so one now only can see 
1961    the range agnostic version here.
1962
1963*/
1964
1965void tex_aux_show_eqtb(halfword n)
1966{
1967    if (n < null_cs) {
1968        tex_print_format("bad token %i, case 1", n);
1969    } else if (eqtb_indirect_range(n)) {
1970        tex_print_cs(n);
1971        tex_print_char('=');
1972        tex_print_cmd_chr(eq_type(n), eq_value(n));
1973        if (eq_type(n) >= call_cmd) {
1974            tex_print_char(':');
1975            tex_token_show(eq_value(n));
1976        }
1977    } else {
1978        switch (eq_type(n)) {
1979            case internal_toks_reference_cmd:
1980                tex_print_cmd_chr(internal_toks_cmd, n);
1981                goto TOKS;
1982            case register_toks_reference_cmd:
1983                tex_print_str_esc("toks");
1984                tex_print_int(register_toks_number(n));
1985              TOKS:
1986                tex_print_char('=');
1987                tex_token_show(eq_value(n));
1988                break;
1989            case internal_box_reference_cmd:
1990                tex_print_cmd_chr(eq_type(n), n);
1991                goto BOX;
1992            case register_box_reference_cmd:
1993                tex_print_str_esc("box");
1994                tex_print_int(register_box_number(n));
1995              BOX:
1996                tex_print_char('=');
1997                if (eq_value(n)) {
1998                    tex_show_node_list(eq_value(n), 0, 1);
1999                    tex_print_levels();
2000                } else {
2001                    tex_print_str("void");
2002                }
2003                break;
2004            case internal_glue_reference_cmd:
2005                tex_print_cmd_chr(internal_glue_cmd, n);
2006                goto SKIP;
2007            case register_glue_reference_cmd:
2008                tex_print_str_esc("skip");
2009                tex_print_int(register_glue_number(n));
2010              SKIP:
2011                tex_print_char('=');
2012                if (tracing_nodes_par > 2) {
2013                    tex_print_format("<%i>", eq_value(n));
2014                }
2015                tex_print_spec(eq_value(n), pt_unit);
2016                break;
2017            case internal_muglue_reference_cmd:
2018                tex_print_cmd_chr(internal_muglue_cmd, n);
2019                goto MUSKIP;
2020            case register_muglue_reference_cmd:
2021                tex_print_str_esc("muskip");
2022                tex_print_int(register_muglue_number(n));
2023              MUSKIP:
2024                if (tracing_nodes_par > 2) {
2025                    tex_print_format("<%i>", eq_value(n));
2026                }
2027                tex_print_char('=');
2028                tex_print_spec(eq_value(n), mu_unit);
2029                break;
2030            case internal_integer_reference_cmd:
2031                tex_print_cmd_chr(internal_integer_cmd, n);
2032                goto COUNT;
2033            case register_integer_reference_cmd:
2034                tex_print_str_esc("count");
2035                tex_print_int(register_integer_number(n));
2036              COUNT:
2037                tex_print_char('=');
2038                tex_print_int(eq_value(n));
2039                break;
2040            case internal_attribute_reference_cmd:
2041                tex_print_cmd_chr(internal_attribute_cmd, n);
2042                goto ATTRIBUTE;
2043            case register_attribute_reference_cmd:
2044                tex_print_str_esc("attribute");
2045                tex_print_int(register_attribute_number(n));
2046              ATTRIBUTE:
2047                tex_print_char('=');
2048                tex_print_int(eq_value(n));
2049                break;
2050            case internal_posit_reference_cmd:
2051                tex_print_cmd_chr(internal_posit_cmd, n);
2052                goto POSIT;
2053            case register_posit_reference_cmd:
2054                tex_print_str_esc("posit");
2055                tex_print_int(register_posit_number(n));
2056              POSIT:
2057                tex_print_char('=');
2058                tex_print_posit(eq_value(n));
2059                break;
2060            case internal_dimension_reference_cmd:
2061                tex_print_cmd_chr(internal_dimension_cmd, n);
2062                goto DIMEN;
2063            case register_dimension_reference_cmd:
2064                tex_print_str_esc("dimen");
2065                tex_print_int(register_dimension_number(n));
2066              DIMEN:
2067                tex_print_char('=');
2068                tex_print_dimension(eq_value(n), pt_unit);
2069                break;
2070            case specification_reference_cmd:
2071                tex_print_cmd_chr(specification_cmd, n);
2072                tex_print_char('=');
2073                if (eq_value(n)) {
2074                 // if (tracing_nodes_par > 2) {
2075                 //     tex_print_format("<%i>", eq_value(n));
2076                 // }
2077                    tex_print_int(specification_count(eq_value(n)));
2078                } else {
2079                    tex_print_char('0');
2080                }
2081                break;
2082            case unit_reference_cmd:
2083                tex_print_cmd_chr(association_cmd, n);
2084                tex_print_char('=');
2085                if (eq_value(n)) {
2086                    tex_print_str("todo");
2087                } else {
2088                    tex_print_char('0');
2089                }
2090                break;
2091            default:
2092                tex_print_format("bad token %i, case 2", n);
2093                break;
2094        }
2095    }
2096}
2097
2098/*tex
2099
2100    A couple of (self documenting) convenient helpers. They do what we do in \LUATEX, but we now
2101    have collapsed all the options in one mode parameter that also gets stored in the glyph so
2102    the older functions are gone. Progress.
2103
2104*/
2105
2106halfword tex_automatic_disc_penalty(halfword mode)
2107{
2108    return hyphenation_permitted(mode, automatic_penalty_hyphenation_mode) ? automatic_hyphen_penalty_par : ex_hyphen_penalty_par;
2109}
2110
2111halfword tex_explicit_disc_penalty(halfword mode)
2112{
2113    return hyphenation_permitted(mode, explicit_penalty_hyphenation_mode) ? explicit_hyphen_penalty_par : ex_hyphen_penalty_par;
2114}
2115
2116/*tex
2117
2118    The table of equivalents needs to get (pre)populated by the right commands and references, so
2119    that happens here (called in maincontrol at ini time).
2120
2121    For diagnostic purposes we now have the type set for registers. As a consequence we not have
2122    four |glue_ref| variants, which is a trivial extension.
2123
2124*/
2125
2126inline static void tex_aux_set_eq(halfword base, quarterword level, singleword cmd, halfword value, halfword count)
2127{
2128    if (count > 0) {
2129        set_eq_level(base, level);
2130        set_eq_type(base, cmd);
2131        set_eq_flag(base, 0);
2132        set_eq_value(base, value);
2133        for (int k = base + 1; k <= base + count; k++){
2134            copy_eqtb_entry(k, base);
2135        }
2136    }
2137}
2138
2139void tex_synchronize_equivalents(void)
2140{
2141    tex_aux_set_eq(null_cs, level_zero, undefined_cs_cmd, null, lmt_hash_state.hash_data.top - 1);
2142}
2143
2144void tex_initialize_equivalents(void)
2145{
2146    /*tex Order matters here! */
2147    tex_aux_set_eq(null_cs,                     level_zero, undefined_cs_cmd,                 null,                   lmt_hash_state.hash_data.top - 1);
2148    tex_aux_set_eq(internal_glue_base,          level_one,  internal_glue_reference_cmd,      zero_glue,              number_glue_pars);
2149    tex_aux_set_eq(register_glue_base,          level_one,  register_glue_reference_cmd,      zero_glue,              max_glue_register_index);
2150    tex_aux_set_eq(internal_muglue_base,        level_one,  internal_muglue_reference_cmd,    zero_glue,              number_muglue_pars);
2151    tex_aux_set_eq(register_muglue_base,        level_one,  register_muglue_reference_cmd,    zero_glue,              max_muglue_register_index);
2152    tex_aux_set_eq(internal_toks_base,          level_one,  internal_toks_reference_cmd,      null,                   number_tok_pars);
2153    tex_aux_set_eq(register_toks_base,          level_one,  register_toks_reference_cmd,      null,                   max_toks_register_index);
2154    tex_aux_set_eq(internal_box_base,           level_one,  internal_box_reference_cmd,       null,                   number_box_pars);
2155    tex_aux_set_eq(register_box_base,           level_one,  register_box_reference_cmd,       null,                   max_box_register_index);
2156    tex_aux_set_eq(internal_integer_base,       level_one,  internal_integer_reference_cmd,   0,                      number_integer_pars);
2157    tex_aux_set_eq(register_integer_base,       level_one,  register_integer_reference_cmd,   0,                      max_integer_register_index);
2158    tex_aux_set_eq(internal_attribute_base,     level_one,  internal_attribute_reference_cmd, unused_attribute_value, number_attribute_pars);
2159    tex_aux_set_eq(register_attribute_base,     level_one,  register_attribute_reference_cmd, unused_attribute_value, max_attribute_register_index);
2160    tex_aux_set_eq(internal_posit_base,         level_one,  internal_posit_reference_cmd,     0,                      number_posit_pars);
2161    tex_aux_set_eq(register_posit_base,         level_one,  register_posit_reference_cmd,     0,                      max_posit_register_index);
2162    tex_aux_set_eq(internal_dimension_base,     level_one,  internal_dimension_reference_cmd, 0,                      number_dimension_pars);
2163    tex_aux_set_eq(register_dimension_base,     level_one,  register_dimension_reference_cmd, 0,                      max_dimension_register_index);
2164    tex_aux_set_eq(internal_specification_base, level_one,  specification_reference_cmd,      null,                   number_specification_pars);
2165    tex_aux_set_eq(internal_unit_base,          level_one,  unit_reference_cmd,               unset_unit_class,       max_unit_register_index);
2166    tex_aux_set_eq(undefined_control_sequence,  level_zero, undefined_cs_cmd,                 null,                   0);
2167    /*tex why here? */
2168    cat_code_table_par = 0;
2169}
2170
2171int tex_located_save_value(int id)
2172{
2173    int i = lmt_save_state.save_stack_data.ptr - 1;
2174    while (save_type(i) != level_boundary_save_type) {
2175        i--;
2176    }
2177    while (i < lmt_save_state.save_stack_data.ptr) {
2178        if (save_type(i) == restore_old_value_save_type && save_value(i) == id) {
2179            /*
2180            if (math_direction_par != save_value(i - 1)) {
2181                return 1;
2182            }
2183            */
2184            return save_value(i - 1);
2185        }
2186        i++;
2187    }
2188    return 0;
2189}
2190
2191extern int tex_cs_state(halfword p) 
2192{
2193    if (p == null_cs) {
2194        return cs_null_error;
2195    } else if (p < hash_base) {
2196        return cs_below_base_error;
2197    } else if (p == undefined_control_sequence) {
2198        return cs_undefined_error;
2199    } else if (eqtb_out_of_range(p)) {
2200        return cs_out_of_range_error;
2201    } else {
2202        return cs_no_error;
2203    }
2204}
2205