texscanning.c /size: 343 Kb    last modification: 2025-02-21 11:03
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7static void tex_aux_scan_expr                 (halfword level, int braced);
8static void tex_aux_scan_expression           (int level, int braced);
9static void tex_aux_scan_integer_expression   (int braced);
10static void tex_aux_scan_dimension_expression (int braced);
11
12/*tex
13    A helper.
14*/
15
16static inline void tex_push_back(halfword tok, halfword cmd, halfword chr)
17{
18    if (cmd != spacer_cmd && tok != deep_frozen_relax_token && ! (cmd == relax_cmd && chr == no_relax_code)) {
19        tex_back_input(tok);
20    }
21}
22
23/*tex
24
25    Let's turn now to some procedures that \TEX\ calls upon frequently to digest certain kinds of
26    patterns in the input. Most of these are quite simple; some are quite elaborate. Almost all of
27    the routines call |get_x_token|, which can cause them to be invoked recursively.
28
29    The |scan_left_brace| routine is called when a left brace is supposed to be the next non-blank
30    token. (The term \quote {left brace} means, more precisely, a character whose catcode is
31    |left_brace|.) \TEX\ allows |\relax| to appear before the |left_brace|.
32
33*/
34
35/* This reads a mandatory |left_brace|: */
36
37void tex_scan_left_brace(void)
38{
39    /*tex Get the next non-blank non-relax non-call token */
40    while(1) {
41        tex_get_x_token();
42        switch (cur_cmd) {
43            case left_brace_cmd:
44                /* we found one */
45                return;
46            case spacer_cmd:
47            case relax_cmd:
48         /* case end_paragraph_cmd: */ /* could be an option in keyword scanner */
49                /* stay in while */
50                break;
51            default:
52                /* we recover */
53                tex_handle_error(
54                    back_error_type,
55                    "Missing { inserted",
56                    "A left brace was mandatory here, so I've put one in."
57                );
58                cur_tok = left_brace_token + '{';
59                cur_cmd = left_brace_cmd;
60                cur_chr = '{';
61                ++lmt_input_state.align_state;
62                return;
63        }
64    }
65}
66
67/*tex
68
69    The |scan_optional_equals| routine looks for an optional |=| sign preceded by optional spaces;
70    |\relax| is not ignored here.
71
72*/
73
74void tex_scan_optional_equals(void)
75{
76    /*tex Get the next non-blank non-call token. */
77    do {
78        tex_get_x_token();
79    } while (cur_cmd == spacer_cmd);
80    if (cur_tok != equal_token) {
81        tex_back_input(cur_tok);
82    }
83}
84
85/*tex
86
87    Here is a procedure that sounds an alarm when mu and non-mu units are being switched.
88
89*/
90
91static void tex_aux_mu_error(int n)
92{
93    tex_handle_error(
94        normal_error_type,
95        "Incompatible glue units (case %i)",
96        n,
97        "I'm going to assume that 1mu=1pt when they're mixed."
98    );
99}
100
101/*tex
102
103    The next routine |scan_something_internal| is used to fetch internal numeric quantities like
104    |\hsize|, and also to handle the |\the| when expanding constructions like |\the\toks0| and
105    |\the\baselineskip|. Soon we will be considering the |scan_int| procedure, which calls
106    |scan_something_internal|; on the other hand, |scan_something_internal| also calls |scan_int|,
107    for constructions like |\catcode\`\$| or |\fontdimen 3 \ff|. So we have to declare |scan_int|
108    as a |forward| procedure. A few other procedures are also declared at this point.
109
110    \TEX\ doesn't know exactly what to expect when |scan_something_internal| begins. For example,
111    an integer or dimension or glue value could occur immediately after |\hskip|; and one can even
112    say |\the} with respect to token lists in constructions like |\xdef\o{\the\output}|. On the
113    other hand, only integers are allowed after a construction like |\count|. To handle the various
114    possibilities, |scan_something_internal| has a |level| parameter, which tells the \quote
115    {highest} kind of quantity that |scan_something_internal| is allowed to produce. Seven levels
116    are  distinguished, namely |int_val|, |attr_val|, |dimen_val|, |glue_val|, |mu_val|, |tok_val|
117    and |ident_val|.
118
119    The output of |scan_something_internal| (and of the other routines |scan_int|, |scan_dimension|,
120    and |scan_glue| below) is put into the global variable |cur_val|, and its level is put into
121    |cur_val_level|. The highest values of |cur_val_level| are special: |mu_val| is used only when
122    |cur_val| points to something in a \quote {muskip} register, or to one of the three parameters
123    |\thinmuskip|, |\medmuskip|, |\thickmuskip|; |ident_val| is used only when |cur_val| points to
124    a font identifier; |tok_val| is used only when |cur_val| points to |null| or to the reference
125    count of a token list. The last two cases are allowed only when |scan_something_internal| is
126    called with |level = tok_val|.
127
128    If the output is glue, |cur_val| will point to a glue specification, and the reference count
129    of that glue will have been updated to reflect this reference; if the output is a nonempty
130    token list, |cur_val| will point to its reference count, but in this case the count will not
131    have been updated. Otherwise |cur_val| will contain the integer or scaled value in question.
132
133*/
134
135/*tex 
136    Maybe some day ... we could be more granular, for instance by using a bitset and then also
137    registering the data type involved. However it would more be for internal tracing and flagging
138    than for reporting. 
139*/
140
141typedef enum arithmic_errors { 
142    arithmic_error_reset, 
143    arithmic_error_overflow, 
144    arithmic_error_division_by_zero, 
145} arithmic_errors;
146
147scanner_state_info lmt_scanner_state = {
148    .current_cmd       = 0,
149    .current_chr       = 0,
150    .current_cs        = 0,
151 // .current_flag      = 0,
152    .current_tok       = 0,
153    .current_val       = 0,
154    .current_val_level = 0,
155    .current_box       = 0,
156    .last_cs_name      = 0,
157    .arithmic_error    = 0,
158    .expression_depth  = 0,
159};
160
161/*tex
162
163    When a |glue_val| changes to a |dimen_val|, we use the width component of the glue; there is no
164    need to decrease the reference count, since it has not yet been increased. When a |dimen_val|
165    changes to an |int_val|, we use scaled points so that the value doesn't actually change. And
166    when a |mu_val| changes to a |glue_val|, the value doesn't change either.
167
168    In \LUATEX\ we don't share glue but we have copies, so there is no need to mess with the
169    reference count and downgrading.
170
171*/
172
173static inline void tex_aux_downgrade_cur_val(int level, int succeeded, int negative)
174{
175    switch (cur_val_level) {
176        case posit_val_level:
177            if (negative) {
178                cur_val = tex_posit_neg(cur_val);
179            }
180            switch (level) {
181                case dimension_val_level:
182                    cur_val = tex_posit_to_dimension(cur_val);
183                cur_val_level = level;
184                    break;
185                case integer_val_level:
186                case attribute_val_level:
187                    cur_val = (halfword) tex_posit_to_integer(cur_val);
188                cur_val_level = level;
189                    break;
190            }
191          // if (cur_val_level > level) {
192          //     cur_val_level = level;
193          // }
194            break;
195        case integer_val_level:
196            if (cur_val_level > level) {
197                cur_val_level = level;
198            }
199            if (negative) {
200                cur_val = -cur_val;
201            }
202            if (level == posit_val_level) {
203                cur_val = tex_integer_to_posit(cur_val).v;
204            }
205            break;
206        case attribute_val_level:
207            if (cur_val_level > level) {
208                cur_val_level = level;
209            }
210            if (negative) {
211                cur_val = -cur_val;
212            }
213            if (level == posit_val_level) {
214                cur_val = tex_integer_to_posit(cur_val).v;
215            }
216            break;
217        case dimension_val_level:
218            if (cur_val_level > level) {
219                cur_val_level = level;
220            }
221            if (negative) {
222                cur_val = -cur_val;
223            }
224            if (level == posit_val_level) {
225                cur_val = tex_dimension_to_posit(cur_val).v;
226            }
227            break;
228        case muglue_val_level:
229            if (level == glue_val_level) {
230                goto COPYGLUE;
231            }
232        case glue_val_level:
233            if (level == posit_val_level) {
234                cur_val_level = level;
235                cur_val = tex_dimension_to_posit(negative ? - glue_amount(cur_val) : glue_amount(cur_val)).v;
236            } else if (cur_val_level > level) {
237                /* we can end up here with tok_val_level and a minus .. fuzzy */
238                cur_val_level = level;
239                cur_val = negative ? - glue_amount(cur_val) : glue_amount(cur_val);
240            } else {
241              COPYGLUE:
242                if (succeeded == 1) {
243                    cur_val = tex_new_glue_spec_node(cur_val);
244                }
245                if (negative) {
246                    glue_amount(cur_val) = -glue_amount(cur_val);
247                    glue_stretch(cur_val) = -glue_stretch(cur_val);
248                    glue_shrink(cur_val) = -glue_shrink(cur_val);
249                }
250            }
251            break;
252        case token_val_level:
253        case font_val_level:
254        case mathspec_val_level:
255        case fontspec_val_level:
256            /*tex
257                This test pays back as this actually happens, but we also need it for the
258                |none_lua_function| handling. We end up here in |ident_val_level| and |token_val_level|
259                and they don't downgrade, nor negate which saves a little testing.
260            */
261            break;
262     // case specification_val_level:
263     // case list_val_level:
264     // case no_val_level:
265     //     break;
266        default:
267            tex_confusion("downgrade");
268     }
269}
270
271/*tex
272
273    Some of the internal items can be fetched both routines, and these have been split off into the
274    next routine, that returns true if the command code was understood.
275
276    The |last_item_cmd| branch has been flattened a bit because we don't need to treat \ETEX\
277    specific thingies special any longer.
278
279*/
280
281static void tex_aux_set_cur_val_by_lua_value_cmd(halfword index, halfword property)
282{
283    int category = lua_value_none_code;
284    halfword value = 0; /* can also be scaled */
285    strnumber u = tex_save_cur_string();
286    lmt_token_state.luacstrings = 0;
287    category = lmt_function_call_by_category(index, property, &value);
288    switch (category) {
289        case lua_value_none_code:
290            cur_val_level = no_val_level;
291            break;
292        case lua_value_integer_code:
293        case lua_value_cardinal_code:
294            cur_val_level = integer_val_level;
295            break;
296        case lua_value_dimension_code:
297            cur_val_level = dimension_val_level;
298            break;
299        case lua_value_skip_code:
300            cur_val_level = glue_val_level;
301            break;
302        case lua_value_boolean_code:
303            /*tex For usage with |\ifboolean| */
304            value = value ? 1 : 0;
305            cur_val_level = integer_val_level;
306            break;
307        case lua_value_float_code:
308            cur_val_level = posit_val_level;
309            break;
310        case lua_value_string_code:
311            cur_val_level = no_val_level;
312            break;
313        case lua_value_node_code:
314        case lua_value_direct_code:
315            if (value) {
316                switch (node_type(value)) {
317                    case hlist_node:
318                    case vlist_node:
319                    case whatsit_node:
320                    case rule_node:
321                        cur_val_level = list_val_level;
322                        break;
323                    default:
324                        /* maybe a warning */
325                        value = null;
326                        cur_val_level = no_val_level;
327                        break;
328                }
329            } else {
330                value = null;
331                cur_val_level = no_val_level;
332            }
333            break;
334        case lua_value_conditional_code:
335            /* for now */
336        default:
337            cur_val_level = no_val_level;
338            break;
339    }
340    cur_val = value;
341    tex_restore_cur_string(u);
342    if (lmt_token_state.luacstrings > 0) {
343        tex_lua_string_start();
344    }
345}
346
347halfword tex_scan_lua_value(int index)
348{
349    tex_aux_set_cur_val_by_lua_value_cmd(index, 0);
350    return cur_val_level;
351}
352
353static halfword tex_aux_scan_register_index(void)
354{
355    do {
356        tex_get_x_token();
357    } while (cur_cmd == spacer_cmd);
358    switch (cur_cmd) {
359        case register_toks_cmd      : return cur_chr - register_toks_base;
360        case register_integer_cmd   : return cur_chr - register_integer_base;
361        case register_attribute_cmd : return cur_chr - register_attribute_base;
362        case register_posit_cmd     : return cur_chr - register_posit_base;
363        case register_dimension_cmd : return cur_chr - register_dimension_base;
364        case register_glue_cmd      : return cur_chr - register_glue_base;
365        case register_muglue_cmd    : return cur_chr - register_muglue_base;
366        case char_given_cmd         : return cur_chr;
367        case mathspec_cmd           : return tex_get_math_spec(cur_chr).character_value;
368        case integer_cmd            : return cur_chr;
369     /* case index_cmd              : return cur_chr; */
370        case posit_cmd              : return cur_chr;
371        case dimension_cmd          : return cur_chr;
372        default                     : return -1;
373    }
374}
375
376static halfword tex_aux_scan_character_index(void)
377{
378    halfword result = -1;
379    tex_get_token();
380    if (cur_tok < cs_token_flag) {
381        result = cur_chr;
382    } else if (cur_cmd == char_given_cmd) {
383        result = cur_chr;
384    } else if (cur_cmd == mathspec_cmd) {
385        result = tex_get_math_spec(cur_chr).character_value;
386    } else {
387        strnumber txt = cs_text(cur_tok - cs_token_flag);
388        if (tex_single_letter(txt)) {
389            result = aux_str2uni(str_string(txt));
390        } else if (tex_is_active_cs(txt)) {
391            result = active_cs_value(txt);
392        } else {
393            result = max_character_code + 1;
394        }
395    }
396    return result > max_character_code ? -1 : result;
397}
398
399/*
400    Fetch an item in the current node, if appropriate. Here is where |\last*| |\ |, and some more
401    are implemented. The reference count for |\lastskip| will be updated later. We also handle
402    |\inputlineno| and |\badness| here, because they are legal in similar contexts. In the follow
403    up engines much more than these are handled here.
404*/
405
406static int tex_aux_set_cur_val_by_some_cmd(int code)
407{
408    switch (code) {
409        case lastpenalty_code:
410            cur_val_level = integer_val_level;
411            goto COMMON;
412        case lastkern_code:
413            cur_val_level = dimension_val_level;
414            goto COMMON;
415        case lastskip_code:
416            cur_val_level = glue_val_level;
417            goto COMMON;
418        case lastboundary_code:
419            cur_val_level = integer_val_level;
420          COMMON:
421            {
422                cur_val = 0;
423                if (cur_list.tail != contribute_head && ! (cur_list.tail && node_type(cur_list.tail) == glyph_node) && cur_list.mode != nomode) {
424                    switch (code) {
425                        case lastpenalty_code:
426                            if (node_type(cur_list.tail) == penalty_node) {
427                                cur_val = penalty_amount(cur_list.tail);
428                            }
429                            break;
430                        case lastkern_code:
431                            if (node_type(cur_list.tail) == kern_node) {
432                                cur_val = kern_amount(cur_list.tail);
433                            }
434                            break;
435                        case lastskip_code:
436                            if (node_type(cur_list.tail) == glue_node) {
437                                cur_val = cur_list.tail;
438                                if (node_subtype(cur_list.tail) == mu_glue) {
439                                    cur_val_level = muglue_val_level;
440                                }
441                            }
442                            break; /* should we return 1 ? */
443                        case lastboundary_code:
444                            if (node_type(cur_list.tail) == boundary_node && node_subtype(cur_list.tail) == user_boundary) {
445                                cur_val = boundary_data(cur_list.tail);
446                            }
447                            break;
448                    }
449                } else if (cur_list.mode == vmode && cur_list.tail == cur_list.head) {
450                    switch (code) {
451                        case lastpenalty_code:
452                            cur_val = lmt_page_builder_state.last_penalty;
453                            break;
454                        case lastkern_code:
455                            cur_val = lmt_page_builder_state.last_kern;
456                            break;
457                        case lastskip_code:
458                            if (lmt_page_builder_state.last_glue != max_halfword) {
459                                cur_val = lmt_page_builder_state.last_glue;
460                            }
461                            break; /* should we return 1 ? */
462                        case lastboundary_code:
463                            cur_val = lmt_page_builder_state.last_boundary;
464                            break;
465                    }
466                }
467                break;
468            }
469        case last_node_type_code:
470            /*tex
471                We have mode nodes and when the mode parameter is set we report the real numbers.
472                This is a bit messy.
473            */
474            {
475                cur_val_level = integer_val_level;
476                if (cur_list.tail != contribute_head && cur_list.mode != nomode) {
477                    cur_val = node_type(cur_list.tail);
478                } else if (cur_list.mode == vmode && cur_list.tail == cur_list.head) {
479                    cur_val = lmt_page_builder_state.last_node_type;
480                } else if (cur_list.tail == cur_list.head || cur_list.mode == nomode) {
481                    cur_val = -1;
482                } else {
483                    cur_val = node_type(cur_list.tail);
484                }
485                break;
486            }
487        case last_node_subtype_code:
488            {
489                cur_val_level = integer_val_level;
490                if (cur_list.tail != contribute_head && cur_list.mode != nomode) {
491                    cur_val = node_subtype(cur_list.tail);
492                } else if (cur_list.mode == vmode && cur_list.tail == cur_list.head) {
493                    cur_val = lmt_page_builder_state.last_node_subtype;
494                } else if (cur_list.tail == cur_list.head || cur_list.mode == nomode) {
495                    cur_val = -1;
496                } else {
497                    cur_val = node_subtype(cur_list.tail);
498                }
499                break;
500            }
501        case input_line_no_code:
502            cur_val = lmt_input_state.input_line;
503            cur_val_level = integer_val_level;
504            break;
505        case badness_code:
506            cur_val = lmt_packaging_state.last_badness;
507            cur_val_level = integer_val_level;
508            break;
509        case overshoot_code:
510            cur_val = lmt_packaging_state.last_overshoot;
511            cur_val_level = dimension_val_level;
512            break;
513        case luametatex_major_version_code:
514            cur_val = lmt_version_state.majorversion;
515            cur_val_level = integer_val_level;
516            break;
517        case luametatex_minor_version_code:
518            cur_val = lmt_version_state.minorversion;
519            cur_val_level = integer_val_level;
520            break;
521        case luametatex_release_code:
522            cur_val = lmt_version_state.release; 
523            cur_val_level = integer_val_level;
524            break;
525        case luatex_version_code:
526            cur_val = lmt_version_state.version;  /* combined major and minor */
527            cur_val_level = integer_val_level;
528            break;
529        case luatex_revision_code:
530            cur_val = lmt_version_state.revision; /* always zero */
531            cur_val_level = integer_val_level;
532            break;
533        case current_group_level_code:
534            cur_val = cur_level - level_one;
535            cur_val_level = integer_val_level;
536            break;
537        case current_group_type_code:
538            cur_val = cur_group;
539            cur_val_level = integer_val_level;
540            break;
541        case current_stack_size_code:
542            cur_val = lmt_save_state.save_stack_data.ptr;
543            cur_val_level = integer_val_level;
544            break;
545        case current_if_level_code:
546            {
547                halfword q = lmt_condition_state.cond_ptr;
548                cur_val = 0;
549                while (q) {
550                    ++cur_val;
551                    q = node_next(q);
552                }
553                cur_val_level = integer_val_level;
554                break;
555            }
556        case current_if_type_code:
557            {
558                /*tex
559                    We have more conditions than standard \TEX\ and \ETEX\ and the order is also somewhat
560                    different. One problem is that in \ETEX\ a zero means \quotation {not in an test}, so
561                    we're one off! Not that it matters much as this feature is probably never really used,
562                    but we kept if for compatibility reasons. But it's gone now ... as usual with some
563                    sentiment as it was nicely abstracted cleaned up code.
564                */
565                cur_val = lmt_condition_state.cond_ptr ? (lmt_condition_state.cur_if - first_real_if_test_code) : -1;
566                cur_val_level = integer_val_level;
567                break;
568            }
569        case current_if_branch_code:
570            {
571                switch (lmt_condition_state.if_limit) {
572                    case if_code:
573                        cur_val = 0;
574                        break;
575                    case fi_code:
576                        cur_val = -1;
577                        break;
578                    case else_code:
579                    case or_code:
580                    case or_else_code:
581                    case or_unless_code:
582                        cur_val = 1;
583                        break;
584                    default:
585                        cur_val = 0;
586                        break;
587                }
588                cur_val_level = integer_val_level;
589                break;
590            }
591        case glue_stretch_order_code:
592        case glue_shrink_order_code:
593            {
594                /*TeX
595                    Not that we need it but \LUATEX\ now has |\eTeXglue..order|. In \CONTEXT\ we're
596                    not using the internal codes anyway (or symbolic constants). In \LUATEX\ there
597                    is some \ETEX\ related shifting but we don't do that here.
598                */
599                halfword q = tex_scan_glue(glue_val_level, 0, 0);
600                cur_val = (code == glue_stretch_order_code) ? glue_stretch_order(q) : glue_shrink_order(q);
601                tex_flush_node(q);
602                cur_val_level = integer_val_level;
603                break;
604            }
605        case font_id_code:
606            {
607                cur_val = tex_scan_font_identifier(NULL);
608                cur_val_level = integer_val_level;
609                break;
610            }
611        case glyph_x_scaled_code:
612            {
613                cur_val = tex_font_x_scaled(tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
614                cur_val_level = dimension_val_level;
615                break;
616            }
617        case glyph_y_scaled_code:
618            {
619                cur_val = tex_font_y_scaled(tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
620                cur_val_level = dimension_val_level;
621                break;
622            }
623        case font_spec_id_code:
624        case font_spec_scale_code:
625        case font_spec_xscale_code:
626        case font_spec_yscale_code:
627        case font_spec_slant_code:
628        case font_spec_weight_code:
629            {
630                halfword fs = tex_scan_fontspec_identifier();
631                if (fs) {
632                    switch (code) {
633                        case font_spec_id_code:
634                            cur_val = font_spec_identifier(fs);
635                            break;
636                        case font_spec_scale_code:
637                            cur_val = font_spec_scale(fs);
638                            break;
639                        case font_spec_xscale_code:
640                            cur_val = font_spec_x_scale(fs);
641                            break;
642                        case font_spec_yscale_code:
643                            cur_val = font_spec_y_scale(fs);
644                            break;
645                        case font_spec_slant_code:
646                            cur_val = font_spec_slant(fs);
647                            break;
648                        case font_spec_weight_code:
649                            cur_val = font_spec_weight(fs);
650                            break;
651                    }
652                } else {
653                    cur_val = 0;
654                }
655                cur_val_level = integer_val_level;
656                break;
657            }
658        case font_char_wd_code:
659        case font_char_ht_code:
660        case font_char_dp_code:
661        case font_char_ic_code:
662        case font_char_ta_code:
663        case font_char_ba_code:
664        case scaled_font_char_wd_code:
665        case scaled_font_char_ht_code:
666        case scaled_font_char_dp_code:
667        case scaled_font_char_ic_code:
668        case scaled_font_char_ta_code:
669        case scaled_font_char_ba_code:
670            {
671                halfword fnt = tex_scan_font_identifier(NULL);
672                halfword chr = tex_scan_char_number(0);
673                if (tex_char_exists(fnt, chr)) {
674                    switch (code) {
675                        case font_char_wd_code:
676                        case scaled_font_char_wd_code:
677                            cur_val = tex_char_width_from_font(fnt, chr);
678                            break;
679                        case font_char_ht_code:
680                        case scaled_font_char_ht_code:
681                            cur_val = tex_char_height_from_font(fnt, chr);
682                            break;
683                        case font_char_dp_code:
684                        case scaled_font_char_dp_code:
685                            cur_val = tex_char_depth_from_font(fnt, chr);
686                            break;
687                        case font_char_ic_code:
688                        case scaled_font_char_ic_code:
689                            cur_val = tex_char_italic_from_font(fnt, chr);
690                            break;
691                        case font_char_ta_code:
692                        case scaled_font_char_ta_code:
693                            cur_val = tex_char_top_anchor_from_font(fnt, chr);
694                            break;
695                        case font_char_ba_code:
696                        case scaled_font_char_ba_code:
697                            cur_val = tex_char_bottom_anchor_from_font(fnt, chr);
698                            break;
699                    }
700                    switch (code) {
701                        case scaled_font_char_wd_code:
702                        case scaled_font_char_ic_code:
703                        case scaled_font_char_ta_code:
704                        case scaled_font_char_ba_code:
705                            cur_val = tex_font_x_scaled(cur_val);
706                            break;
707                        case scaled_font_char_ht_code:
708                        case scaled_font_char_dp_code:
709                            cur_val = tex_font_y_scaled(cur_val);
710                            break;
711                    }
712                } else {
713                    cur_val = 0;
714                }
715                cur_val_level = dimension_val_level;
716                break;
717            }
718        case font_size_code:
719            {
720                halfword fnt = tex_scan_font_identifier(NULL);
721                cur_val = font_size(fnt);
722                cur_val_level = dimension_val_level;
723                break;
724            }
725        case font_math_control_code:
726            {
727                halfword fnt = tex_scan_font_identifier(NULL);
728                cur_val = font_mathcontrol(fnt);
729                cur_val_level = integer_val_level;
730                break;
731            }
732        case font_text_control_code:
733            {
734                halfword fnt = tex_scan_font_identifier(NULL);
735                cur_val = font_textcontrol(fnt);
736                cur_val_level = integer_val_level;
737                break;
738            }
739        case math_scale_code:
740            {
741                halfword fnt = tex_scan_font_identifier(NULL);
742                if (tex_is_valid_font(fnt)) {
743                    cur_val = tex_get_math_font_scale(fnt, tex_math_style_to_size(tex_current_math_style()));
744                } else {
745                    cur_val = 1000;
746                }
747                cur_val_level = integer_val_level;
748                break;
749            }
750        case math_style_code:
751            {
752                cur_val = tex_current_math_style();
753                if (cur_val < 0) {
754                    cur_val = text_style;
755                }
756                cur_val_level = integer_val_level;
757                break;
758            }
759        case math_main_style_code:
760            {
761                cur_val = tex_current_math_main_style();
762                if (cur_val < 0) {
763                    cur_val = text_style;
764                }
765                cur_val_level = integer_val_level;
766                break;
767            }
768        case math_parent_style_code:
769            {
770                cur_val = tex_current_math_parent_style();
771                if (cur_val < 0) {
772                    cur_val = text_style;
773                }
774                cur_val_level = integer_val_level;
775                break;
776            }
777        case math_style_font_id_code:
778            {
779                halfword style = tex_scan_math_style_identifier(0, 0);
780                halfword family = tex_scan_math_family_number();
781                cur_val = tex_fam_fnt(family, tex_size_of_style(style));
782                cur_val_level = integer_val_level;
783                break;
784            }
785        case math_stack_style_code:
786            {
787                cur_val = tex_math_style_variant(cur_list.math_style, math_parameter_stack_variant);
788                if (cur_val < 0) {
789                    cur_val = text_style;
790                }
791                cur_val_level = integer_val_level;
792                break;
793            }
794        case math_char_class_code:
795        case math_char_fam_code:
796        case math_char_slot_code:
797            /* we actually need two commands or we need to look ahead */
798            {
799                mathcodeval mval = tex_no_math_code();
800                mathdictval dval = tex_no_dict_code();
801                if (tex_scan_math_cmd_val(&mval, &dval)) {
802                    switch (code) {
803                        case math_char_class_code:
804                            cur_val = mval.class_value;
805                            break;
806                        case math_char_fam_code:
807                            cur_val = mval.family_value;
808                            break;
809                        case math_char_slot_code:
810                            cur_val = mval.character_value;
811                            break;
812                        default:
813                            cur_val = 0;
814                            break;
815                    }
816                } else {
817                     cur_val = 0;
818                }
819                cur_val_level = integer_val_level;
820                break;
821            }
822        case scaled_slant_per_point_code:
823        case scaled_interword_space_code:
824        case scaled_interword_stretch_code:
825        case scaled_interword_shrink_code:
826        case scaled_ex_height_code:
827        case scaled_em_width_code:
828        case scaled_extra_space_code:
829            {
830                cur_val =  tex_get_scaled_parameter(cur_font_par, (code - scaled_slant_per_point_code + 1));
831                cur_val_level = dimension_val_level;
832                break;
833            }
834        case scaled_math_axis_code:
835        case scaled_math_ex_height_code:
836        case scaled_math_em_width_code:
837            {
838                halfword style = tex_scan_math_style_identifier(0, 0);
839                switch (code) {
840                    case scaled_math_axis_code:
841                        cur_val = tex_math_parameter_x_scaled(style, math_parameter_axis);
842                        break;
843                    case scaled_math_ex_height_code:
844                        cur_val = tex_math_parameter_y_scaled(style, math_parameter_exheight);
845                        break;
846                    case scaled_math_em_width_code:
847                        cur_val = tex_math_parameter_x_scaled(style, math_parameter_quad);
848                        break;
849                }
850                cur_val_level = dimension_val_level;
851                break;
852            }
853        case last_arguments_code:
854            {
855                cur_val = lmt_expand_state.arguments;
856                cur_val_level = integer_val_level;
857                break;
858            }
859        case parameter_count_code:
860            {
861                cur_val = tex_get_parameter_count();
862                cur_val_level = integer_val_level;
863                break;
864            }
865        case parameter_index_code:
866            {
867                cur_val = tex_get_parameter_index(tex_scan_parameter_index());
868                cur_val_level = integer_val_level;
869                break;
870            }
871        /*
872        case lua_value_function_code:
873            {
874                halfword v = scan_integer(0, NULL, NULL);
875                if (v <= 0) {
876                    tex_normal_error("luafunction", "invalid number");
877                } else {
878                    set_cur_val_by_lua_value_cmd(code);
879                }
880                return 1;
881            }
882        */
883        case insert_progress_code:
884            {
885                cur_val = tex_get_insert_progress(tex_scan_integer(0, NULL, NULL));
886                cur_val_level = dimension_val_level;
887                break;
888            }
889        case left_margin_kern_code:
890        case right_margin_kern_code:
891            {
892                halfword v = tex_scan_integer(0, NULL, NULL);
893                halfword b = box_register(v);
894                if (b && (node_type(b) == hlist_node)) {
895                    if (code == left_margin_kern_code) {
896                        cur_val = tex_left_marginkern(box_list(b));
897                    } else {
898                        cur_val = tex_right_marginkern(box_list(b));
899                    }
900                } else {
901                    tex_normal_error("marginkern", "a hbox expected");
902                    cur_val = 0;
903                }
904                cur_val_level = dimension_val_level;
905                break;
906            }
907        case par_shape_length_code:
908            {
909                cur_val = par_shape_par ? specification_count(par_shape_par) : 0;
910                cur_val_level = integer_val_level;
911                break;
912            }
913        case par_shape_indent_code:
914        case par_shape_width_code:
915            {
916                halfword shape = par_shape_par;
917                if (shape) {
918                    halfword index = tex_scan_integer(0, NULL, NULL);
919                    if (index >= 1 && index <= specification_count(shape)) {
920                        cur_val = code == par_shape_indent_code ? tex_get_specification_indent(shape, index) : tex_get_specification_width(shape, index);
921                    } else {
922                        cur_val = 0;
923                    }
924                } else {
925                    cur_val = 0;
926                }
927                cur_val_level = dimension_val_level;
928                break;
929            }
930        case balance_shape_vsize_code:
931        case balance_shape_top_space_code:
932        case balance_shape_bottom_space_code:
933            {
934                halfword shape = balance_shape_par;
935                if (shape) {
936                    halfword index = tex_scan_integer(0, NULL, NULL);
937                    if (index >= 1) {
938                        switch (code) {
939                            case balance_shape_vsize_code:
940                                cur_val = tex_get_balance_vsize(shape, index);
941                                break;
942                            case balance_shape_top_space_code:
943                                cur_val = glue_amount(tex_get_balance_topskip(shape, index));
944                                break;
945                            case balance_shape_bottom_space_code:
946                                cur_val = glue_amount(tex_get_balance_bottomskip(shape, index));
947                                break;
948                        }
949                    } else {
950                        cur_val = 0;
951                    }
952                } else {
953                    cur_val = 0;
954                }
955                cur_val_level = dimension_val_level;
956                break;
957            }
958        case glue_stretch_code:
959        case glue_shrink_code:
960            {
961                halfword q = tex_scan_glue(glue_val_level, 0, 0);
962                cur_val = code == glue_stretch_code ? glue_stretch(q) : glue_shrink(q);
963                tex_flush_node(q);
964                cur_val_level = dimension_val_level;
965                break;
966            }
967        case mu_to_glue_code:
968            cur_val = tex_scan_glue(muglue_val_level, 0, 0);
969            cur_val_level = glue_val_level;
970            return 1;
971        case glue_to_mu_code:
972            cur_val = tex_scan_glue(glue_val_level, 0, 0);
973            cur_val_level = muglue_val_level;
974            return 1;
975        case numexpr_code:
976     /* case attrexpr_code: */
977            tex_aux_scan_expr(integer_val_level, 0);
978            return 1;
979        case posexpr_code:
980            tex_aux_scan_expr(posit_val_level, 0);
981            return 1;
982        case dimexpr_code:
983            tex_aux_scan_expr(dimension_val_level, 0);
984            return 1;
985        case glueexpr_code:
986            tex_aux_scan_expr(glue_val_level, 0);
987            return 1;
988        case muexpr_code:
989            tex_aux_scan_expr(muglue_val_level, 0);
990            return 1;
991        case numexpression_code:
992            tex_aux_scan_integer_expression(0);
993            return 1;
994        case dimexpression_code:
995            tex_aux_scan_dimension_expression(0);
996            return 1;
997        case numexperimental_code:
998            tex_aux_scan_expression(integer_val_level, 0);
999            return 1;
1000        case dimexperimental_code:
1001            tex_aux_scan_expression(dimension_val_level, 0);
1002            return 1;
1003     // case dimen_to_scale_code:
1004     //     cur_val_level = integer_val_level;
1005     //     cur_val = round_xn_over_d(100, scan_dimension(0, 0, 0, 0, NULL), 65536);
1006     //     return 1;
1007        case numeric_scale_code:
1008            cur_val_level = integer_val_level;
1009            cur_val = tex_scan_scale(0);
1010            return 1;
1011        case numeric_scaled_code:
1012            {
1013                scaled n = tex_scan_scale(0);
1014                scaled i = tex_scan_integer(0, NULL, NULL);
1015                cur_val_level = integer_val_level;
1016                cur_val = tex_xn_over_d(i, n, scaling_factor);
1017            }
1018            return 1;
1019        case index_of_register_code:
1020            cur_val = tex_aux_scan_register_index();
1021            cur_val_level = integer_val_level;
1022            return 1;
1023        case index_of_character_code:
1024            cur_val = tex_aux_scan_character_index();
1025            cur_val_level = integer_val_level;
1026            return 1;
1027        case last_chk_integer_code:
1028            cur_val_level = integer_val_level;
1029            cur_val = lmt_condition_state.chk_integer;
1030            return 1;
1031        case last_chk_dimension_code:
1032            cur_val_level = dimension_val_level;
1033            cur_val = lmt_condition_state.chk_dimension;
1034            return 1;
1035        case last_left_class_code:
1036            cur_val_level = integer_val_level;
1037            cur_val = lmt_math_state.last_left;
1038            if (! valid_math_class_code(cur_val)) {
1039                cur_val = unset_noad_class;
1040            }
1041            return 1;
1042        case last_right_class_code:
1043            cur_val_level = integer_val_level;
1044            cur_val = lmt_math_state.last_right;
1045            if (! valid_math_class_code(cur_val)) {
1046                cur_val = unset_noad_class;
1047            }
1048            return 1;
1049        case last_atom_class_code:
1050            cur_val_level = integer_val_level;
1051            cur_val = lmt_math_state.last_atom;
1052            if (! valid_math_class_code(cur_val)) {
1053                cur_val = unset_noad_class;
1054            }
1055            return 1;
1056        case nested_loop_iterator_code:
1057            cur_val = tex_nested_loop_iterator();
1058            cur_val_level = integer_val_level;
1059            return 1;
1060        case previous_loop_iterator_code:
1061            cur_val = tex_previous_loop_iterator();
1062            cur_val_level = integer_val_level;
1063            return 1;
1064        case current_loop_iterator_code:
1065        case last_loop_iterator_code:
1066            cur_val_level = integer_val_level;
1067            cur_val = lmt_main_control_state.loop_iterator;
1068            return 1;
1069        case current_loop_nesting_code:
1070            cur_val_level = integer_val_level;
1071            cur_val = lmt_main_control_state.loop_nesting;
1072            return 1;
1073        case last_par_trigger_code:
1074            cur_val_level = integer_val_level;
1075            cur_val = lmt_main_control_state.last_par_trigger;
1076            return 1;
1077        case last_par_context_code:
1078            cur_val_level = integer_val_level;
1079            cur_val = lmt_main_control_state.last_par_context;
1080            return 1;
1081        case last_page_extra_code:
1082            cur_val_level = integer_val_level;
1083            cur_val = lmt_page_builder_state.last_extra_used;
1084            return 1;
1085        case math_atom_glue_code:
1086            {
1087                halfword style = tex_scan_math_style_identifier(0, 0);
1088                halfword leftclass = tex_scan_math_class_number(0);
1089                halfword rightclass = tex_scan_math_class_number(0);
1090                cur_val = tex_math_spacing_glue(leftclass, rightclass, style);
1091                cur_val_level = muglue_val_level;
1092                break;
1093            }
1094    }
1095    return 0;
1096}
1097
1098static void tex_aux_set_cur_val_by_auxiliary_cmd(int code)
1099{
1100    switch (code) {
1101        case space_factor_code:
1102            if (is_h_mode(cur_list.mode)) {
1103                cur_val = cur_list.space_factor;
1104            } else {
1105                tex_handle_error(normal_error_type, "Improper %C", auxiliary_cmd, code,
1106                    "You can refer to \\spacefactor only in horizontal mode and not in \n"
1107                    "inside \\write. So I'm forgetting what you said and using zero instead."
1108                );
1109                cur_val = 0;
1110            }
1111            cur_val_level = integer_val_level;
1112            break;
1113        case prev_depth_code:
1114            if (is_v_mode(cur_list.mode)) {
1115                cur_val = cur_list.prev_depth;
1116            } else {
1117                tex_handle_error(normal_error_type, "Improper %C", auxiliary_cmd, code,
1118                    "You can refer to \\prevdepth only in horizontal mode and not in \n"
1119                    "inside \\write. So I'm forgetting what you said and using zero instead."
1120                );
1121                cur_val = 0;
1122            }
1123            cur_val_level = dimension_val_level;
1124            break;
1125        case prev_graf_code:
1126            if (cur_list.mode == nomode) {
1127                /*tex So |prev_graf=0| within |\write|, not that we have that. */
1128                cur_val = 0;
1129            } else {
1130                cur_val = lmt_nest_state.nest[tex_vmode_nest_index()].prev_graf;
1131            }
1132            cur_val_level = integer_val_level;
1133            break;
1134        case interaction_mode_code:
1135            cur_val = lmt_error_state.interaction;
1136            cur_val_level = integer_val_level;
1137            break;
1138        case insert_mode_code:
1139            cur_val = lmt_insert_state.mode;
1140            cur_val_level = integer_val_level;
1141            break;
1142    }
1143}
1144
1145/*tex
1146    For penalty specifications a zero count will report the number of entries. For the others we
1147    always report that value. New is that negative numbers will count from the end.
1148*/
1149
1150static void tex_aux_set_cur_val_by_specification_cmd(int code)
1151{
1152    halfword spec = eq_value(code);
1153    cur_val = spec ? tex_aux_get_specification_value(spec, code) : 0;
1154    cur_val_level = integer_val_level;
1155}
1156
1157# define page_state_okay (lmt_page_builder_state.contents == contribute_nothing && ! lmt_page_builder_state.output_active)
1158
1159static void tex_aux_set_cur_val_by_page_property_cmd(int code)
1160{
1161    switch (code) {
1162        case page_goal_code:
1163            cur_val = page_state_okay ? max_dimension : lmt_page_builder_state.goal;
1164            cur_val_level = dimension_val_level;
1165            break;
1166        case page_vsize_code:
1167            cur_val = page_state_okay ? 0 : lmt_page_builder_state.vsize;
1168            cur_val_level = dimension_val_level;
1169            break;
1170        case page_total_code:
1171            cur_val = page_state_okay ? 0 : lmt_page_builder_state.total;
1172            cur_val_level = dimension_val_level;
1173            break;
1174        case page_excess_code:
1175            cur_val = page_state_okay ? 0 : lmt_page_builder_state.excess;
1176            cur_val_level = dimension_val_level;
1177            break;
1178        case page_depth_code:
1179            cur_val = page_state_okay ? 0 : lmt_page_builder_state.depth;
1180            cur_val_level = dimension_val_level;
1181            break;
1182        case page_stretch_code:
1183            cur_val = page_state_okay ? 0 : lmt_page_builder_state.stretch;
1184            cur_val_level = dimension_val_level;
1185            break;
1186        case page_fistretch_code:
1187            cur_val = page_state_okay ? 0 : lmt_page_builder_state.fistretch;
1188            cur_val_level = dimension_val_level;
1189            break;
1190        case page_filstretch_code:
1191            cur_val = page_state_okay ? 0 : lmt_page_builder_state.filstretch;
1192            cur_val_level = dimension_val_level;
1193            break;
1194        case page_fillstretch_code:
1195            cur_val = page_state_okay ? 0 : lmt_page_builder_state.fillstretch;
1196            cur_val_level = dimension_val_level;
1197            break;
1198        case page_filllstretch_code:
1199            cur_val = page_state_okay ? 0 : lmt_page_builder_state.filllstretch;
1200            cur_val_level = dimension_val_level;
1201            break;
1202        case page_shrink_code:
1203            cur_val = page_state_okay ? 0 : lmt_page_builder_state.shrink;
1204            cur_val_level = dimension_val_level;
1205            break;
1206        case page_last_height_code:
1207            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_height;
1208            cur_val_level = dimension_val_level;
1209            break;
1210        case page_last_depth_code:
1211            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_depth;
1212            cur_val_level = dimension_val_level;
1213            break;
1214        case page_last_stretch_code:
1215            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_stretch;
1216            cur_val_level = dimension_val_level;
1217            break;
1218        case page_last_fistretch_code:
1219            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_fistretch;
1220            cur_val_level = dimension_val_level;
1221            break;
1222        case page_last_filstretch_code:
1223            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_filstretch;
1224            cur_val_level = dimension_val_level;
1225            break;
1226        case page_last_fillstretch_code:
1227            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_fillstretch;
1228            cur_val_level = dimension_val_level;
1229            break;
1230        case page_last_filllstretch_code:
1231            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_filllstretch;
1232            cur_val_level = dimension_val_level;
1233            break;
1234        case page_last_shrink_code:
1235            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_shrink;
1236            cur_val_level = dimension_val_level;
1237            break;
1238        case dead_cycles_code:
1239            cur_val = lmt_page_builder_state.dead_cycles;
1240            cur_val_level = integer_val_level;
1241            break;
1242        case insert_penalties_code:
1243            cur_val = lmt_page_builder_state.insert_penalties;
1244            cur_val_level = integer_val_level;
1245            break;
1246        case insert_heights_code:
1247            cur_val = lmt_page_builder_state.insert_heights;
1248            cur_val_level = dimension_val_level;
1249            break;
1250        case insert_storing_code:
1251            cur_val = lmt_insert_state.storing;
1252            cur_val_level = integer_val_level;
1253            break;
1254        case insert_distance_code:
1255            cur_val = tex_get_insert_distance(tex_scan_integer(0, NULL, NULL));
1256            cur_val_level = glue_val_level;
1257            break;
1258        case insert_multiplier_code:
1259            cur_val = tex_get_insert_multiplier(tex_scan_integer(0, NULL, NULL));
1260            cur_val_level = integer_val_level;
1261            break;
1262        case insert_limit_code:
1263            cur_val = tex_get_insert_limit(tex_scan_integer(0, NULL, NULL));
1264            cur_val_level = dimension_val_level;
1265            break;
1266        case insert_storage_code:
1267            cur_val = tex_get_insert_storage(tex_scan_integer(0, NULL, NULL));
1268            cur_val_level = integer_val_level;
1269            break;
1270        case insert_penalty_code:
1271            cur_val = tex_get_insert_penalty(tex_scan_integer(0, NULL, NULL));
1272            cur_val_level = integer_val_level;
1273            break;
1274        case insert_maxdepth_code:
1275            cur_val = tex_get_insert_maxdepth(tex_scan_integer(0, NULL, NULL));
1276            cur_val_level = dimension_val_level;
1277            break;
1278        case insert_height_code:
1279            cur_val = tex_get_insert_height(tex_scan_integer(0, NULL, NULL));
1280            cur_val_level = dimension_val_level;
1281            break;
1282        case insert_depth_code:
1283            cur_val = tex_get_insert_depth(tex_scan_integer(0, NULL, NULL));
1284            cur_val_level = dimension_val_level;
1285            break;
1286        case insert_width_code:
1287            cur_val = tex_get_insert_width(tex_scan_integer(0, NULL, NULL));
1288            cur_val_level = dimension_val_level;
1289            break;
1290        case insert_line_height_code:
1291            cur_val = tex_get_insert_line_height(tex_scan_integer(0, NULL, NULL));
1292            cur_val_level = dimension_val_level;
1293            break;
1294        case insert_line_depth_code:
1295            cur_val = tex_get_insert_line_depth(tex_scan_integer(0, NULL, NULL));
1296            cur_val_level = dimension_val_level;
1297            break;
1298        case insert_stretch_code:
1299            cur_val = tex_get_insert_stretch(tex_scan_integer(0, NULL, NULL));
1300            cur_val_level = dimension_val_level;
1301            break;
1302        case insert_shrink_code:
1303            cur_val = tex_get_insert_shrink(tex_scan_integer(0, NULL, NULL));
1304            cur_val_level = dimension_val_level;
1305            break;
1306        case split_last_depth_code:  
1307            cur_val = lmt_packaging_state.split_last_depth;
1308            cur_val_level = dimension_val_level;
1309            break;
1310        case split_last_height_code: 
1311            cur_val = lmt_packaging_state.split_last_height;
1312            cur_val_level = dimension_val_level;
1313            break;
1314        case split_last_shrink_code: 
1315            cur_val = lmt_packaging_state.split_last_shrink;
1316            cur_val_level = dimension_val_level;
1317            break;
1318        case split_last_stretch_code:
1319            cur_val = lmt_packaging_state.split_last_stretch;
1320            cur_val_level = dimension_val_level;
1321            break;
1322        case mvl_currently_active_code:
1323            cur_val = tex_current_mvl(NULL, NULL);
1324            cur_val_level = integer_val_level;
1325            break;
1326        default:
1327            tex_confusion("page property");
1328            break;
1329    }
1330}
1331
1332static void tex_aux_set_cur_val_by_define_char_cmd(int code)
1333{
1334    halfword index = tex_scan_char_number(0);
1335    switch (code) {
1336        case catcode_charcode:
1337            code = tex_get_cat_code(cat_code_table_par, index);
1338            break;
1339        case lccode_charcode:
1340            code = tex_get_lc_code(index);
1341            break;
1342        case uccode_charcode:
1343            code = tex_get_uc_code(index);
1344            break;
1345        case sfcode_charcode:
1346            code = tex_get_sf_code(index);
1347            break;
1348        case hccode_charcode:
1349            code = tex_get_hc_code(index);
1350            break;
1351        case hmcode_charcode:
1352            code = tex_get_hm_code(index);
1353            break;
1354        case amcode_charcode:
1355            code = tex_get_am_code(index);
1356            break;
1357        case cccode_charcode:
1358            code = tex_get_cc_code(index);
1359            break;
1360        case mathcode_charcode:
1361        case extmathcode_charcode:
1362            code = tex_get_math_code_number(index);
1363            break;
1364        case delcode_charcode:
1365        case extdelcode_charcode:
1366            code = tex_get_del_code_number(index);
1367            break;
1368        default:
1369            tex_confusion("scan char");
1370            break;
1371    }
1372    cur_val = code;
1373    cur_val_level = integer_val_level;
1374}
1375
1376/*
1377    First, here is a short routine that is called from lua code. All the real work is delegated to
1378    |short_scan_something_internal| that is shared between this routine and |scan_something_internal|.
1379    In the end it was much cleaner to integrate |tex_aux_short_scan_something_internal| into the two
1380    switches.
1381*/
1382
1383static halfword tex_aux_scan_math_style_number(halfword code)
1384{
1385    switch (code) {
1386        case yet_unset_math_style:
1387            return tex_scan_math_style_identifier(0, 0);
1388        case scaled_math_style:
1389            return cur_list.math_scale;
1390        case former_choice_math_style:
1391            return 0;
1392        default:
1393            return code;
1394    }
1395}
1396
1397static void tex_aux_set_cur_val_by_math_style_cmd(halfword code)
1398{
1399    cur_val = tex_aux_scan_math_style_number(code);
1400    cur_val_level = integer_val_level;
1401}
1402
1403/*tex
1404
1405    OK, we're ready for |scan_something_internal| itself. A second parameter, |negative|, is set
1406    |true| if the value that is found should be negated. It is assumed that |cur_cmd| and |cur_chr|
1407    represent the first token of the internal quantity to be scanned; an error will be signalled if
1408    |cur_cmd < min_internal| or |cur_cmd > max_internal|.
1409
1410*/
1411
1412/*tex Fetch an internal parameter: */
1413
1414static void tex_aux_missing_number_error(int where)
1415{
1416    tex_handle_error(
1417        back_error_type,
1418        "Missing number, case %i, treated as zero", where,
1419        "A number should have been here; I inserted '0'. (If you can't figure out why I\n"
1420        "needed to see a number, look up 'weird error' in the index to The TeXbook.)"
1421    );
1422}
1423
1424static void tex_aux_scan_dimension_out_of_range_error(int where) {
1425    tex_handle_error(
1426        normal_error_type,
1427        "Dimension too large, case %i", where, 
1428        "I can't work with sizes bigger than about 19 feet (45 Theodores as of 2023),\n"
1429        "575 centimeters, 2300 Toves, 230 Ediths or 16383 points. Continue and I'll use\n"
1430        "the largest value I can."
1431    );
1432}
1433
1434static void tex_aux_scan_integer_out_of_range_error(int where) {
1435    tex_handle_error(
1436        normal_error_type,
1437        "Number too large, case %i", where, 
1438        "I can only go up to 2147483647 = '17777777777 = \"7FFFFFFF, so I'm using that\n"
1439        "number instead of yours."
1440    );
1441}
1442
1443static void tex_aux_improper_constant_error(void)
1444{
1445    tex_handle_error(
1446        back_error_type,
1447        "Improper alphabetic constant",
1448        "A one-character control sequence belongs after a ` mark. So I'm essentially\n"
1449        "inserting \\0 here."
1450    );
1451}
1452
1453/* todo: get rid of cur_val */
1454
1455// static int tex_aux_valid_tok_level(halfword level)
1456// {
1457//     if (level == token_val_level) {
1458//         return 1;
1459//     } else {
1460//         if (lmt_error_state.intercept) {
1461//             lmt_error_state.last_intercept = 1 ;
1462//         } else {
1463//             tex_aux_missing_number_error();
1464//         }
1465//         cur_val = 0;
1466//         cur_val_level = dimension_val_level; /* why dimen */
1467//         return 0;
1468//     }
1469// }
1470
1471static int tex_aux_scan_hyph_data_number(halfword code, halfword *target)
1472{
1473    switch (code) {
1474        case prehyphenchar_code:
1475            *target = tex_get_pre_hyphen_char(language_par);
1476            break;
1477        case posthyphenchar_code:
1478            *target = tex_get_post_hyphen_char(language_par);
1479            break;
1480        case preexhyphenchar_code:
1481            *target = tex_get_pre_exhyphen_char(language_par);
1482            break;
1483        case postexhyphenchar_code:
1484            *target = tex_get_post_exhyphen_char(language_par);
1485            break;
1486        case hyphenationmin_code:
1487            *target = tex_get_hyphenation_min(language_par);
1488            break;
1489        case hjcode_code:
1490            *target = tex_get_hj_code(language_par, tex_scan_integer(0, NULL, NULL));
1491            break;
1492        default:
1493            return 0;
1494    }
1495    return 1;
1496}
1497
1498static void tex_aux_set_cur_val_by_math_parameter_cmd(halfword chr)
1499{
1500    switch (chr) {
1501        case math_parameter_reset_spacing:
1502            /* or just zero */
1503        case math_parameter_set_spacing:
1504        case math_parameter_let_spacing:
1505        case math_parameter_copy_spacing:
1506            {
1507                halfword left = tex_scan_math_class_number(0);
1508                halfword right = tex_scan_math_class_number(0);
1509                halfword style = tex_scan_math_style_identifier(0, 0);
1510                halfword node = tex_math_spacing_glue(left, right, style);
1511                cur_val = node ? node : zero_glue;
1512                cur_val_level = muglue_val_level;
1513                break;
1514            }
1515        case math_parameter_set_atom_rule:
1516        case math_parameter_let_atom_rule:
1517        case math_parameter_copy_atom_rule:
1518        // case math_parameter_let_parent:
1519        case math_parameter_copy_parent:
1520        case math_parameter_set_defaults:
1521            {
1522                // cur_val = 0;
1523                // cur_val_level = integer_val_level;
1524                break;
1525            }
1526        case math_parameter_let_parent:
1527            {
1528                halfword mathclass = tex_scan_math_class_number(0);
1529                if (valid_math_class_code(mathclass)) {
1530                    cur_val = tex_math_has_class_parent(mathclass);
1531                    cur_val_level = integer_val_level;
1532                }
1533                break;
1534            }
1535        case math_parameter_set_pre_penalty:
1536        case math_parameter_set_post_penalty:
1537        case math_parameter_set_display_pre_penalty:
1538        case math_parameter_set_display_post_penalty:
1539            {
1540                halfword mathclass = tex_scan_math_class_number(0);
1541                if (valid_math_class_code(mathclass)) {
1542                    switch (chr) {
1543                        case math_parameter_set_pre_penalty:
1544                            cur_val = count_parameter(first_math_pre_penalty_code + mathclass);
1545                            break;
1546                        case math_parameter_set_post_penalty:
1547                            cur_val = count_parameter(first_math_post_penalty_code + mathclass);
1548                            break;
1549                        case math_parameter_set_display_pre_penalty:
1550                            cur_val = count_parameter(first_math_display_pre_penalty_code + mathclass);
1551                            break;
1552                        case math_parameter_set_display_post_penalty:
1553                            cur_val = count_parameter(first_math_display_post_penalty_code + mathclass);
1554                            break;
1555                    }
1556                } else {
1557                    cur_val = 0;
1558                }
1559                cur_val_level = integer_val_level;
1560                break;
1561            }
1562        case math_parameter_ignore:
1563            {
1564                halfword code = tex_scan_math_parameter();
1565                cur_val = code >= 0 ? count_parameter(first_math_ignore_code + code) : 0;
1566                cur_val_level = integer_val_level;
1567                break;
1568            }
1569        case math_parameter_options:
1570            {
1571                halfword mathclass = tex_scan_math_class_number(0);
1572                if (valid_math_class_code(mathclass)) {
1573                    cur_val = count_parameter(first_math_options_code + mathclass);
1574                } else {
1575                    cur_val = 0;
1576                }
1577                break;
1578            }
1579        default:
1580            {
1581                cur_val = tex_scan_math_style_identifier(0, 0);
1582                switch (math_parameter_value_type(chr)) {
1583                    case math_integer_parameter:
1584                        cur_val_level = integer_val_level;
1585                        break;
1586                    case math_dimension_parameter:
1587                        cur_val_level = dimension_val_level;
1588                        break;
1589                    case math_muglue_parameter:
1590                        cur_val_level = muglue_val_level;
1591                        break;
1592                    case math_style_parameter:
1593                        cur_val_level = integer_val_level;
1594                        break;
1595                }
1596                chr = tex_get_math_parameter(cur_val, chr, NULL);
1597                if (cur_val_level == muglue_val_level) {
1598                    switch (chr) {
1599                        case petty_muskip_code:
1600                            chr = petty_muskip_par;
1601                            break;
1602                        case tiny_muskip_code:
1603                            chr = tiny_muskip_par;
1604                            break;
1605                        case thin_muskip_code:
1606                            chr = thin_muskip_par;
1607                            break;
1608                        case med_muskip_code:
1609                            chr = med_muskip_par;
1610                            break;
1611                        case thick_muskip_code:
1612                            chr = thick_muskip_par;
1613                            break;
1614                    }
1615                }
1616                cur_val = chr;
1617                break;
1618            }
1619    }
1620}
1621
1622static void tex_aux_set_cur_val_by_box_property_cmd(halfword chr)
1623{
1624    /*tex We hike on the dimen_cmd but some are integers. */
1625    halfword n = tex_scan_box_register_number();
1626    halfword b = box_register(n);
1627    switch (chr) {
1628        case box_width_code:
1629            cur_val = b ? box_width(b) : 0;
1630            cur_val_level = dimension_val_level;
1631            break;
1632        case box_height_code:
1633            cur_val = b ? box_height(b) : 0;
1634            cur_val_level = dimension_val_level;
1635            break;
1636        case box_depth_code:
1637            cur_val = b ? box_depth(b) : 0;
1638            cur_val_level = dimension_val_level;
1639            break;
1640        case box_direction_code:
1641            cur_val = b ? box_dir(b) : 0;
1642            cur_val_level = integer_val_level;
1643            break;
1644        case box_geometry_code:
1645            cur_val = b ? box_geometry(b) : 0;
1646            cur_val_level = integer_val_level;
1647            break;
1648        case box_orientation_code:
1649            cur_val = b ? box_orientation(b) : 0;
1650            cur_val_level = integer_val_level;
1651            break;
1652        case box_anchor_code:
1653        case box_anchors_code:
1654            cur_val = b ? box_anchor(b) : 0;
1655            cur_val_level = integer_val_level;
1656            break;
1657        case box_source_code:
1658            cur_val = b ? box_source_anchor(b) : 0;
1659            cur_val_level = integer_val_level;
1660            break;
1661        case box_target_code:
1662            cur_val = b ? box_target_anchor(b) : 0;
1663            cur_val_level = integer_val_level;
1664            break;
1665        case box_xoffset_code:
1666            cur_val = b ? box_x_offset(b) : 0;
1667            cur_val_level = dimension_val_level;
1668            break;
1669        case box_yoffset_code:
1670            cur_val = b ? box_y_offset(b) : 0;
1671            cur_val_level = dimension_val_level;
1672            break;
1673        case box_xmove_code:
1674            cur_val = b ? (box_width(b) - box_x_offset(b)) : 0;
1675            cur_val_level = dimension_val_level;
1676            break;
1677        case box_ymove_code:
1678            cur_val = b ? (box_total(b) - box_y_offset(b)) : 0;
1679            cur_val_level = dimension_val_level;
1680            break;
1681        case box_total_code:
1682            cur_val = b ? box_total(b) : 0;
1683            cur_val_level = dimension_val_level;
1684            break;
1685        case box_shift_code:
1686            cur_val = b ? box_shift_amount(b) : 0;
1687            cur_val_level = dimension_val_level;
1688            break;
1689        case box_adapt_code:
1690            cur_val = 0;
1691            cur_val_level = integer_val_level;
1692            break;
1693        case box_repack_code:
1694            if (node_type(b) == hlist_node) {
1695                cur_val = box_list(b) ? tex_natural_hsize(box_list(b), NULL) : 0;
1696            } else {
1697                cur_val = box_list(b) ? tex_natural_vsize(box_list(b)) : 0;
1698            }
1699            cur_val_level = dimension_val_level;
1700            break;
1701        case box_stretch_code:
1702            cur_val = box_list(b) ? tex_stretch(b) : 0;
1703            cur_val_level = dimension_val_level;
1704            break;
1705        case box_shrink_code:
1706            cur_val = box_list(b) ? tex_shrink(b) : 0;
1707            cur_val_level = dimension_val_level;
1708            break;
1709        case box_subtype_code:
1710            cur_val = node_subtype(b);
1711            cur_val_level = integer_val_level;
1712            break;
1713        case box_freeze_code:
1714            cur_val = node_type(b) == hlist_node ? box_width(b) : box_total(b);
1715            cur_val_level = dimension_val_level;
1716            break;
1717        case box_migrate_code:
1718            cur_val = 0;
1719            if (node_type(b) == hlist_node) {
1720                if (box_pre_migrated(b))  { cur_val |= auto_migrate_pre;  }
1721                if (box_post_migrated(b)) { cur_val |= auto_migrate_post; }
1722            }
1723            cur_val_level = integer_val_level;
1724            break;
1725        case box_limitate_code:
1726            /* todo: return the delta */
1727            cur_val = node_type(b) == hlist_node ? box_width(b) : box_total(b);
1728            cur_val_level = dimension_val_level;
1729            break;
1730        case box_finalize_code:
1731            /* todo: return what? */
1732            cur_val = node_type(b) == hlist_node ? box_width(b) : box_total(b);
1733            cur_val_level = dimension_val_level;
1734            break;
1735        case box_limit_code:
1736            /* todo: return the delta */
1737            cur_val = node_type(b) == hlist_node ? box_width(b) : box_total(b);
1738            cur_val_level = dimension_val_level;
1739            break;
1740        case box_attribute_code:
1741            {
1742                halfword att = tex_scan_attribute_register_number();
1743                cur_val = b ? tex_has_attribute(b, att, unused_attribute_value) : unused_attribute_value; /* always b */
1744                cur_val_level = integer_val_level;
1745                break;
1746            }
1747        case box_vadjust_code:
1748            cur_val = 0;
1749            if (b) {
1750                if (box_pre_adjusted(b)) {
1751                    cur_val |= has_pre_adjust;
1752                }
1753                if (box_post_adjusted(b)) {
1754                    cur_val |= has_post_adjust;
1755                }
1756                if (box_pre_migrated(b)) {
1757                    cur_val |= has_pre_migrated;
1758                }
1759                if (box_post_migrated(b)) {
1760                    cur_val |= has_post_migrated;
1761                }
1762            }
1763            cur_val_level = integer_val_level;
1764            break;
1765        case box_inserts_code:
1766            cur_val = tex_identify_inserts(b, 0); /* todo: optional callback id */
1767            cur_val_level = integer_val_level;
1768            break;
1769    }
1770}
1771
1772static void tex_aux_set_cur_val_by_font_property_cmd(halfword chr)
1773{
1774    switch (chr) {
1775        case font_hyphen_code:
1776            {
1777                halfword fnt = tex_scan_font_identifier(NULL);
1778                cur_val = font_hyphen_char(fnt);
1779                cur_val_level = integer_val_level;
1780                break;
1781            }
1782        case font_skew_code:
1783            {
1784                halfword fnt = tex_scan_font_identifier(NULL);
1785                cur_val = font_skew_char(fnt);
1786                cur_val_level = integer_val_level;
1787                break;
1788            }
1789        case font_lp_code:
1790            {
1791                halfword fnt = tex_scan_font_identifier(NULL);
1792                halfword chr = tex_scan_char_number(0);
1793                cur_val = tex_char_lp_from_font(fnt, chr);
1794                cur_val_level = dimension_val_level;
1795                break;
1796            }
1797        case font_rp_code:
1798            {
1799                halfword fnt = tex_scan_font_identifier(NULL);
1800                halfword chr = tex_scan_char_number(0);
1801                cur_val = tex_char_rp_from_font(fnt, chr);
1802                cur_val_level = dimension_val_level;
1803                break;
1804            }
1805        case font_ef_code:
1806            {
1807                halfword fnt = tex_scan_font_identifier(NULL);
1808                halfword chr = tex_scan_char_number(0);
1809                cur_val = tex_char_ef_from_font(fnt, chr);
1810                cur_val_level = integer_val_level;
1811                break;
1812            }
1813        case font_cf_code:
1814            {
1815                halfword fnt = tex_scan_font_identifier(NULL);
1816                halfword chr = tex_scan_char_number(0);
1817                cur_val = tex_char_cf_from_font(fnt, chr);
1818                cur_val_level = integer_val_level;
1819                break;
1820            }
1821        case font_dimension_code:
1822            {
1823                cur_val = tex_get_font_dimension();
1824                cur_val_level = dimension_val_level;
1825                break;
1826            }
1827        case scaled_font_dimension_code:
1828            {
1829                cur_val = tex_get_scaled_font_dimension();
1830                cur_val_level = dimension_val_level;
1831                break;
1832            }
1833    }
1834}
1835
1836static void tex_aux_set_cur_val_by_register_cmd(halfword chr)
1837{
1838    switch (chr) {
1839        case integer_val_level:
1840            {
1841                halfword n = tex_scan_integer_register_number();
1842                cur_val = count_register(n);
1843                break;
1844            }
1845        case attribute_val_level:
1846            {
1847                halfword n = tex_scan_attribute_register_number();
1848                cur_val = attribute_register(n);
1849                break;
1850            }
1851        case posit_val_level:
1852            {
1853                halfword n = tex_scan_posit_register_number();
1854                cur_val = posit_register(n);
1855                break;
1856            }
1857        case dimension_val_level:
1858            {
1859                scaled n = tex_scan_dimension_register_number();
1860                cur_val = dimension_register(n);
1861                break;
1862            }
1863        case glue_val_level:
1864            {
1865                halfword n = tex_scan_glue_register_number();
1866                cur_val = skip_register(n);
1867                break;
1868            }
1869        case muglue_val_level:
1870            {
1871                halfword n = tex_scan_muglue_register_number();
1872                cur_val = muskip_register(n);
1873                break;
1874            }
1875        case token_val_level:
1876            {
1877                halfword n = tex_scan_toks_register_number();
1878                cur_val = toks_register(n);
1879                break;
1880            }
1881    }
1882    cur_val_level = chr;
1883}
1884
1885static void tex_aux_set_cur_val_by_math_spec_cmd(halfword chr)
1886{
1887    cur_val = chr;
1888    if (chr) {
1889        switch (node_subtype(chr)) {
1890            case tex_mathcode:
1891                cur_val = math_spec_value(chr);
1892                cur_val_level = integer_val_level;
1893                break;
1894            case umath_mathcode:
1895            /* case umathnum_mathcode: */
1896            case mathspec_mathcode:
1897                cur_val_level = mathspec_val_level;
1898                break;
1899            default:
1900                cur_val = 0;
1901                cur_val_level = integer_val_level;
1902                break;
1903        }
1904    } else {
1905        cur_val_level = integer_val_level;
1906    }
1907}
1908
1909static halfword tex_aux_scan_something_internal(halfword cmd, halfword chr, int level, int negative, halfword property)
1910{
1911    int succeeded = 1;
1912    switch (cmd) {
1913        /* begin of tex_aux_short_scan_something_internal */
1914        case char_given_cmd:
1915            cur_val = chr;
1916            cur_val_level = integer_val_level;
1917            break;
1918        case some_item_cmd:
1919            {
1920                /*tex
1921                    Because the items in this case directly refer to |cur_chr|, it needs to be saved
1922                    and restored.
1923                */
1924                int save_cur_chr = cur_chr;
1925                cur_chr = chr;
1926                if (tex_aux_set_cur_val_by_some_cmd(chr)) {
1927                    succeeded = 2;
1928                } else {
1929                    cur_chr = save_cur_chr;
1930                }
1931                break;
1932            }
1933        case internal_toks_cmd:
1934        case register_toks_cmd:
1935            cur_val = eq_value(chr);
1936            cur_val_level = token_val_level;
1937            break;
1938        case internal_integer_cmd:
1939        case register_integer_cmd:
1940        case internal_attribute_cmd:
1941        case register_attribute_cmd:
1942            cur_val = eq_value(chr);
1943            cur_val_level = integer_val_level;
1944            if (level == posit_val_level) {
1945                cur_val = (halfword) tex_posit_to_integer(cur_val);
1946            }
1947            break;
1948        case internal_posit_cmd:
1949        case register_posit_cmd:
1950            cur_val = eq_value(chr);
1951            cur_val_level = posit_val_level;
1952            break;
1953        case internal_dimension_cmd:
1954        case register_dimension_cmd:
1955            cur_val = eq_value(chr);
1956            cur_val_level = dimension_val_level;
1957            if (level == posit_val_level) {
1958                cur_val = tex_posit_to_dimension(cur_val);
1959            }
1960            break;
1961        case internal_glue_cmd:
1962        case register_glue_cmd:
1963            cur_val = eq_value(chr);
1964            cur_val_level = glue_val_level;
1965            break;
1966        case internal_muglue_cmd:
1967        case register_muglue_cmd:
1968            cur_val = eq_value(chr);
1969            cur_val_level = muglue_val_level;
1970            break;
1971        case lua_value_cmd:
1972            tex_aux_set_cur_val_by_lua_value_cmd(chr, property);
1973            if (cur_val_level == no_val_level) {
1974                return 0;
1975            }
1976            break;
1977        case iterator_value_cmd:
1978            cur_val = chr > 0x100000 ? - (chr - 0x100000) : chr;
1979            cur_val_level = integer_val_level;
1980            break;
1981        case math_style_cmd:
1982            tex_aux_set_cur_val_by_math_style_cmd(chr);
1983            break;
1984        case auxiliary_cmd:
1985            tex_aux_set_cur_val_by_auxiliary_cmd(chr);
1986            break;
1987        case page_property_cmd:
1988            tex_aux_set_cur_val_by_page_property_cmd(chr);
1989            break;
1990        case specification_cmd:
1991            tex_aux_set_cur_val_by_specification_cmd(chr);
1992            break;
1993        case define_char_code_cmd:
1994            tex_aux_set_cur_val_by_define_char_cmd(chr);
1995            break;
1996        /* end of tex_aux_short_scan_something_internal */
1997        case define_font_cmd:
1998         // if (tex_aux_valid_tok_level(level)) {
1999            if (level == token_val_level) { /* Is this test still needed? */
2000                cur_val = cur_font_par;
2001                cur_val_level = font_val_level;
2002                return cur_val;
2003            } else {
2004                break;
2005            }
2006        case set_font_cmd:
2007         // if (tex_aux_valid_tok_level(level)) {
2008            if (level == token_val_level) { /* Is this test still needed? */
2009                cur_val = cur_chr;
2010                cur_val_level = font_val_level;
2011                return cur_val;
2012            } else {
2013                break;
2014            }
2015        case define_family_cmd:
2016            {
2017                halfword fam = tex_scan_math_family_number();
2018                cur_val = tex_fam_fnt(fam, chr);
2019                cur_val_level = font_val_level;
2020                return cur_val;
2021            }
2022        case math_parameter_cmd:
2023            tex_aux_set_cur_val_by_math_parameter_cmd(chr);
2024            break;
2025        case box_property_cmd:
2026            tex_aux_set_cur_val_by_box_property_cmd(chr);
2027            break;
2028        case font_property_cmd:
2029            tex_aux_set_cur_val_by_font_property_cmd(chr);
2030            break;
2031        case register_cmd:
2032            tex_aux_set_cur_val_by_register_cmd(chr);
2033            break;
2034        case ignore_something_cmd:
2035            break;
2036        case hyphenation_cmd:
2037            if (tex_aux_scan_hyph_data_number(chr, &cur_val)) {
2038                cur_val_level = integer_val_level;
2039                break;
2040            } else {
2041                goto DEFAULT;
2042            }
2043        case integer_cmd:
2044        case index_cmd:
2045            cur_val = chr;
2046            cur_val_level = integer_val_level;
2047            break;
2048        case dimension_cmd:
2049            cur_val = chr;
2050            cur_val_level = dimension_val_level;
2051            break;
2052        case posit_cmd:
2053            cur_val = chr;
2054            cur_val_level = posit_val_level;
2055            break;
2056        case gluespec_cmd:
2057            cur_val = chr;
2058            cur_val_level = glue_val_level;
2059            break;
2060        case mugluespec_cmd:
2061            cur_val = chr;
2062            cur_val_level = muglue_val_level;
2063            break;
2064        case mathspec_cmd:
2065            tex_aux_set_cur_val_by_math_spec_cmd(chr);
2066            break;
2067        case fontspec_cmd:
2068            cur_val = tex_get_font_identifier(chr) ? chr : null;
2069            cur_val_level = fontspec_val_level;
2070            break;
2071        case association_cmd:
2072            switch (chr) {
2073                case unit_association_code:
2074                    cur_val = tex_get_unit_class(tex_scan_unit_register_number(0));
2075                    cur_val_level = integer_val_level;
2076                    break;
2077            }
2078            break;
2079        case begin_paragraph_cmd:
2080            switch (chr) {
2081                case snapshot_par_code:
2082                    {
2083                        halfword par = tex_find_par_par(cur_list.head);
2084                        cur_val = par ? par_state(par) : 0;
2085                        cur_val_level = integer_val_level;
2086                        break;
2087                    }
2088                /* case attribute_par_code: */
2089                case wrapup_par_code:
2090                    {
2091                        halfword par = tex_find_par_par(cur_list.head);
2092                        cur_val = par ? par_end_par_tokens(par) : null;
2093                        cur_val_level = token_val_level;
2094                        break;
2095                    }
2096                default:
2097                    goto DEFAULT;
2098            }
2099            break;
2100        /*
2101        case special_box_cmd:
2102            switch (chr) {
2103                case left_box_code:
2104                    cur_val = cur_mode == hmode ? local_left_box_par : null;
2105                    cur_val_level = list_val_level;
2106                    return cur_val;
2107                case right_box_code:
2108                    cur_val = cur_mode == hmode ? local_right_box_par : null;
2109                    cur_val_level = list_val_level;
2110                    return cur_val;
2111                default:
2112                   goto DEFAULT;
2113            }
2114            break;
2115        */
2116        /*tex
2117            This one is not in the [min_internal_cmd,max_internal_cmd] range!
2118        */
2119        case parameter_cmd:
2120            tex_get_x_token();
2121            if (valid_iterator_reference(cur_tok)) {
2122                cur_val = tex_expand_iterator(cur_tok);
2123                cur_val_level = integer_val_level;
2124                break;
2125            } else {
2126                goto DEFAULT;
2127            }
2128        case interaction_cmd:
2129            cur_val = cur_chr;
2130            cur_val_level = integer_val_level;
2131            break;
2132        case specificationspec_cmd:
2133            {
2134                quarterword code = node_subtype(chr);
2135                halfword count = tex_get_specification_count(chr);
2136                if (count) {
2137                    switch (code) {
2138                        case integer_list_code:
2139                        case dimension_list_code:
2140                        case posit_list_code:
2141                            {
2142                                /* todo: repeat */
2143                                halfword index = tex_scan_integer(0, NULL, NULL);
2144                                halfword value = 0;
2145                                if (index) {
2146                                    if (index < 0) {
2147                                        index = count + index + 1;
2148                                    }
2149                                    if (index > count && specification_rotate(chr)) {
2150                                        index = (index % specification_count(chr));
2151                                        if (index == 0) { 
2152                                            index = specification_count(chr);
2153                                        }
2154                                    } 
2155                                    if (index >= 1 && index <= count) {
2156                                        if (specification_double(chr)) {
2157                                            switch (tex_scan_integer(0, NULL, NULL)) {
2158                                                case 1:
2159                                                    value = tex_get_specification_nepalty(chr, index);
2160                                                    if (specification_integer(chr)) {
2161                                                        code = integer_list_code;
2162                                                    }
2163                                                    break;
2164                                                case 2:
2165                                                    value = tex_get_specification_penalty(chr, index);
2166                                                    break;
2167                                            }
2168                                        } else {
2169                                            value = tex_get_specification_penalty(chr, index);
2170                                        }
2171                                    } else {    
2172                                        tex_specification_range_error(chr);
2173                                    }
2174                                    switch (code) {
2175                                        case integer_list_code:
2176                                            cur_val = value;
2177                                            cur_val_level = integer_val_level;
2178                                            break ;
2179                                        case dimension_list_code:
2180                                            cur_val = value;
2181                                            cur_val_level = dimension_val_level;
2182                                            break;
2183                                        case posit_list_code:
2184                                            cur_val = value;
2185                                            cur_val_level = posit_val_level;
2186                                            break;
2187                                    }
2188                                } else {
2189                                    cur_val = count;
2190                                    cur_val_level = integer_val_level;
2191                                }
2192                            }
2193                            break;
2194                        default:
2195                            cur_val = count;
2196                            cur_val_level = integer_val_level;
2197                            break;
2198                    }
2199                } else {
2200                    cur_val = count;
2201                    cur_val_level = integer_val_level;
2202                }
2203                break;
2204            }
2205# if (match_experiment)
2206case integer_reference_cmd:
2207    {
2208        halfword n = cur_chr;
2209        if (node_token_flagged(n)) {
2210            tex_get_token();
2211            n = node_token_sum(n,cur_chr);
2212        }
2213        cur_val = n;
2214        cur_val_level = integer_val_level;
2215        break;
2216    }
2217case dimension_reference_cmd:
2218    {
2219        halfword n = cur_chr;
2220        if (node_token_flagged(n)) {
2221            tex_get_token();
2222            n = node_token_sum(n,cur_chr);
2223        }
2224        cur_val = n;
2225        cur_val_level = dimension_val_level;
2226        break;
2227    }
2228# endif 
2229        default:
2230          DEFAULT:
2231            /*tex Complain that |\the| can not do this; give zero result. */
2232            tex_handle_error(
2233                normal_error_type,
2234                "You can't use '%C' after \\the",
2235                cmd, chr,
2236                "I'm forgetting what you said and using zero instead."
2237            );
2238            cur_val = 0;
2239            cur_val_level = (level == token_val_level) ? integer_val_level : dimension_val_level;
2240            break;
2241    }
2242    tex_aux_downgrade_cur_val(level, succeeded, negative);
2243    return cur_val;
2244}
2245
2246void tex_scan_something_simple(halfword cmd, halfword chr)
2247{
2248    if (cmd >= min_internal_cmd && cmd <= max_internal_cmd) {
2249        tex_aux_scan_something_internal(cmd, chr, no_val_level, 0, 0);
2250    } else {
2251     // tex_handle_error(
2252     //     normal_error_type,
2253     //     "You can't use '%C' as (Lua) tex library index",
2254     //     cmd, chr,
2255     //     "I'm forgetting what you said and using zero instead."
2256     // );
2257        cur_val = 0;
2258        cur_val_level = integer_val_level;
2259    }
2260}
2261
2262/*tex
2263
2264    It is nice to have routines that say what they do, so the original |scan_eight_bit_int| is
2265    superceded by |scan_register_number| and |scan_mark_number|. It may become split up even further
2266    in the future.
2267
2268    Many of the |restricted classes| routines are the essentially the same except for the upper
2269    limit and the error message, so it makes sense to combine these all into one function.
2270
2271*/
2272
2273static inline halfword tex_aux_scan_limited_int(int optional_equal, int min, int max, const char *invalid)
2274{
2275    halfword v = tex_scan_integer(optional_equal, NULL, NULL);
2276    if (v < min || v > max) {
2277        tex_handle_error(
2278            normal_error_type,
2279            "%s (%i) should be in the range %i..%i",
2280            invalid, v, min, max,
2281            "I'm going to use 0 instead of that illegal code value."
2282        );
2283        return 0;
2284    } else {
2285        return v;
2286    }
2287}
2288
2289halfword   tex_scan_integer_register_number   (void)               { return tex_aux_scan_limited_int(0, 0, max_integer_register_index, "Integer register index"); }
2290halfword   tex_scan_dimension_register_number (void)               { return tex_aux_scan_limited_int(0, 0, max_dimension_register_index, "Dimension register index"); }
2291halfword   tex_scan_attribute_register_number (void)               { return tex_aux_scan_limited_int(0, 0, max_attribute_register_index, "Attribute register index"); }
2292halfword   tex_scan_posit_register_number     (void)               { return tex_aux_scan_limited_int(0, 0, max_posit_register_index, "Posit register index"); }
2293halfword   tex_scan_glue_register_number      (void)               { return tex_aux_scan_limited_int(0, 0, max_glue_register_index, "Glue register index"); }
2294halfword   tex_scan_muglue_register_number    (void)               { return tex_aux_scan_limited_int(0, 0, max_muglue_register_index, "Muglue register index"); }
2295halfword   tex_scan_toks_register_number      (void)               { return tex_aux_scan_limited_int(0, 0, max_toks_register_index, "Toks register index"); }
2296halfword   tex_scan_box_register_number       (void)               { return tex_aux_scan_limited_int(0, 0, max_box_register_index, "Box register index"); }
2297halfword   tex_scan_unit_register_number      (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_unit_register_index, "Unit register index"); }
2298halfword   tex_scan_mark_number               (void)               { return tex_aux_scan_limited_int(0, 0, max_mark_index, "Marks index"); }
2299halfword   tex_scan_char_number               (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_character_code, "Character code"); }
2300halfword   tex_scan_math_char_number          (void)               { return tex_aux_scan_limited_int(0, 0, max_math_character_code, "Character code"); }
2301halfword   tex_scan_math_family_number        (void)               { return tex_aux_scan_limited_int(0, 0, max_math_family_index, "Math family"); }
2302halfword   tex_scan_math_properties_number    (void)               { return tex_aux_scan_limited_int(0, 0, max_math_property, "Math properties"); }
2303halfword   tex_scan_math_group_number         (void)               { return tex_aux_scan_limited_int(0, 0, max_math_group, "Math group"); }
2304halfword   tex_scan_math_index_number         (void)               { return tex_aux_scan_limited_int(0, 0, max_math_index, "Math index"); }
2305halfword   tex_scan_math_discretionary_number (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_math_discretionary, "Math discretionary"); }
2306singleword tex_scan_box_index                 (void)               { return (singleword) tex_aux_scan_limited_int(0, 0, max_box_index, "Box index"); }
2307singleword tex_scan_box_axis                  (void)               { return (singleword) tex_aux_scan_limited_int(0, 0, max_box_axis, "Box axis"); }
2308halfword   tex_scan_category_code             (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_category_code,"Category code"); }
2309halfword   tex_scan_space_factor              (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, min_space_factor, max_space_factor, "Space factor"); }
2310halfword   tex_scan_scale_factor              (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, min_scale_factor, max_scale_factor, "Scale factor"); }
2311halfword   tex_scan_function_reference        (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_function_reference, "Function reference"); }
2312halfword   tex_scan_bytecode_reference        (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_bytecode_index, "Bytecode reference"); }
2313halfword   tex_scan_limited_scale             (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, -max_limited_scale, max_limited_scale, "Limited scale"); }
2314halfword   tex_scan_positive_scale            (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, min_limited_scale, max_limited_scale, "Limited scale"); }
2315halfword   tex_scan_positive_number           (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_integer, "Positive number"); }
2316halfword   tex_scan_parameter_index           (void)               { return tex_aux_scan_limited_int(0, 0, 15, "Parameter index"); }
2317halfword   tex_scan_classification_code       (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_classification_code,"Classification code"); }
2318
2319halfword   tex_scan_math_class_number(int optional_equal)
2320{
2321    halfword v = tex_aux_scan_limited_int(optional_equal, -1, max_math_class_code + 1, "Math class");
2322    if (v >= 0 && v <= max_math_class_code) {
2323        return v;
2324    } else {
2325        return unset_noad_class;
2326    }
2327}
2328
2329/*tex
2330
2331    An integer number can be preceded by any number of spaces and |+| or |-| signs. Then comes
2332    either a decimal constant (i.e., radix 10), an octal constant (i.e., radix 8, preceded by~|'|),
2333    a hexadecimal constant (radix 16, preceded by~|"|), an alphabetic constant (preceded by~|`|),
2334    or an internal variable. After scanning is complete, |cur_val| will contain the answer, which
2335    must be at most $2^{31}-1=2147483647$ in absolute value. The value of |radix| is set to 10, 8,
2336    or 16 in the cases of decimal, octal, or hexadecimal constants, otherwise |radix| is set to
2337    zero. An optional space follows a constant.
2338
2339    The |scan_int| routine is used also to scan the integer part of a fraction; for example, the
2340    |3| in |3.14159| will be found by |scan_int|. The |scan_dimension| routine assumes that |cur_tok
2341    = point_token| after the integer part of such a fraction has been scanned by |scan_int|, and
2342    that the decimal point has been backed up to be scanned again.
2343
2344*/
2345
2346/*tex
2347
2348    The next function is somewhat special. It is also called in other scanners and therefore
2349    |cur_val| cannot simply be replaced. For that reason we do return the value but also set
2350    |cur_val|, just in case. I might sort this out some day when other stuff has been reworked.
2351
2352    The routine has been optimnized a bit (equal scanning and such) and after a while I decided to
2353    split the three cases. It makes for a bit nicer code.
2354
2355    If we backport the checking code to \LUATEX, a pre May 24 2020 copy has to be taken, because
2356    that is closer to the original.
2357
2358*/
2359
2360static void tex_aux_scan_integer_no_number(int where)
2361{
2362    /*tex Express astonishment that no number was here. */
2363    if (lmt_error_state.intercept) {
2364        lmt_error_state.last_intercept = 1 ;
2365        if (cur_cmd != spacer_cmd) {
2366            tex_back_input(cur_tok);
2367        }
2368    } else {
2369        tex_aux_missing_number_error(where);
2370    }
2371}
2372
2373halfword tex_scan_integer(int optional_equal, int *radix, int *grouped)
2374{
2375    bool negative = false;
2376    long long result = 0;
2377    while (1) {
2378        tex_get_x_token();
2379        if (cur_cmd == spacer_cmd) {
2380            continue;
2381        } else if (cur_cmd == left_brace_cmd) {
2382            tex_aux_scan_integer_expression(1);
2383         // tex_aux_scan_expression(integer_val_level, 1);
2384            result = cur_val;
2385            if (grouped) { 
2386                *grouped = 1;
2387            }
2388            goto THATSIT;
2389        } else if (cur_tok == equal_token) {
2390            if (optional_equal) {
2391                optional_equal = 0;
2392                continue;
2393            } else {
2394                break;
2395            }
2396        } else if (cur_tok == minus_token) {
2397            negative = ! negative;
2398        } else if (cur_tok != plus_token) {
2399            break;
2400        }
2401    };
2402    if (cur_tok == alpha_token) {
2403        /*tex
2404            Scan an alphabetic character code into |result|. A space is ignored after an alphabetic
2405            character constant, so that such constants behave like numeric ones. We don't expand the
2406            next token!
2407        */
2408        tex_get_token();
2409        if (cur_tok < cs_token_flag) {
2410            result = cur_chr;
2411            if (cur_cmd == right_brace_cmd) {
2412               ++lmt_input_state.align_state;
2413        //  } else if (cur_cmd < right_brace_cmd) {
2414            } else if (cur_cmd == left_brace_cmd || cur_cmd == relax_cmd) {
2415                /* left_brace_cmd or relax_cmd (really?)*/
2416               --lmt_input_state.align_state;
2417            }
2418        } else {
2419            /*tex
2420                The value of a csname in this context is its name. A single letter case happens more
2421                frequently than an active character but both seldom are ran into anyway.
2422            */
2423            strnumber txt = cs_text(cur_tok - cs_token_flag);
2424            if (tex_single_letter(txt)) {
2425                result = aux_str2uni(str_string(txt));
2426            } else if (tex_is_active_cs(txt)) {
2427                result = active_cs_value(txt);
2428            } else {
2429                result = max_character_code + 1;
2430            }
2431        }
2432        if (result > max_character_code) {
2433            if (lmt_error_state.intercept) {
2434                lmt_error_state.last_intercept = 1;
2435                tex_back_input(cur_tok);
2436            } else {
2437                tex_aux_improper_constant_error();
2438                return 0;
2439            }
2440        } else {
2441            /*tex Scan an optional space. */
2442            tex_get_x_token();
2443            if (cur_cmd != spacer_cmd) {
2444                tex_back_input(cur_tok);
2445            }
2446        }
2447    } else if ((cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) || cur_cmd == parameter_cmd) {
2448        result = tex_aux_scan_something_internal(cur_cmd, cur_chr, integer_val_level, 0, 0);
2449        if (cur_val_level != integer_val_level) {
2450            tex_aux_scan_integer_no_number(6);
2451            return 0;
2452        }
2453    } else {
2454        /*tex has an error message been issued? */
2455        bool vacuous = true;
2456        bool ok_so_far = true;
2457        /*tex
2458            Scan a numeric constant. The interwoven common loop has been split up now.
2459        */
2460        switch (cur_tok) {
2461            case octal_token:
2462                {
2463                    if (radix) {
2464                        *radix = 8;
2465                    }
2466                    while (1) {
2467                        unsigned d = 0;
2468                        tex_get_x_token();
2469                        if ((cur_tok >= zero_token) && (cur_tok <= seven_token)) {
2470                            d = cur_tok - zero_token;
2471                        } else {
2472                            goto DONE;
2473                        }
2474                        vacuous = false;
2475                        if (ok_so_far) {
2476                            result = result * 8 + d;
2477                            if (result > max_integer) {
2478                                result = max_integer;
2479                                if (lmt_error_state.intercept) {
2480                                    vacuous = true;
2481                                    goto DONE;
2482                                } else {
2483                                    tex_aux_scan_integer_out_of_range_error(1);
2484                                }
2485                                ok_so_far = 0;
2486                            }
2487                        }
2488                    }
2489                 // break;
2490                }
2491            case hex_token:
2492                {
2493                    if (radix) {
2494                        *radix = 16;
2495                    }
2496                    while (1) {
2497                        unsigned d = 0;
2498                        tex_get_x_token();
2499                        if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
2500                            d = cur_tok - zero_token;
2501                        } else if ((cur_tok >= A_token_l) && (cur_tok <= F_token_l)) {
2502                            d = cur_tok - A_token_l + 10;
2503                        } else if ((cur_tok >= A_token_o) && (cur_tok <= F_token_o)) {
2504                            d = cur_tok - A_token_o + 10;
2505                        } else {
2506                            goto DONE;
2507                        }
2508                        vacuous = false;
2509                        if (ok_so_far) {
2510                            result = result * 16 + d;
2511                            if (result > max_integer) {
2512                                result = max_integer;
2513                                if (lmt_error_state.intercept) {
2514                                    vacuous = true;
2515                                    goto DONE;
2516                                } else {
2517                                    tex_aux_scan_integer_out_of_range_error(2);
2518                                }
2519                                ok_so_far = false;
2520                            }
2521                        }
2522                    }
2523                 // break;
2524                }
2525            default:
2526                if (radix) {
2527                    *radix = 10;
2528                }
2529                while (1) {
2530                    unsigned d = 0;
2531                    if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
2532                        d = cur_tok - zero_token;
2533                    } else {
2534                        goto DONE;
2535                    }
2536                    vacuous = false;
2537                    if (ok_so_far) {
2538                        result = result * 10 + d;
2539                        if (result > max_integer) {
2540                            result = max_integer;
2541                            if (lmt_error_state.intercept) {
2542                                vacuous = true;
2543                                goto DONE;
2544                            } else {
2545                                tex_aux_scan_integer_out_of_range_error(3);
2546                            }
2547                            ok_so_far = false;
2548                        }
2549                    }
2550                    tex_get_x_token();
2551                }
2552                // break;
2553        }
2554      DONE:
2555        if (vacuous) {
2556            tex_aux_scan_integer_no_number(7);
2557        } else {
2558            tex_push_back(cur_tok, cur_cmd, cur_chr);
2559        }
2560    }
2561    /*tex For now we still keep |cur_val| set too. */
2562  THATSIT:
2563    cur_val = (halfword) (negative ? - result : result);
2564    return cur_val;
2565}
2566
2567void tex_scan_integer_validate(void)
2568{
2569    while (1) {
2570        tex_get_x_token();
2571        if (cur_cmd == spacer_cmd || cur_tok == minus_token) {
2572            continue;
2573        } else if (cur_tok != plus_token) {
2574            break;
2575        }
2576    };
2577    if (cur_tok == alpha_token) {
2578        long long result = 0;
2579        tex_get_token();
2580        if (cur_tok < cs_token_flag) {
2581            result = cur_chr;
2582            /* Really when validating? */
2583            if (cur_cmd == right_brace_cmd) {
2584               ++lmt_input_state.align_state;
2585            } else if (cur_cmd == left_brace_cmd || cur_cmd == relax_cmd) {
2586               --lmt_input_state.align_state;
2587            }
2588        } else {
2589            strnumber txt = cs_text(cur_tok - cs_token_flag);
2590            if (tex_single_letter(txt)) {
2591                result = aux_str2uni(str_string(txt));
2592            } else if (tex_is_active_cs(txt)) {
2593                result = active_cs_value(txt);
2594            } else {
2595                result = max_character_code + 1;
2596            }
2597        }
2598        if (result > max_character_code) {
2599            if (lmt_error_state.intercept) {
2600                lmt_error_state.last_intercept = 1;
2601                tex_back_input(cur_tok);
2602            } else {
2603                tex_aux_improper_constant_error();
2604            }
2605        } else {
2606            tex_get_x_token();
2607            if (cur_cmd != spacer_cmd) {
2608                tex_back_input(cur_tok);
2609            }
2610        }
2611    } else if ((cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) || cur_cmd == parameter_cmd) {
2612        tex_aux_scan_something_internal(cur_cmd, cur_chr, integer_val_level, 0, 0);
2613        if (cur_val_level != integer_val_level) {
2614            tex_aux_scan_integer_no_number(8);
2615        }
2616    } else {
2617        bool vacuous = true;
2618        switch (cur_tok) {
2619            case octal_token:
2620                while (1) {
2621                    tex_get_x_token();
2622                    if (! (cur_tok >= zero_token && cur_tok <= seven_token)) {
2623                        goto DONE;
2624                    }
2625                    vacuous = false;
2626                }
2627            case hex_token:
2628                while (1) {
2629                    tex_get_x_token();
2630                    if (! ((cur_tok >= zero_token && cur_tok <= nine_token) ||
2631                            (cur_tok >= A_token_l  && cur_tok <= F_token_l ) ||
2632                            (cur_tok >= A_token_o  && cur_tok <= F_token_o ) )) {
2633                        goto DONE;
2634                    }
2635                    vacuous = false;
2636                }
2637            default:
2638                while (1) {
2639                    if (! (cur_tok >= zero_token && cur_tok <= nine_token)) {
2640                        goto DONE;
2641                    }
2642                    vacuous = false;
2643                    tex_get_x_token();
2644                }
2645        }
2646      DONE:
2647        if (vacuous) {
2648            tex_aux_scan_integer_no_number(9);
2649        } else {
2650            tex_push_back(cur_tok, cur_cmd, cur_chr);
2651        }
2652    }
2653}
2654
2655int tex_scan_cardinal(int optional_equal, unsigned *value, int dontbark)
2656{
2657    long long result = 0;
2658    while (1) {
2659        tex_get_x_token();
2660        if (cur_cmd != spacer_cmd) {
2661            if (optional_equal && (cur_tok == equal_token)) {
2662                optional_equal = 0;
2663            } else if (cur_cmd == left_brace_cmd) {
2664                tex_aux_scan_integer_expression(1);
2665             // tex_aux_scan_expression(integer_val_level, 1);
2666                /* todo: what if negative */
2667                if (result < 0) { 
2668                 // if (dontbark) {
2669                 //     return 0;
2670                 // } else {
2671                 //     tex_aux_missing_number_error(1);
2672                 // }
2673                    /*tex What to do? */
2674                    result = 0;
2675                    cur_val = 0;
2676                    return 0;
2677                } else { 
2678                    result = cur_val;
2679                }
2680                goto THATSIT;
2681            } else {
2682                break;
2683            }
2684        }
2685    }
2686    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
2687        result = tex_aux_scan_something_internal(cur_cmd, cur_chr, integer_val_level, 0, 0);
2688    } else {
2689        bool vacuous = true;
2690        switch (cur_tok) {
2691            case octal_token:
2692                {
2693                    while (1) {
2694                        unsigned d = 0;
2695                        tex_get_x_token();
2696                        if ((cur_tok >= zero_token) && (cur_tok <= seven_token)) {
2697                            d = cur_tok - zero_token;
2698                        } else {
2699                            goto DONE;
2700                        }
2701                        vacuous = false;
2702                        result = result * 8 + d;
2703                        if (result > max_cardinal) {
2704                            result = max_cardinal;
2705                        }
2706                    }
2707                 // break;
2708                }
2709            case hex_token:
2710                {
2711                    while (1) {
2712                        unsigned d = 0;
2713                        tex_get_x_token();
2714                        if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
2715                            d = cur_tok - zero_token;
2716                        } else if ((cur_tok >= A_token_l) && (cur_tok <= F_token_l)) {
2717                            d = cur_tok - A_token_l + 10;
2718                        } else if ((cur_tok >= A_token_o) && (cur_tok <= F_token_o)) {
2719                            d = cur_tok - A_token_o + 10;
2720                        } else {
2721                            goto DONE;
2722                        }
2723                        vacuous = false;
2724                        result = result * 16 + d;
2725                        if (result > max_cardinal) {
2726                            result = max_cardinal;
2727                        }
2728                    }
2729                 // break;
2730                }
2731            default:
2732                {
2733                    while (1) {
2734                        unsigned d = 0;
2735                        if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
2736                            d = cur_tok - zero_token;
2737                        } else {
2738                            goto DONE;
2739                        }
2740                        vacuous = false;
2741                        result = result * 10 + d;
2742                        if (result > max_cardinal) {
2743                            result = max_cardinal;
2744                        }
2745                        tex_get_x_token();
2746                    }
2747                 // break;
2748                }
2749        }
2750      DONE:
2751        if (vacuous) {
2752            if (dontbark) {
2753                return 0;
2754            } else {
2755                tex_aux_missing_number_error(1);
2756            }
2757        } else {
2758            tex_push_back(cur_tok, cur_cmd, cur_chr);
2759        }
2760    }
2761  THATSIT:
2762    *value = (unsigned) result;
2763    cur_val = (halfword) result;
2764    return 1;
2765}
2766
2767/*tex
2768
2769    The following code is executed when |scan_something_internal| was called asking for |mu_val|,
2770    when we really wanted a mudimen instead of muglue.
2771
2772*/
2773
2774static halfword tex_aux_coerced_glue(halfword value, halfword level)
2775{
2776    if (level == glue_val_level || level == muglue_val_level) {
2777        int v = glue_amount(value);
2778        tex_flush_node(value);
2779        return v;
2780    } else {
2781        return value;
2782    }
2783}
2784
2785/*tex
2786
2787    The |scan_dimension| routine is similar to |scan_int|, but it sets |cur_val| to a |scaled| value,
2788    i.e., an integral number of sp. One of its main tasks is therefore to interpret the
2789    abbreviations for various kinds of units and to convert measurements to scaled points.
2790
2791    There are three parameters: |mu| is |true| if the finite units must be |mu|, while |mu| is
2792    |false| if |mu| units are disallowed; |inf| is |true| if the infinite units |fil|, |fill|,
2793    |filll| are permitted; and |shortcut| is |true| if |cur_val| already contains an integer and
2794    only the units need to be considered.
2795
2796    The order of infinity that was found in the case of infinite glue is returned in the global
2797    variable |cur_order|.
2798
2799    Constructions like |-'77 pt| are legal dimensions, so |scan_dimension| may begin with |scan_int|.
2800    This explains why it is convenient to use |scan_integer| also for the integer part of a decimal
2801    fraction.
2802
2803    Several branches of |scan_dimension| work with |cur_val| as an integer and with an auxiliary
2804    fraction |f|, so that the actual quantity of interest is $|cur_val|+|f|/2^{16}$. At the end of
2805    the routine, this \quote {unpacked} representation is put into the single word |cur_val|, which
2806    suddenly switches significance from |integer| to |scaled|.
2807
2808    The necessary conversion factors can all be specified exactly as fractions whose numerator and
2809    denominator add to 32768 or less. According to the definitions here, $\rm 2660 \, dd \approx
2810    1000.33297 \, mm$; this agrees well with the value $\rm 1000.333 \, mm$ cited by Hans Rudolf
2811    Bosshard in {\em Technische Grundlagen zur Satzherstellung} (Bern, 1980). The Didot point has
2812    been newly standardized in 1978; it's now exactly $\rm 1 \, nd = 0.375 \, mm$. Conversion uses
2813    the equation $0.375 = 21681 / 20320 / 72.27 \cdot 25.4$. The new Cicero follows the new Didot
2814    point; $\rm 1 \, nc = 12 \, nd$. These would lead to the ratios $21681 / 20320$ and $65043
2815    / 5080$, respectively. The closest approximations supported by the algorithm would be $11183 /
2816    10481$ and $1370 / 107$. In order to maintain the relation $\rm 1 \, nc = 12 \, nd$, we pick
2817    the ratio $685 / 642$ for $\rm nd$, however.
2818
2819*/
2820
2821static void tex_aux_scan_dimension_mu_error(void) {
2822    tex_handle_error(
2823        normal_error_type,
2824        "Illegal unit of measure (mu inserted)",
2825        "The unit of measurement in math glue must be mu." );
2826
2827}
2828
2829static void tex_aux_scan_dimension_fi_error(void) {
2830    tex_handle_error(
2831        normal_error_type,
2832        "Illegal unit of measure",
2833        "The unit of measurement can't be fi, fil, fill or filll here." );
2834
2835}
2836
2837/*tex
2838    The Edith and Tove were introduced at BachoTeX 2023 and because the error message
2839    was still in feet we decided to adapt it accordingly so now in addition it reports
2840    different values, including Theodores little feet measured by Arthur as being roughly
2841    five Ediths.
2842*/
2843
2844static void tex_aux_scan_dimension_unknown_unit_error(void) {
2845    tex_handle_error(
2846        normal_error_type,
2847        "Illegal unit of measure (pt inserted)",
2848        "Dimensions can be in units of em, ex, sp, cm, mm, es, ts, pt, bp, dk, pc, dd\n"
2849        "cc or in; but yours is a new one! I'll assume that you meant to say pt, for\n"
2850        "printer's points: two letters. User units also have at most two characters."
2851    );
2852}
2853
2854static void tex_aux_scan_expression_unexpected_negation_error(void) {
2855    tex_handle_error(
2856        normal_error_type,
2857        "Unexpected minus, not or unequal symbol found at the start of an expression",
2858        "You can use {-{...}} but not -{...} in expressions because it would give\n"
2859        "unexpected results. I'll ignore this negation.\n"
2860    );
2861}
2862
2863# define set_conversion(A,B) do { num=(A); denom=(B); } while(0)
2864
2865/*tex
2866
2867    This function sets |cur_val| to a dimension. We still have some |cur_val| sync issue so no
2868    result replacement yet. (The older variant, also already optimzied can be found in the
2869    history).
2870
2871    When order is |NULL| mu units and glue fills are not scanned.
2872
2873*/
2874
2875typedef enum scanned_unit {
2876    no_unit_scanned,         /* 0 : error */
2877    normal_unit_scanned,     /* 1 : cm mm pt bp dd cc in dk */
2878    scaled_point_scanned,    /* 2 : sp */
2879    relative_unit_scanned,   /* 3 : ex em px */
2880    math_unit_scanned,       /* 4 : mu */
2881    flexible_unit_scanned,   /* 5 : fi fil fill filll */
2882    quantitity_unit_scanned, /* 6 : internal quantity */
2883} scanned_unit;
2884
2885/*tex
2886
2887    We support the Knuthian Potrzebie cf.\ \url {https://en.wikipedia.org/wiki/Potrzebie} as the
2888    |dk| unit. It was added on 2021-09-22 exactly when we crossed the season during an evening
2889    session at the 15th \CONTEXT\ meeting in Bassenge (Boirs) Belgium. It took a few iterations to
2890    find the best numerator and denominator, but Taco Hoekwater, Harald Koenig and Mikael Sundqvist
2891    figured it out in this interactive session. The error messages have been adapted accordingly and
2892    the scanner in the |tex| library also handles it. One |dk| is 6.43985pt. There is no need to
2893    make \METAPOST\ aware of this unit because there it is just a numeric multiplier in a macro
2894    package.
2895
2896    From Wikipedia:
2897
2898    In issue 33, Mad published a partial table of the \quotation {Potrzebie System of Weights and
2899    Measures}, developed by 19-year-old Donald~E. Knuth, later a famed computer scientist. According
2900    to Knuth, the basis of this new revolutionary system is the potrzebie, which equals the thickness
2901    of Mad issue 26, or 2.2633484517438173216473 mm [...].
2902
2903    We also provide alternatives for the inch: the |es| and |ts|, two units dedicated to women
2904    (Edith and Tove) that come close to the inch but are more metric. Their values have been
2905    carefully callibrated at the 2023 BachoTeX meeting and a report will be published in the
2906    proceedings as well as TUGboat (medio 2023).
2907
2908    An additional |eu| has been introduced as a multiplier for |ts| that defaults to 10 which makes
2909    one |eu| default to one |es|.
2910
2911    The |true| addition has now officially been dropped.
2912
2913*/
2914
2915/*tex
2916    We keep this as reference:
2917*/
2918
2919// static int tex_aux_scan_unit(halfword *num, halfword *denom, halfword *value, halfword *order)
2920// {
2921// //AGAIN: /* only for true */
2922//     do {
2923//         tex_get_x_token();
2924//     } while (cur_cmd == spacer_cmd);
2925//     if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
2926//         return quantitity_unit_scanned;
2927//     } else {
2928//         int chrone, chrtwo;
2929//         halfword tokone, toktwo;
2930//         halfword save_cur_cs = cur_cs;
2931//         tokone = cur_tok;
2932//         if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
2933//             chrone = cur_chr;
2934//         } else {
2935//             goto BACK_ONE;
2936//         }
2937//         tex_get_x_token();
2938//         toktwo = cur_tok;
2939//         if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
2940//             chrtwo = cur_chr;
2941//         } else {
2942//             goto BACK_TWO;
2943//         }
2944//         cur_cs = save_cur_cs;
2945//         switch (chrone) {
2946//             case 'p': case 'P':
2947//                 switch (chrtwo) {
2948//                     case 't': case 'T':
2949//                         return normal_unit_scanned;
2950//                     case 'c': case 'C':
2951//                         *num = 12;
2952//                         *denom = 1;
2953//                         return normal_unit_scanned;
2954//                     case 'x': case 'X':
2955//                         *value = px_dimension_par;
2956//                         return relative_unit_scanned;
2957//                 }
2958//                 break;
2959//             case 'm': case 'M':
2960//                 switch (chrtwo) {
2961//                     case 'm': case 'M':
2962//                         *num = 7227;
2963//                         *denom = 2540;
2964//                         return normal_unit_scanned;
2965//                     case 'u': case 'U':
2966//                         if (order) {
2967//                             return math_unit_scanned;
2968//                         } else {
2969//                             break;
2970//                         }
2971//                 }
2972//                 break;
2973//             case 'c': case 'C':
2974//                 switch (chrtwo) {
2975//                     case 'm': case 'M':
2976//                         *num = 7227;
2977//                         *denom = 254;
2978//                         return normal_unit_scanned;
2979//                     case 'c': case 'C':
2980//                         *num = 14856;
2981//                         *denom = 1157;
2982//                         return normal_unit_scanned;
2983//                 }
2984//                 break;
2985//             case 's': case 'S':
2986//                 switch (chrtwo) {
2987//                     case 'p': case 'P':
2988//                         return scaled_point_scanned;
2989//                 }
2990//                 break;
2991//             case 'b': case 'B':
2992//                 switch (chrtwo) {
2993//                     case 'p': case 'P':
2994//                         *num = 7227;
2995//                         *denom = 7200;
2996//                         return normal_unit_scanned;
2997//                 }
2998//                 break;
2999//             case 'i': case 'I':
3000//                 switch (chrtwo) {
3001//                     case 'n': case 'N':
3002//                         *num = 7227;
3003//                         *denom = 100;
3004//                         return normal_unit_scanned;
3005//                 }
3006//                 break;
3007//             case 'd': case 'D':
3008//                 switch (chrtwo) {
3009//                     case 'd': case 'D':
3010//                         *num = 1238;
3011//                         *denom = 1157;
3012//                         return normal_unit_scanned;
3013//                     case 'k': case 'K': /* number: 422042 */
3014//                         *num = 49838;  // 152940;
3015//                         *denom = 7739; //  23749;
3016//                         return normal_unit_scanned;
3017//                 }
3018//                 break;
3019//             case 't': case 'T':
3020//                 switch (chrtwo) {
3021//                     case 's': case 'S':
3022//                         *num = 4588;
3023//                         *denom = 645;
3024//                         return normal_unit_scanned;
3025//                 }
3026//              // if (order) {
3027//              //     switch (chrtwo) {
3028//              //         case 'r': case 'R':
3029//              //             if (tex_scan_mandate_keyword("true", 2)) {
3030//              //                 /*tex This is now a bogus prefix that might get dropped! */
3031//              //                 goto AGAIN;
3032//              //             }
3033//              //     }
3034//              // }
3035//                 break;
3036//             case 'e': case 'E':
3037//                 switch (chrtwo) {
3038//                     case 'm': case 'M':
3039//                         *value = tex_get_scaled_em_width(cur_font_par);
3040//                         return relative_unit_scanned;
3041//                     case 'x': case 'X':
3042//                         *value = tex_get_scaled_ex_height(cur_font_par);
3043//                         return relative_unit_scanned;
3044//                     case 's': case 'S':
3045//                         *num = 9176;
3046//                         *denom = 129;
3047//                         return normal_unit_scanned;
3048//                     case 'u': case 'U':
3049//                         *num = 9176 * eu_factor_par;
3050//                         *denom = 129 * 10;
3051//                         return normal_unit_scanned;
3052//                 }
3053//                 break;
3054//             case 'f': case 'F':
3055//                 if (order) {
3056//                     switch (chrtwo) {
3057//                         case 'i': case 'I':
3058//                             *order = fi_glue_order;
3059//                             if (tex_scan_character("lL", 0, 0, 0)) {
3060//                                 *order = fil_glue_order;
3061//                                 if (tex_scan_character("lL", 0, 0, 0)) {
3062//                                     *order = fill_glue_order;
3063//                                     if (tex_scan_character("lL", 0, 0, 0)) {
3064//                                         *order = filll_glue_order;
3065//                                     }
3066//                                 }
3067//                             }
3068//                             return flexible_unit_scanned;
3069//                     }
3070//                 }
3071//                 break;
3072//         }
3073//         /* only lowercase for now */
3074//         {
3075//             int index = unit_parameter_index(chrone, chrtwo);
3076//             if (index >= 0) {
3077//                 halfword cs = unit_parameter(index);
3078//                 if (cs > 0) {
3079//                     halfword cmd = eq_type(cs);
3080//                     halfword chr = eq_value(cs);
3081//                     switch (cmd) {
3082//                         case internal_dimension_cmd:
3083//                         case register_dimension_cmd:
3084//                             *value = eq_value(chr);
3085//                             return relative_unit_scanned;
3086//                         case dimension_cmd:
3087//                             *value = chr;
3088//                             return relative_unit_scanned;
3089//                     }
3090//                 }
3091//             }
3092//         }
3093//       BACK_TWO:
3094//         tex_back_input(toktwo);
3095//       BACK_ONE:
3096//         tex_back_input(tokone);
3097//         cur_cs = save_cur_cs;
3098//         return no_unit_scanned;
3099//     }
3100// }
3101
3102/*tex
3103    The hash variant makes the binary some 500 bytes larger but it just looks a bit nicer and we
3104    might need to calculate index anyway. Let the compiler sort it out.
3105*/
3106
3107/*tex
3108    User units are bound to dimensions and some more. It could have been a callback but that is
3109    kind of ugly at the scanner level and also bit unnatural. It would perform a bit less but this
3110    is not critical so that's not an excuse. Somehow I like it this way. We would have to pass the
3111    hash and handle it at the \LUA\ end.
3112*/
3113
3114# define unit_hashes(a,b,c,d) \
3115         unit_parameter_hash(a,c): \
3116    case unit_parameter_hash(b,d): \
3117    case unit_parameter_hash(a,d): \
3118    case unit_parameter_hash(b,c)
3119
3120int tex_valid_userunit(halfword cmd, halfword chr, halfword cs)
3121{
3122    (void) cs;
3123    switch (cmd) {
3124        case call_cmd:
3125        case protected_call_cmd:
3126        case semi_protected_call_cmd:
3127        case constant_call_cmd:
3128            return chr && ! get_token_preamble(chr);
3129        case internal_dimension_cmd:
3130        case register_dimension_cmd:
3131        case dimension_cmd:
3132        case lua_value_cmd:
3133            return 1;
3134        default:
3135            return 0;
3136    }
3137}
3138
3139int tex_get_unit_class(halfword index)
3140{
3141    halfword class = unit_parameter(index);
3142    return class < 0 ? - class : class ? user_unit_class : unset_unit_class;
3143}
3144
3145int tex_get_userunit(halfword index, scaled *value)
3146{
3147    halfword cs = unit_parameter(index);
3148    if (cs > 0) {
3149        halfword cmd = eq_type(cs);
3150        halfword chr = eq_value(cs);
3151        switch (cmd) {
3152            case internal_dimension_cmd:
3153            case register_dimension_cmd:
3154                *value = eq_value(chr);
3155                return relative_unit_scanned;
3156            case dimension_cmd:
3157                *value = chr;
3158                return relative_unit_scanned;
3159            case call_cmd:
3160            case protected_call_cmd:
3161            case semi_protected_call_cmd:
3162            case constant_call_cmd:
3163                if (chr && ! get_token_preamble(chr)) {
3164                    halfword list = token_link(chr);
3165                 // if (list) {
3166                        tex_begin_associated_list(list);
3167                        tex_aux_scan_expr(dimension_val_level, 0);
3168                        if (cur_val_level == dimension_val_level) {
3169                            *value = cur_val;
3170                            return 1;
3171                        }
3172                 // }
3173                }
3174                /*tex So we can actually have an empty macro. */
3175                *value = 0;
3176                return 1;
3177            case lua_value_cmd:
3178                /* We pass the index but more for tracing than for usage. */
3179                tex_aux_set_cur_val_by_lua_value_cmd(chr, index);
3180                if (cur_val_level == dimension_val_level) {
3181                    *value = cur_val;
3182                    return 1;
3183             }
3184        }
3185    }
3186    /*tex We could go zero but better not. */
3187    *value = 0;
3188    return 0;
3189}
3190
3191void tex_initialize_units(void)
3192{
3193    unit_parameter(unit_parameter_hash('p','t')) = - tex_unit_class;
3194    unit_parameter(unit_parameter_hash('c','m')) = - tex_unit_class;
3195    unit_parameter(unit_parameter_hash('m','m')) = - tex_unit_class;
3196    unit_parameter(unit_parameter_hash('e','m')) = - tex_unit_class;
3197    unit_parameter(unit_parameter_hash('e','x')) = - tex_unit_class;
3198    unit_parameter(unit_parameter_hash('s','p')) = - tex_unit_class;
3199    unit_parameter(unit_parameter_hash('b','p')) = - tex_unit_class;
3200    unit_parameter(unit_parameter_hash('f','i')) = - tex_unit_class;
3201    unit_parameter(unit_parameter_hash('t','s')) = - luametatex_unit_class;
3202    unit_parameter(unit_parameter_hash('e','s')) = - luametatex_unit_class;
3203    unit_parameter(unit_parameter_hash('e','u')) = - luametatex_unit_class;
3204    unit_parameter(unit_parameter_hash('d','k')) = - luametatex_unit_class;
3205    unit_parameter(unit_parameter_hash('m','u')) = - tex_unit_class;
3206    unit_parameter(unit_parameter_hash('d','d')) = - tex_unit_class;
3207    unit_parameter(unit_parameter_hash('c','c')) = - tex_unit_class;
3208    unit_parameter(unit_parameter_hash('p','c')) = - tex_unit_class;
3209    unit_parameter(unit_parameter_hash('p','x')) = - pdftex_unit_class;
3210    unit_parameter(unit_parameter_hash('i','n')) = - tex_unit_class;
3211}
3212
3213static int tex_aux_scan_unit(halfword *num, halfword *denom, halfword *value, halfword *order)
3214{
3215//AGAIN: /* only for true */
3216    do {
3217        tex_get_x_token();
3218    } while (cur_cmd == spacer_cmd);
3219    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
3220        return quantitity_unit_scanned;
3221    } else {
3222        int chrone, chrtwo, index;
3223        halfword tokone, toktwo;
3224        halfword save_cur_cs = cur_cs;
3225        tokone = cur_tok;
3226        if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
3227            chrone = cur_chr;
3228        } else {
3229            goto BACK_ONE;
3230        }
3231        tex_get_x_token();
3232        toktwo = cur_tok;
3233        if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
3234            chrtwo = cur_chr;
3235        } else {
3236            goto BACK_TWO;
3237        }
3238        cur_cs = save_cur_cs; /* not needed */
3239        index = unit_parameter_index(chrone, chrtwo);
3240        if (index >= 0) {
3241            switch (index) {
3242                case unit_hashes('p','P','t','T'):
3243                    return normal_unit_scanned;
3244                case unit_hashes('c','C','m','M'):
3245                    *num = 7227;
3246                    *denom = 254;
3247                    return normal_unit_scanned;
3248                case unit_hashes('m','M','m','M'):
3249                    *num = 7227;
3250                    *denom = 2540;
3251                    return normal_unit_scanned;
3252                case unit_hashes('e','E','m','M'):
3253                    *value = tex_get_scaled_em_width(cur_font_par);
3254                    return relative_unit_scanned;
3255                case unit_hashes('e','E','x','X'):
3256                    *value = tex_get_scaled_ex_height(cur_font_par);
3257                    return relative_unit_scanned;
3258                case unit_hashes('s','S','p','P'):
3259                    return scaled_point_scanned;
3260                case unit_hashes('b','B','p','P'):
3261                    *num = 7227;
3262                    *denom = 7200;
3263                    return normal_unit_scanned;
3264                case unit_hashes('f','F','i','I'):
3265                    if (order) {
3266                        *order = fi_glue_order;
3267                        if (tex_scan_character("lL", 0, 0, 0)) {
3268                            *order = fil_glue_order;
3269                            if (tex_scan_character("lL", 0, 0, 0)) {
3270                                *order = fill_glue_order;
3271                                if (tex_scan_character("lL", 0, 0, 0)) {
3272                                    *order = filll_glue_order;
3273                                }
3274                            }
3275                        }
3276                        return flexible_unit_scanned;
3277                    }
3278                    break;
3279                case unit_hashes('t','T','s','S'):
3280                    *num = 4588;
3281                    *denom = 645;
3282                    return normal_unit_scanned;
3283                case unit_hashes('e','E','s','S'):
3284                    *num = 9176;
3285                    *denom = 129;
3286                    return normal_unit_scanned;
3287                case unit_hashes('e','E','u','U'):
3288                    *num = 9176 * eu_factor_par;
3289                    *denom = 129 * 10;
3290                    return normal_unit_scanned;
3291                case unit_hashes('d','D','k','K'): /* number: 422042 */
3292                    *num = 49838;  // 152940;
3293                    *denom = 7739; //  23749;
3294                    return normal_unit_scanned;
3295                case unit_hashes('m','M','u','U'):
3296                    if (order) {
3297                        return math_unit_scanned;
3298                    } else {
3299                        break;
3300                    }
3301                case unit_hashes('d','D','d','D'):
3302                    *num = 1238;
3303                    *denom = 1157;
3304                    return normal_unit_scanned;
3305                case unit_hashes('c','C','c','C'):
3306                    *num = 14856;
3307                    *denom = 1157;
3308                    return normal_unit_scanned;
3309                case unit_hashes('p','P','c','C'):
3310                    *num = 12;
3311                    *denom = 1;
3312                    return normal_unit_scanned;
3313                case unit_hashes('p','P','x','X'):
3314                    *value = px_dimension_par;
3315                    return relative_unit_scanned;
3316                case unit_hashes('i','I','n','N'):
3317                    *num = 7227;
3318                    *denom = 100;
3319                    return normal_unit_scanned;
3320                case unit_hashes('n','N','o','O'):
3321                    /* this one is reserved so that we don't clash in the |not| scanner (unary) */
3322                    return scaled_point_scanned;
3323             // case unit_hashes('t','T','r','R'):
3324             //     if (order) {
3325             //        if (tex_scan_mandate_keyword("true", 2)) {
3326             //            /*tex This is now a bogus prefix that might get dropped! */
3327             //            goto AGAIN;
3328             //        }
3329             //     }
3330             //     break;
3331                default:
3332                    if (tex_get_userunit(index, value)) {
3333                        return relative_unit_scanned;
3334                    }
3335            }
3336        }
3337      BACK_TWO:
3338        tex_back_input(toktwo);
3339      BACK_ONE:
3340        tex_back_input(tokone);
3341        cur_cs = save_cur_cs;
3342        return no_unit_scanned;
3343    }
3344}
3345
3346static int tex_aux_scan_unit_only(halfword *value)
3347{
3348    int chrone, chrtwo, index;
3349    halfword tokone, toktwo;
3350    halfword save_cur_cs = cur_cs;
3351    tokone = cur_tok;
3352    if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
3353        chrone = cur_chr;
3354    } else {
3355        goto BACK_ONE;
3356    }
3357    tex_get_x_token(); /* no need for x here */
3358    toktwo = cur_tok;
3359    if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
3360        chrtwo = cur_chr;
3361    } else {
3362        goto BACK_TWO;
3363    }
3364    cur_cs = save_cur_cs; /* not needed */
3365    index = unit_parameter_index(chrone, chrtwo);
3366    if (index >= 0) {
3367        switch (index) {
3368            case unit_hashes('p','P','t','T'):
3369                return normal_unit_scanned;
3370            case unit_hashes('c','C','m','M'):
3371                *value = 1864679;
3372                return normal_unit_scanned;
3373            case unit_hashes('m','M','m','M'):
3374                *value = 186467;
3375                return normal_unit_scanned;
3376            case unit_hashes('e','E','m','M'):
3377                *value = tex_get_scaled_em_width(cur_font_par);
3378                return relative_unit_scanned;
3379            case unit_hashes('e','E','x','X'):
3380                *value = tex_get_scaled_ex_height(cur_font_par);
3381                return relative_unit_scanned;
3382            case unit_hashes('s','S','p','P'):
3383                *value = 1;
3384                return scaled_point_scanned;
3385            case unit_hashes('b','B','p','P'):
3386                *value = 65781;
3387                return normal_unit_scanned;
3388            case unit_hashes('t','T','s','S'):
3389                *value = 466169;
3390                return normal_unit_scanned;
3391            case unit_hashes('e','E','s','S'):
3392                *value = 4661692;
3393                return normal_unit_scanned;
3394            case unit_hashes('e','E','u','U'):
3395                *value = tex_round_xn_over_d(4661692, eu_factor_par, scaling_factor);
3396                return normal_unit_scanned;
3397            case unit_hashes('d','D','k','K'): 
3398                *value = 422042;
3399                return normal_unit_scanned;
3400            case unit_hashes('d','D','d','D'):
3401                *value = 70124;
3402                return normal_unit_scanned;
3403            case unit_hashes('c','C','c','C'):
3404                *value = 841489;
3405                return normal_unit_scanned;
3406            case unit_hashes('p','P','c','C'):
3407                *value = 786432;
3408                return normal_unit_scanned;
3409            case unit_hashes('p','P','x','X'):
3410                *value = px_dimension_par;
3411                return relative_unit_scanned;
3412            case unit_hashes('i','I','n','N'):
3413                *value = 4736286;
3414                return normal_unit_scanned;
3415            case unit_hashes('n','N','o','O'):
3416                /* this one is reserved so that we don't clash in the |not| scanner (unary) */
3417                *value = 1;
3418                return normal_unit_scanned;
3419            default:
3420                if (tex_get_userunit(index, value)) {
3421                    return relative_unit_scanned;
3422                }
3423        }
3424    }
3425  BACK_TWO:
3426    tex_back_input(toktwo);
3427  BACK_ONE:
3428    tex_back_input(tokone);
3429    cur_cs = save_cur_cs;
3430    return no_unit_scanned;
3431}
3432
3433/*tex
3434    When we drop |true| support we can use the next variant which is a bit more efficient and also
3435    handles optional units. Later we will see a more limited variant that also includes the scaler.
3436    This version can still be found in the archive.
3437
3438    The fact that we can say |.3\somedimen| is made easy because when we fail to see a unit we can
3439    check if the seen token is some quantity.
3440*/
3441
3442halfword tex_scan_dimension(int mu, int inf, int shortcut, int optional_equal, halfword *order, int *grouped)
3443{
3444    bool negative = false;
3445    int fraction = 0;
3446    int num = 0;
3447    int denom = 0;
3448    scaled v = 0;
3449    int save_cur_val;
3450    halfword cur_order = normal_glue_order;
3451    lmt_scanner_state.arithmic_error = 0;
3452    if (! shortcut) {
3453        while (1) {
3454            tex_get_x_token();
3455            if (cur_cmd == spacer_cmd) {
3456                continue;
3457            } else if (cur_cmd == left_brace_cmd) {
3458                tex_aux_scan_dimension_expression(1);
3459                if (grouped) { 
3460                    *grouped = 1;
3461                }
3462                goto THATSIT;
3463             // tex_aux_scan_experimental(dimension_val_level, 1);
3464            } else if (cur_tok == equal_token) {
3465                if (optional_equal) {
3466                    optional_equal = 0;
3467                    continue;
3468                } else {
3469                    break;
3470                }
3471            } else if (cur_tok == minus_token) {
3472                negative = ! negative;
3473            } else if (cur_tok != plus_token) {
3474                break;
3475            }
3476        }
3477        if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
3478            cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, mu ? muglue_val_level : dimension_val_level, 0, 0); /* adapts cur_val_level */
3479            if (mu) {
3480                cur_val = tex_aux_coerced_glue(cur_val, cur_val_level);
3481                if (cur_val_level == muglue_val_level) {
3482                    goto ATTACH_SIGN;
3483                } else if (cur_val_level != integer_val_level) {
3484                    tex_aux_mu_error(2);
3485                }
3486            } else if (cur_val_level == dimension_val_level) {
3487                goto ATTACH_SIGN;
3488            } else if (cur_val_level == posit_val_level) {
3489                cur_val = tex_posit_to_dimension(cur_val);
3490                goto ATTACH_SIGN;
3491            }
3492        } else {
3493            int has_fraction = tex_token_is_separator(cur_tok);
3494            if (has_fraction) {
3495                cur_val = 0;
3496            } else {
3497                int cur_radix;
3498                tex_back_input(cur_tok);
3499                cur_val = tex_scan_integer(0, &cur_radix, NULL);
3500                if (cur_radix == 10 && tex_token_is_separator(cur_tok)) {
3501                    has_fraction = 1;
3502                    tex_get_token();
3503                }
3504            }
3505            if (has_fraction) {
3506                unsigned k = 0;
3507                unsigned char digits[18];
3508                while (1) {
3509                    tex_get_x_token();
3510                    if ((cur_tok > nine_token) || (cur_tok < zero_token)) {
3511                        break;
3512                    } else if (k < 17) {
3513                        digits[k] = (unsigned char) (cur_tok - zero_token);
3514                        ++k;
3515                    }
3516                }
3517                fraction = tex_round_decimals_digits(digits, k);
3518                if (cur_cmd != spacer_cmd) {
3519                    tex_back_input(cur_tok);
3520                }
3521            }
3522        }
3523    } else {
3524        /* only when we scan for a glue (filler) component */
3525    }
3526    // kind of weird: 
3527    if (cur_val < 0) {
3528        negative = ! negative;
3529        cur_val = -cur_val;
3530    }
3531    save_cur_val = cur_val;
3532    /*tex
3533        Actually we have cur_tok but it's already pushed back and we also need to skip spaces so
3534        let's not overdo this.
3535    */
3536    if (! lmt_error_state.last_intercept) {
3537        switch (tex_aux_scan_unit(&num, &denom, &v, &cur_order)) {
3538            case no_unit_scanned:
3539                /* error */
3540                if (lmt_error_state.intercept) {
3541                    lmt_error_state.last_intercept = 1;
3542                } else {
3543                    tex_aux_scan_dimension_unknown_unit_error();
3544                }
3545                goto ATTACH_FRACTION;
3546            case normal_unit_scanned:
3547                /* cm mm pt bp dd cc in dk */
3548                if (mu) {
3549                    tex_aux_scan_dimension_unknown_unit_error();
3550                } else if (num) {
3551                    int remainder = 0;
3552                    cur_val = tex_xn_over_d_r(cur_val, num, denom, &remainder);
3553                    fraction = (num * fraction + unity * remainder) / denom;
3554                    cur_val += fraction / unity;
3555                    fraction = fraction % unity;
3556                }
3557                goto ATTACH_FRACTION;
3558            case scaled_point_scanned:
3559                /* sp */
3560                if (mu) {
3561                    tex_aux_scan_dimension_unknown_unit_error();
3562                }
3563                goto DONE;
3564            case relative_unit_scanned:
3565                /* ex em px */
3566                if (mu) {
3567                    tex_aux_scan_dimension_unknown_unit_error();
3568                }
3569                cur_val = tex_nx_plus_y(save_cur_val, v, tex_xn_over_d(v, fraction, unity));
3570                goto DONE;
3571            case math_unit_scanned:
3572                /* mu (slightly different but an error anyway */
3573                if (! mu) {
3574                    tex_aux_scan_dimension_mu_error();
3575                }
3576                goto ATTACH_FRACTION;
3577            case flexible_unit_scanned:
3578                /* fi fil fill filll */
3579                if (mu) {
3580                    tex_aux_scan_dimension_unknown_unit_error();
3581                } else if (! inf) {
3582                    if (! order && lmt_error_state.intercept) {
3583                        lmt_error_state.last_intercept = 1;
3584                    } else {
3585                        tex_aux_scan_dimension_fi_error();
3586                    }
3587                }
3588                goto ATTACH_FRACTION;
3589            case quantitity_unit_scanned:
3590                /* internal quantity */
3591                cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, mu ? muglue_val_level : dimension_val_level, 0, 0); /* adapts cur_val_level */
3592                if (mu) {
3593                    cur_val = tex_aux_coerced_glue(cur_val, cur_val_level);
3594                    if (cur_val_level != muglue_val_level) {
3595                        tex_aux_mu_error(3);
3596                    }
3597                }
3598                v = cur_val;
3599                cur_val = tex_nx_plus_y(save_cur_val, v, tex_xn_over_d(v, fraction, unity));
3600                goto ATTACH_SIGN;
3601        }
3602    }
3603  ATTACH_FRACTION:
3604    if (cur_val >= 040000) { // 0x4000
3605        lmt_scanner_state.arithmic_error = 1;
3606    } else {
3607        cur_val = cur_val * unity + fraction;
3608    }
3609  DONE:
3610    tex_get_x_token();
3611    tex_push_back(cur_tok, cur_cmd, cur_chr);
3612  ATTACH_SIGN:
3613    if (lmt_scanner_state.arithmic_error || (abs(cur_val) >= 010000000000)) { // 0x40000000
3614        if (lmt_error_state.intercept) {
3615            lmt_error_state.last_intercept = 1;
3616        } else {
3617            tex_aux_scan_dimension_out_of_range_error(1);
3618        }
3619        cur_val = max_dimension;
3620        lmt_scanner_state.arithmic_error = 0;
3621    }
3622  THATSIT:
3623    if (order) {
3624        *order = cur_order;
3625    }
3626    if (negative) {
3627        cur_val = -cur_val;
3628    }
3629    return cur_val;
3630}
3631
3632void tex_scan_dimension_validate(void)
3633{
3634    lmt_scanner_state.arithmic_error = 0;
3635    while (1) {
3636        tex_get_x_token();
3637        if (cur_cmd == spacer_cmd || cur_tok == minus_token) {
3638            continue;
3639        } else if (cur_tok != plus_token) {
3640            break;
3641        }
3642    }
3643    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
3644        tex_aux_scan_something_internal(cur_cmd, cur_chr, dimension_val_level, 0, 0);
3645        if (cur_val_level == dimension_val_level || cur_val_level == posit_val_level) {
3646            return;
3647        }
3648    } else {
3649        int has_fraction = tex_token_is_separator(cur_tok);
3650        if (! has_fraction) {
3651            int cur_radix;
3652            tex_back_input(cur_tok);
3653            tex_scan_integer(0, &cur_radix, NULL);
3654            if (cur_radix == 10 && tex_token_is_separator(cur_tok)) {
3655                has_fraction = 1;
3656                tex_get_token();
3657            }
3658        }
3659        if (has_fraction) {
3660            while (1) {
3661                tex_get_x_token();
3662                if (cur_tok > nine_token || cur_tok < zero_token) {
3663                    break;
3664                }
3665            }
3666            if (cur_cmd != spacer_cmd) {
3667                tex_back_input(cur_tok);
3668            }
3669        }
3670    }
3671    {
3672        int num = 0;
3673        int denom = 0;
3674        scaled value;
3675        switch (tex_aux_scan_unit(&num, &denom, &value, NULL)) {
3676            case no_unit_scanned:
3677            case flexible_unit_scanned:
3678                lmt_error_state.last_intercept = 1;
3679                break;
3680            case normal_unit_scanned:
3681            case scaled_point_scanned:
3682            case relative_unit_scanned:
3683            case math_unit_scanned:
3684                break;
3685            case quantitity_unit_scanned:
3686                tex_aux_scan_something_internal(cur_cmd, cur_chr, dimension_val_level, 0, 0);
3687                return;
3688        }
3689    }
3690    tex_get_x_token();
3691    tex_push_back(cur_tok, cur_cmd, cur_chr);
3692}
3693
3694/*tex
3695
3696    The final member of \TEX's value-scanning trio is |scan_glue|, which makes |cur_val| point to
3697    a glue specification. The reference count of that glue spec will take account of the fact that
3698    |cur_val| is pointing to~it. The |level| parameter should be either |glue_val| or |mu_val|.
3699
3700    Since |scan_dimension| was so much more complex than |scan_integer|, we might expect |scan_glue|
3701    to be even worse. But fortunately, it is very simple, since most of the work has already been
3702    done.
3703
3704*/
3705
3706/* todo: get rid of cur_val */
3707
3708halfword tex_scan_glue(int level, int optional_equal, int options_too)
3709{
3710    /*tex should the answer be negated? */
3711    bool negative = false;
3712    /*tex new glue specification */
3713    halfword q = null;
3714    /*tex does |level=mu_val|? */
3715    int mu = level == muglue_val_level;
3716    /*tex Get the next non-blank non-sign. */
3717    do {
3718        /*tex Get the next non-blank non-call token. */
3719        while (1) {
3720            tex_get_x_token();
3721            if (cur_cmd != spacer_cmd) {
3722                if (optional_equal && (cur_tok == equal_token)) {
3723                    optional_equal = 0;
3724                } else if (cur_cmd == left_brace_cmd) {
3725                 // tex_back_input(cur_tok);
3726                    tex_aux_scan_expr(level, 1);
3727                    cur_val_level = level;
3728                    return cur_val;
3729                } else {
3730                    break;
3731                }
3732            }
3733        }
3734        if (cur_tok == minus_token) {
3735            negative = ! negative;
3736            cur_tok = plus_token;
3737        }
3738    } while (cur_tok == plus_token);
3739    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
3740        cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, level, negative, 0);
3741        if (cur_val_level >= glue_val_level) {
3742            if (cur_val_level != level) {
3743                tex_aux_mu_error(4);
3744            }
3745            return cur_val;
3746        }
3747        if (cur_val_level == integer_val_level) {
3748            cur_val = tex_scan_dimension(mu, 0, 1, 0, NULL, NULL);
3749     /*
3750        This only works for |\mutoglue\emwidth| and not a fraction, so let's not do this now. At
3751        some point we could consider making |\mutoglue| and |\gluetomu| more tolerant but they
3752        are hardly used anyway.
3753     */
3754     /* } else if (cur_val_level == dimension_val_level) { */
3755     /*    mu = 0;                                         */
3756        } else if (level == muglue_val_level) {
3757            tex_aux_mu_error(5);
3758        }
3759    } else {
3760        tex_back_input(cur_tok);
3761        cur_val = tex_scan_dimension(mu, 0, 0, 0, NULL, NULL);
3762        if (negative) {
3763            cur_val = -cur_val;
3764        }
3765    }
3766    /*tex
3767        Create a new glue specification whose width is |cur_val|; scan for its stretch and shrink
3768        components.
3769    */
3770    q = tex_new_glue_spec_node(zero_glue);
3771    glue_amount(q) = cur_val;
3772    while (1) {
3773        switch (tex_scan_character("pmlPML", 0, 1, 0)) {
3774            case 0:
3775                return q;
3776            case 'p': case 'P':
3777                if (tex_scan_mandate_keyword("plus", 1)) {
3778                    halfword order = normal_glue_order;
3779                    glue_stretch(q) = tex_scan_dimension(mu, 1, 0, 0, &order, NULL);
3780                    glue_stretch_order(q) = order;
3781                }
3782                break;
3783            case 'm': case 'M':
3784                if (tex_scan_mandate_keyword("minus", 1)) {
3785                    halfword order = normal_glue_order;
3786                    glue_shrink(q) = tex_scan_dimension(mu, 1, 0, 0, &order, NULL);
3787                    glue_shrink_order(q) = order;
3788                }
3789                break;
3790            case 'l': case 'L':
3791                if (options_too && tex_scan_mandate_keyword("limit", 1)) {
3792                    glue_options(q) |= glue_option_limit;
3793                    break;
3794                } else {
3795                    /* fall through */
3796                }
3797            default:
3798                tex_aux_show_keyword_error(options_too ? "plus|minus|limit" : "plus|minus");
3799                return q;
3800        }
3801    }
3802}
3803
3804/*tex
3805
3806    This started as an experiment. A font object is just a container for a combination of id and
3807    scales. It permits fast font switching (not that setting the font id and scales separately is
3808    that slow) and has the benefit of a more sparse logging. We use nodes and not some array
3809    because after all we always have symbolic names and we then get saving and restoring as well as
3810    memory management for free.
3811
3812    When an spec is given we make a copy but can overload the scales after that. Otherwise we just
3813    create a new spec with default scales 1000. This fontspec object was introduced after we had
3814    experimental compact font support in \CONTEXT\ for over a year working well.
3815
3816*/
3817
3818halfword tex_scan_font(int optional_equal)
3819{
3820    halfword fv = null;
3821    halfword id, fs;
3822    if (optional_equal) {
3823        tex_scan_optional_equals();
3824    }
3825    id = tex_scan_font_identifier(&fv);
3826    if (fv) {
3827        fs = tex_copy_node(fv);
3828    } else {
3829        /*tex We create a new one and assign the mandate id. */
3830        fs = tex_new_node(font_spec_node, normal_code);
3831        font_spec_identifier(fs) = id;
3832        font_spec_scale(fs) = unused_scale_value;
3833        font_spec_x_scale(fs) = unused_scale_value;
3834        font_spec_y_scale(fs) = unused_scale_value;
3835        font_spec_slant(fs) = 0;
3836        font_spec_weight(fs) = 0;
3837    }
3838    while (1) {
3839        switch (tex_scan_character("asxywoASXYWO", 0, 1, 0)) {
3840            case 0:
3841                return fs;
3842            case 'a': case 'A':
3843                if (tex_scan_mandate_keyword("all", 1)) {
3844                    font_spec_scale(fs) = tex_scan_scale_factor(0);
3845                    font_spec_x_scale(fs) = tex_scan_scale_factor(0);
3846                    font_spec_y_scale(fs) = tex_scan_scale_factor(0);
3847                    font_spec_slant(fs) = tex_scan_scale_factor(0);
3848                    font_spec_weight(fs) =  tex_scan_scale_factor(0);
3849                    font_spec_state(fs) |= font_spec_all_set;
3850                }
3851                break;
3852            case 's': case 'S':
3853                switch (tex_scan_character("clCL", 0, 1, 0)) {
3854                    case 'c': case 'C':
3855                        if (tex_scan_mandate_keyword("scale", 2)) {
3856                            font_spec_scale(fs) = tex_scan_scale_factor(0);
3857                            font_spec_state(fs) |= font_spec_scale_set;
3858                        }
3859                        break;
3860                    case 'l': case 'L':
3861                        if (tex_scan_mandate_keyword("slant", 2)) {
3862                            font_spec_slant(fs) = tex_scan_scale_factor(0);
3863                            font_spec_state(fs) |= font_spec_slant_set;
3864                        }
3865                        break;
3866                    default:
3867                        tex_aux_show_keyword_error("scale|slant");
3868                        return fs;
3869                }
3870                break;
3871            case 'x': case 'X':
3872                if (tex_scan_mandate_keyword("xscale", 1)) {
3873                    font_spec_x_scale(fs) = tex_scan_scale_factor(0);
3874                    font_spec_state(fs) |= font_spec_x_scale_set;
3875                }
3876                break;
3877            case 'y': case 'Y':
3878                if (tex_scan_mandate_keyword("yscale", 1)) {
3879                    font_spec_y_scale(fs) = tex_scan_scale_factor(0);
3880                    font_spec_state(fs) |= font_spec_y_scale_set;
3881                }
3882                break;
3883            case 'w': case 'W':
3884                if (tex_scan_mandate_keyword("weight", 1)) {
3885                    font_spec_weight(fs) = tex_scan_scale_factor(0);
3886                    font_spec_state(fs) |= font_spec_weight_set;
3887                }
3888                break;
3889            case 'o': case 'O': /* oblique */
3890                if (tex_scan_mandate_keyword("oblique", 1)) {
3891                    font_spec_slant(fs) = tex_scan_scale_factor(0);
3892                    font_spec_state(fs) |= font_spec_slant_set;
3893                }
3894                break;
3895            default:
3896                return fs;
3897        }
3898    }
3899}
3900
3901/*tex
3902
3903    This procedure is supposed to scan something like |\skip \count 12|, i.e., whatever can follow
3904    |\the|, and it constructs a token list containing something like |-3.0pt minus 0.5 fill|.
3905
3906    There is a bit duplicate code here but it makes a nicer switch as we also need to deal with
3907    tokens and font identifiers.
3908
3909*/
3910
3911# define push_selector { \
3912    saved_selector = lmt_print_state.selector; \
3913    lmt_print_state.selector = new_string_selector_code; \
3914}
3915
3916# define pop_selector { \
3917    lmt_print_state.selector = saved_selector; \
3918}
3919
3920halfword tex_the_value_toks(int code, halfword *tail, halfword property) /* maybe split this as already checked */
3921{
3922    tex_get_x_token();
3923    cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, token_val_level, 0, property);
3924    switch (cur_val_level) {
3925        case integer_val_level:
3926        case attribute_val_level:
3927            {
3928                int saved_selector;
3929                push_selector;
3930                tex_print_int(cur_val);
3931                pop_selector;
3932                return tex_cur_str_toks(tail);
3933            }
3934        case posit_val_level:
3935            {
3936                int saved_selector;
3937                push_selector;
3938                tex_print_posit(cur_val);
3939                pop_selector;
3940                return tex_cur_str_toks(tail);
3941            }
3942        case dimension_val_level:
3943            {
3944                int saved_selector;
3945                push_selector;
3946                tex_print_dimension(cur_val, code == the_without_unit_code ? no_unit : pt_unit);
3947                pop_selector;
3948                return tex_cur_str_toks(tail);
3949            }
3950        case glue_val_level:
3951        case muglue_val_level:
3952            {
3953                int saved_selector;
3954                push_selector;
3955                tex_print_spec(cur_val, (code != the_without_unit_code) ? (cur_val_level == glue_val_level ? pt_unit : mu_unit) : no_unit);
3956                tex_flush_node(cur_val);
3957                pop_selector;
3958                return tex_cur_str_toks(tail);
3959            }
3960        case token_val_level:
3961            {
3962                /*tex Copy the token list */
3963                halfword h = null;
3964                halfword p = null;
3965                if (cur_val) {
3966                    /*tex Do not copy the reference count! */
3967                    halfword r = token_link(cur_val);
3968                    while (r) {
3969                        p = tex_store_new_token(p, token_info(r));
3970                        if (! h) {
3971                            h = p;
3972                        }
3973                        r = token_link(r);
3974                    }
3975                }
3976                if (tail) {
3977                    *tail = p;
3978                }
3979                return h;
3980            }
3981        case font_val_level:
3982            {
3983                int saved_selector;
3984                push_selector;
3985                tex_print_font_identifier(cur_val);
3986                pop_selector;
3987                return tex_cur_str_toks(tail);
3988            }
3989        case mathspec_val_level:
3990            {
3991                /*tex So we don't mess with null font. */
3992                if (cur_val) {
3993                    int saved_selector;
3994                    push_selector;
3995                    tex_print_mathspec(cur_val);
3996                    pop_selector;
3997                    return tex_cur_str_toks(tail);
3998                } else {
3999                    return null;
4000                }
4001            }
4002        case fontspec_val_level:
4003            {
4004                /*tex So we don't mess with null font. */
4005                if (cur_val) {
4006                    int saved_selector;
4007                    push_selector;
4008                    tex_print_font_specifier(cur_val);
4009                    pop_selector;
4010                    return tex_cur_str_toks(tail);
4011                } else {
4012                    return null;
4013                }
4014            }
4015        case list_val_level:
4016            {
4017                if (cur_val) {
4018                  // halfword copy = tex_copy_node_list(cur_val, null);
4019                     halfword copy = tex_copy_node(cur_val);
4020                     tex_tail_append(copy);
4021                     cur_val = null;
4022                }
4023                break;
4024            }
4025    }
4026    return null;
4027}
4028
4029void tex_detokenize_list(halfword head)
4030{
4031    int saved_selector;
4032    push_selector;
4033    tex_show_token_list(head, 0, 0);
4034    pop_selector;
4035}
4036
4037halfword tex_the_detokenized_toks(halfword *tail, int expand, int protect) // maybe we need single here too
4038{
4039    halfword head = expand ? tex_scan_toks_expand(0, tail, 1, protect) : tex_scan_general_text(tail);
4040    if (head) {
4041        halfword first = expand ? token_link(head) : head;
4042        if (first) {
4043            int saved_selector;
4044            push_selector;
4045            tex_show_token_list(first, 0, protect);
4046            pop_selector;
4047            tex_flush_token_list(head);
4048            return tex_cur_str_toks(tail);
4049        }
4050    }
4051    return null;
4052}
4053
4054/*tex
4055    The |the_without_unit| variant implements |\thewithoutunit| is not really that impressive but
4056    just there because it's cheap to implement and also avoids a kind of annoying macro definition,
4057    one of the kind that demonstrates that one really understands \TEX. Now, with plenty of memory
4058    and disk space the added code is probably not noticed and adds less bytes to the binary than a
4059    macro does to the (and probably every) format file.
4060*/
4061
4062halfword tex_the_toks(int code, halfword *tail)
4063{
4064    switch (code) {
4065        case the_code:
4066        case the_without_unit_code:
4067            return tex_the_value_toks(code, tail, 0);
4068     /* case the_with_property_code: */
4069     /*     return tex_the_value_toks(code, tail, tex_scan_integer(0, 0)); */
4070        case unexpanded_code:
4071            return tex_scan_general_text(tail);
4072        case detokenize_code:
4073        case expanded_detokenize_code:
4074        case protected_detokenize_code:
4075        case protected_expanded_detokenize_code:
4076            return tex_the_detokenized_toks(tail,
4077                code == expanded_detokenize_code  || code == protected_expanded_detokenize_code,
4078                code == protected_detokenize_code || code == protected_expanded_detokenize_code
4079            );
4080        default:
4081            return null;
4082    }
4083}
4084
4085strnumber tex_the_scanned_result(void)
4086{
4087    /*tex return value */
4088    strnumber r;
4089    /*tex holds |selector| setting */
4090    int saved_selector;
4091    push_selector;
4092    switch (cur_val_level) {
4093        case integer_val_level:
4094        case attribute_val_level:
4095            tex_print_int(cur_val);
4096            break;
4097        case posit_val_level:
4098            tex_print_posit(cur_val);
4099            break;
4100        case dimension_val_level:
4101            tex_print_dimension(cur_val, pt_unit);
4102            break;
4103        case glue_val_level:
4104            tex_print_spec(cur_val, pt_unit);
4105            tex_flush_node(cur_val);
4106            break;
4107        case muglue_val_level:
4108            tex_print_spec(cur_val, mu_unit);
4109            tex_flush_node(cur_val);
4110            break;
4111        case token_val_level:
4112            if (cur_val) {
4113                tex_token_show(cur_val);
4114                break;
4115            } else {
4116                r = get_nullstr();
4117                goto DONE;
4118            }
4119        /*
4120        case list_val_level:
4121            printf("TODO\n");
4122            if (cur_val) {
4123                cur_val = tex_copy_node(cur_val);
4124                tex_couple_nodes(cur_list.tail, cur_val);
4125                cur_list.tail = cur_val;
4126            }
4127            r = get_nullstr();
4128            goto DONE;
4129        */
4130        default:
4131            r = get_nullstr();
4132            goto DONE;
4133    }
4134    r = tex_make_string();
4135  DONE:
4136    pop_selector;
4137    return r;
4138}
4139
4140/*tex
4141
4142    The following routine is used to implement |\fontdimen n f|. We no longer automatically increase
4143    the number of allocated dimensions because we have plenty of dimensions available and loading is
4144    done differently anyway.
4145
4146*/
4147
4148static halfword tex_aux_scan_font_id_and_parameter(halfword *fnt, halfword *n)
4149{
4150    *n = tex_scan_integer(0, NULL, NULL);
4151    *fnt = tex_scan_font_identifier(NULL);
4152    if (*n <= 0 || *n > max_integer) {
4153        tex_handle_error(
4154            normal_error_type,
4155            "Font '%s' has at most %i fontdimen parameters",
4156            font_original(*fnt), font_parameter_count(*fnt),
4157            "The font parameter index is out of range."
4158        );
4159        return 0;
4160    } else {
4161        return 1;
4162    }
4163}
4164
4165void tex_set_font_dimension(void)
4166{
4167    halfword fnt, n;
4168    if (tex_aux_scan_font_id_and_parameter(&fnt, &n)) {
4169        tex_set_font_parameter(fnt, n, tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
4170    }
4171}
4172
4173halfword tex_get_font_dimension(void)
4174{
4175    halfword fnt, n;
4176    return tex_aux_scan_font_id_and_parameter(&fnt, &n) ? tex_get_font_parameter(fnt, n) : null;
4177}
4178
4179void tex_set_scaled_font_dimension(void)
4180{
4181    halfword fnt, n;
4182    if (tex_aux_scan_font_id_and_parameter(&fnt, &n)) {
4183        tex_set_scaled_parameter(fnt, n, tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
4184    }
4185}
4186
4187halfword tex_get_scaled_font_dimension(void)
4188{
4189    halfword fnt, n;
4190    return tex_aux_scan_font_id_and_parameter(&fnt, &n) ? tex_get_scaled_parameter(fnt, n) : null;
4191}
4192
4193/*tex Declare procedures that scan font-related stuff. */
4194
4195halfword tex_scan_math_style_identifier(int tolerant, int styles)
4196{
4197    halfword style = tex_scan_integer(0, NULL, NULL);
4198    if (is_valid_math_style(style)) {
4199        return style;
4200    } else if (styles && are_valid_math_styles(style)) {
4201        return style;
4202    } else if (tolerant) {
4203        return -1;
4204    } else {
4205        tex_handle_error(
4206            back_error_type,
4207            "Missing math style, treated as \\displaystyle",
4208            "A style should have been here; I inserted '\\displaystyle'."
4209        );
4210        return display_style;
4211    }
4212}
4213
4214halfword tex_scan_math_parameter(void)
4215{
4216    do {
4217        tex_get_x_token();
4218    } while (cur_cmd == spacer_cmd);
4219    if (cur_cmd == math_parameter_cmd && cur_chr < math_parameter_last) {
4220        return cur_chr;
4221    } else {
4222        tex_handle_error(
4223            normal_error_type,
4224            "Invalid math parameter",
4225            "I'm going to ignore this one."
4226        );
4227        return -1;
4228    }
4229}
4230
4231halfword tex_scan_fontspec_identifier(void)
4232{
4233    /*tex Get the next non-blank non-call. */
4234    do {
4235        tex_get_x_token();
4236    } while (cur_cmd == spacer_cmd);
4237    if (cur_cmd == fontspec_cmd) {
4238        return cur_chr;
4239    } else {
4240        return 0;
4241    }
4242}
4243
4244halfword tex_scan_font_identifier(halfword *spec)
4245{
4246    /*tex Get the next non-blank non-call. */
4247    do {
4248        tex_get_x_token();
4249    } while (cur_cmd == spacer_cmd);
4250    switch (cur_cmd) {
4251        case define_font_cmd:
4252            return cur_font_par;
4253        case set_font_cmd:
4254            return cur_chr;
4255        case fontspec_cmd:
4256            {
4257                halfword fnt = tex_get_font_identifier(cur_chr);
4258                if (fnt && spec) {
4259                    *spec = fnt ? cur_chr : null;
4260                }
4261                return fnt;
4262            }
4263        case define_family_cmd:
4264            {
4265                halfword siz = cur_chr;
4266                halfword fam = tex_scan_math_family_number();
4267                halfword fnt = tex_fam_fnt(fam, siz);
4268                return fnt;
4269            }
4270        case register_integer_cmd:
4271            {
4272                /*tex Checking here saves a push back when we want an integer. */
4273                halfword fnt = register_integer_number(cur_chr);
4274                if (tex_is_valid_font(fnt)) {
4275                    return fnt;
4276                } else {
4277                    break; /* to error */
4278                }
4279            }
4280        case integer_cmd:
4281            {
4282                /*tex Checking here saves a push back when we want an integer. */
4283                halfword fnt = cur_chr;
4284                if (tex_is_valid_font(fnt)) {
4285                    return fnt;
4286                } else {
4287                    break; /* to error */
4288                }
4289            }
4290        case internal_integer_cmd:
4291            {
4292                /*tex Bonus:  |\setfontid| */
4293                if (internal_integer_number(cur_chr) == font_code) {
4294                    halfword fnt = tex_scan_integer(0, NULL, NULL);
4295                    if (tex_is_valid_font(fnt)) {
4296                        return fnt;
4297                    }
4298                }
4299                break; /* to error */
4300            }
4301        default:
4302            {
4303                /*tex We abuse |scan_cardinal| here but we have to push back.  */
4304                unsigned fnt = null_font;
4305                tex_back_input(cur_tok);
4306                if (tex_scan_cardinal(0, &fnt, 1)) {
4307                    if (tex_is_valid_font((halfword) fnt)) {
4308                        return (halfword) fnt;
4309                    }
4310                }
4311                break; /* to error */
4312            }
4313    }
4314    tex_handle_error(
4315        back_error_type,
4316        "Missing or invalid font identifier (or equivalent) or integer (register or otherwise)",
4317        "I was looking for a control sequence whose current meaning has been defined by\n"
4318        "\\font or a valid font id number."
4319    );
4320    return null_font;
4321}
4322
4323/*tex
4324
4325    The |scan_general_text| procedure is much like |scan_toks (false, false)|, but will be invoked
4326    via |expand|, i.e., recursively.
4327
4328    The token list (balanced text) created by |scan_general_text| begins at |link (temp_token_head)|
4329    and ends at |cur_val|. (If |cur_val = temp_token_head|, the list is empty.)
4330
4331*/
4332
4333halfword tex_scan_general_text(halfword *tail)
4334{
4335    /*tex The tail of the token list being built: */
4336    halfword p = get_reference_token();
4337    halfword head;
4338    /*tex The number of nested left braces: */
4339    halfword unbalance = 0;
4340    halfword saved_scanner_status = lmt_input_state.scanner_status;
4341    halfword saved_warning_index = lmt_input_state.warning_index;
4342    halfword saved_def_ref = lmt_input_state.def_ref;
4343    lmt_input_state.scanner_status = scanner_is_absorbing;
4344    lmt_input_state.warning_index = cur_cs;
4345    lmt_input_state.def_ref = p;
4346    /*tex Remove the compulsory left brace. */
4347    tex_scan_left_brace();
4348    while (1) {
4349        tex_get_token();
4350        if (! cur_cs) {
4351            switch (cur_cmd) {
4352                case left_brace_cmd:
4353                    if (! cur_cs) {
4354                        ++unbalance;
4355                    }
4356                    break;
4357                case right_brace_cmd:
4358                    if (unbalance) {
4359                        --unbalance;
4360                        break;
4361                    } else {
4362                        goto DONE;
4363                    }
4364            }
4365        }
4366        p = tex_store_new_token(p, cur_tok);
4367    }
4368  DONE:
4369    head = token_link(lmt_input_state.def_ref);
4370    if (tail) {
4371        *tail = head ? p : null;
4372    }
4373    /*tex Discard reference count. */
4374    tex_put_available_token(lmt_input_state.def_ref);
4375    lmt_input_state.scanner_status = saved_scanner_status;
4376    lmt_input_state.warning_index = saved_warning_index;
4377    lmt_input_state.def_ref = saved_def_ref;
4378    return head;
4379}
4380
4381/*tex
4382
4383    |scan_toks|. This function returns a pointer to the tail of a new token list, and it also makes
4384    |def_ref| point to the reference count at the head of that list.
4385
4386    There are two boolean parameters, |macro_def| and |xpand|. If |macro_def| is true, the goal is
4387    to create the token list for a macro definition; otherwise the goal is to create the token list
4388    for some other \TEX\ primitive: |\mark|, |\output|, |\everypar|, |\lowercase|, |\uppercase|,
4389    |\message|, |\errmessage|, |\write|, or |\special|. In the latter cases a left brace must be
4390    scanned next; this left brace will not be part of the token list, nor will the matching right
4391    brace that comes at the end. If |xpand| is false, the token list will simply be copied from the
4392    input using |get_token|. Otherwise all expandable tokens will be expanded until unexpandable
4393    tokens are left, except that the results of expanding |\the| are not expanded further. If both
4394    |macro_def| and |xpand| are true, the expansion applies only to the macro body (i.e., to the
4395    material following the first |left_brace| character).
4396
4397    The value of |cur_cs| when |scan_toks| begins should be the |eqtb| address of the control
4398    sequence to display in runaway error messages.
4399
4400    Watch out: there are two extensions to the macro definition parser: a |#0| will just gobble the
4401    argument and not copy it to the parameter stack, and |#+| will not remove braces around a
4402    \quote {single group} argument, something that comes in handy when you grab and pass over an
4403    argument.
4404
4405    If the next character is a parameter number, make |cur_tok| a |match| token; but if it is a
4406    left brace, store |left_brace|, |end_match|, set |hash_brace|, and |goto done|.
4407
4408    For practical reasone, we have split the |scan_toks| function up in four smaller dedicated
4409    functions. When we add features it makes no sense to clutter the code even more. Keep in mind
4410    that compared to the reference \TEX\ inplementation we have to support |\expanded| token lists
4411    but also |\protected| and friends. There is of course some overlap now but that's a small
4412    price to pay for readability.
4413
4414    The split functions need less redundant checking and the expandable variants got one loop
4415    instead of two nested loops.
4416
4417*/
4418
4419static inline bool tex_parameter_escape_mode(void)
4420{
4421    return (parameter_mode_par & parameter_escape_mode) == parameter_escape_mode;
4422}
4423
4424halfword tex_scan_toks_normal(int left_brace_found, halfword *tail)
4425{
4426    halfword unbalance = 0;
4427    halfword result = get_reference_token();
4428    halfword p = result;
4429    lmt_input_state.scanner_status = scanner_is_absorbing;
4430    lmt_input_state.warning_index = cur_cs;
4431    lmt_input_state.def_ref = result;
4432    if (! left_brace_found) {
4433        tex_scan_left_brace();
4434    }
4435    while (1) {
4436        tex_get_token();
4437        switch (cur_cmd) {
4438            case left_brace_cmd:
4439                if (! cur_cs) {
4440                    ++unbalance;
4441                }
4442                break;
4443            case right_brace_cmd:
4444                if (! cur_cs) {
4445                    if (unbalance) {
4446                        --unbalance;
4447                    } else {
4448                        goto DONE;
4449                    }
4450                }
4451                break;
4452            case prefix_cmd:
4453                if (cur_chr == enforced_code && (! overload_mode_par || lmt_main_state.run_state != production_state)) { /* todo cur_tok == let_aliased_token */
4454                    cur_tok = token_val(prefix_cmd, always_code);
4455                }
4456                break;
4457        }
4458        p = tex_store_new_token(p, cur_tok);
4459    }
4460  DONE:
4461    lmt_input_state.scanner_status = scanner_is_normal;
4462    if (tail) {
4463        *tail = p;
4464    }
4465    return result;
4466}
4467
4468halfword tex_scan_toks_expand(int left_brace_found, halfword *tail, int expandconstant, int keepparameters)
4469{
4470    halfword unbalance = 0;
4471    halfword result = get_reference_token();
4472    halfword p = result;
4473    lmt_input_state.scanner_status = scanner_is_absorbing;
4474    lmt_input_state.warning_index = cur_cs;
4475    lmt_input_state.def_ref = result;
4476    if (! left_brace_found) {
4477        tex_scan_left_brace();
4478    }
4479    while (1) {
4480      PICKUP:
4481        tex_get_next();
4482        switch (cur_cmd) {
4483            case call_cmd:
4484            case tolerant_call_cmd:
4485                tex_expand_current_token();
4486                goto PICKUP;
4487            case constant_call_cmd:
4488                {
4489                    halfword h = token_link(cur_chr);
4490                    while (h) {
4491                        p = tex_store_new_token(p, token_info(h));
4492                        h = token_link(h);
4493                    }
4494                    goto PICKUP;
4495                }
4496            case protected_call_cmd:
4497            case tolerant_protected_call_cmd:
4498                cur_tok = cs_token_flag + cur_cs;
4499                goto APPENDTOKEN;
4500            case semi_protected_call_cmd:
4501            case tolerant_semi_protected_call_cmd:
4502                if (expandconstant) {
4503                    tex_expand_current_token();
4504                    goto PICKUP;
4505                } else {
4506                    cur_tok = cs_token_flag + cur_cs;
4507                    goto APPENDTOKEN;
4508                }
4509            case the_cmd:
4510                {
4511                    halfword t = null;
4512                    halfword h = tex_the_toks(cur_chr, &t);
4513                    if (h) {
4514                        set_token_link(p, h);
4515                        p = t;
4516                    }
4517                    goto PICKUP;
4518                }
4519            case parameter_cmd:
4520                {
4521                    /*tex This is kind of tricky and thereby experimental. No need for the extensive x here. */
4522                    halfword tok1, tok2;
4523                    tex_x_token();
4524                    tok1 = cur_tok;
4525                    tex_get_next();
4526                    tex_x_token();
4527                    tok2 = cur_tok;
4528                    if (keepparameters || ! tex_parameter_escape_mode()) {
4529                        /* not done */
4530                    } else {
4531                        halfword t;
4532                        halfword h = tex_expand_parameter(tok2, &t);
4533                        if (h) {
4534                            set_token_link(p, h);
4535                            p = t;
4536                            goto PICKUP;
4537                        } else {
4538                            tex_flush_token_list(h);
4539                        }
4540                    }
4541                    p = tex_store_new_token(p, tok1);
4542                    p = tex_store_new_token(p, tok2);
4543                    goto PICKUP;
4544                }
4545            case prefix_cmd:
4546                if (cur_chr == enforced_code && (! overload_mode_par || lmt_main_state.run_state != production_state)) {
4547                    cur_tok = token_val(prefix_cmd, always_code);
4548                    goto APPENDTOKEN;
4549                }
4550            default:
4551                if (cur_cmd > max_command_cmd) {
4552                    tex_expand_current_token();
4553                    goto PICKUP;
4554                } else {
4555                    goto DONEEXPANDING;
4556                }
4557        }
4558      DONEEXPANDING:
4559        tex_x_token();
4560     // if (cur_tok < right_brace_limit) {
4561     //     if (cur_cmd == left_brace_cmd) {
4562     //         ++unbalance;
4563     //     } else if (unbalance) {
4564     //         --unbalance;
4565     //     } else {
4566     //         goto FINALYDONE;
4567     //     }
4568     // }
4569        if (! cur_cs) {
4570            switch (cur_cmd) {
4571                case left_brace_cmd:
4572                    ++unbalance;
4573                    break;
4574                case right_brace_cmd:
4575                    if (unbalance) {
4576                        --unbalance;
4577                    } else {
4578                        goto FINALYDONE;
4579                    }
4580                    break;
4581            }
4582        }
4583      APPENDTOKEN:
4584        p = tex_store_new_token(p, cur_tok);
4585    }
4586  FINALYDONE:
4587    lmt_input_state.scanner_status = scanner_is_normal;
4588    if (tail) {
4589        *tail = p;
4590    }
4591    return result;
4592}
4593
4594static void tex_aux_too_many_parameters_error(void)
4595{
4596    tex_handle_error(
4597        normal_error_type,
4598        "You already have 15 parameters",
4599        "I'm going to ignore the # sign you just used, as well the token that followed it.\n"
4600        /*tex That last bit was added in the TeX 2021 buglet fix round. */
4601    );
4602}
4603
4604static void tex_aux_parameters_order_error(void)
4605{
4606    tex_handle_error(
4607        back_error_type,
4608        "Parameters must be numbered consecutively",
4609        "I've inserted the digit you should have used after the #."
4610    );
4611}
4612
4613static void tex_aux_missing_brace_error(void)
4614{
4615    tex_handle_error(
4616        normal_error_type,
4617        "Missing { inserted",
4618        "Where was the left brace? You said something like '\\def\\a}', which I'm going to\n"
4619        "interpret as '\\def\\a{}'."
4620    );
4621}
4622
4623static void tex_aux_illegal_parameter_in_body_error(void)
4624{
4625    tex_handle_error(
4626        back_error_type,
4627        "Illegal parameter number in definition of %S",
4628        lmt_input_state.warning_index,
4629        "You meant to type ## instead of #, right? Or maybe a } was forgotten somewhere\n"
4630        "earlier, and things are all screwed up? I'm going to assume that you meant ##."
4631    );
4632}
4633
4634/*tex
4635    There are interesting aspects in reporting the preamble, like:
4636
4637    \starttyping
4638    \def\test#1#{test#1} : macro:#1{->test#1{
4639    \stoptyping
4640
4641    So, the \type {#} gets reported as left brace.
4642
4643    The |\par| handling depends on the mode
4644
4645    \starttyping
4646    % 0x1 text | 0x2 macro | 0x4 go-on
4647
4648    \autoparagraphmode0 \def\foo#1\par{[#1]} 0: \meaningfull\foo\par \foo test\par test\par
4649    \autoparagraphmode1 \def\foo#1\par{[#1]} 1: \meaningfull\foo\par \foo test\par test\par
4650    \autoparagraphmode2 \def\foo#1\par{[#1]} 2: \meaningfull\foo\par \foo test\par test\par % discard after #1 till \par
4651    \autoparagraphmode4 \def\foo#1\par{[#1]} 4: \meaningfull\foo\par \foo test\par test\par
4652    \stoptyping
4653*/
4654
4655/* There is no real gain and we get a 1K larger binary: */ /* inline */
4656
4657static int tex_aux_valid_macro_preamble(halfword *p, int *counter, halfword *hash_brace)
4658{
4659    halfword h = *p;
4660    while (1) {
4661        tex_get_token();
4662        switch (cur_cmd) {
4663            case left_brace_cmd:
4664            case right_brace_cmd:
4665                if (cur_cs) {
4666                    break;
4667                } else {
4668                    goto DONE;
4669                }
4670            case parameter_cmd:
4671                tex_get_token();
4672                /*
4673                    cf. TeX 2021 we not do a more strict testing. Interesting is that wondered why we
4674                    had a more generous test here but just considered that a feature or intended side
4675                    effect but in the end we have to be strict.
4676
4677                    \starttyping
4678                    \def\cs#1#\bgroup hi#1}       % was weird but okay pre 2021
4679                    \def\cs#1\bgroup{hi#1\bgroup} % but this is better indeed
4680                    \stoptyping
4681                */
4682                if (cur_tok < left_brace_limit) {
4683             /* if (cur_cmd == left_brace_cmd) { */
4684                    /*tex The |\def\foo#{}| case. */
4685                    *hash_brace = cur_tok;
4686                    *p = tex_store_new_token(*p, cur_tok);
4687                    *p = tex_store_new_token(*p, end_match_token);
4688                    set_token_preamble(h, macro_with_preamble);
4689                    set_token_parameters(h, *counter);
4690                    return 1;
4691                } else if (*counter == 0xF) {
4692                    tex_aux_too_many_parameters_error();
4693                } else {
4694                    switch (cur_tok) {
4695                        case zero_token:
4696                            ++*counter;
4697                            cur_tok = match_token;
4698                            break;
4699                        case asterisk_token:
4700                            cur_tok = spacer_match_token;
4701                            break;
4702                        case plus_token:
4703                            ++*counter;
4704                            cur_tok = keep_match_token;
4705                            break;
4706                        case minus_token:
4707                            cur_tok = thrash_match_token;
4708                            break;
4709                        case period_token:
4710                            cur_tok = par_spacer_match_token;
4711                            break;
4712                        case comma_token:
4713                            cur_tok = keep_spacer_match_token;
4714                            break;
4715                        case slash_token:
4716                            ++*counter;
4717                            cur_tok = prune_match_token;
4718                            break;
4719                        case colon_token:
4720                            cur_tok = continue_match_token;
4721                            break;
4722                        case semi_colon_token:
4723                            cur_tok = quit_match_token;
4724                            break;
4725                        case equal_token:
4726                            ++*counter;
4727                            cur_tok = mandate_match_token;
4728                            break;
4729                        case circumflex_token_l:
4730                        case circumflex_token_o:
4731                            ++*counter;
4732                            cur_tok = leading_match_token;
4733                            break;
4734                        case underscore_token_l:
4735                        case underscore_token_o:
4736                            ++*counter;
4737                            cur_tok = mandate_keep_match_token;
4738                            break;
4739                        case at_token_l:
4740                        case at_token_o:
4741                            cur_tok = par_command_match_token;
4742                            break;
4743# if (match_experiment)
4744case d_token_l:
4745case d_token_o:
4746    ++*counter;
4747    cur_tok = dimension_match_token;
4748    break;
4749case i_token_l:
4750case i_token_o:
4751    ++*counter;
4752    cur_tok = integer_match_token;
4753    break;
4754# endif 
4755                        case L_token_l:
4756                        case L_token_o:
4757                            cur_tok = left_match_token;
4758                            break;
4759                        case R_token_l:
4760                        case R_token_o:
4761                            cur_tok = right_match_token;
4762                            break;
4763                        case G_token_l:
4764                        case G_token_o:
4765                            cur_tok = gobble_match_token;
4766                            break;
4767                        case M_token_l:
4768                        case M_token_o:
4769                            cur_tok = gobble_more_match_token;
4770                            break;
4771                        case S_token_l:
4772                        case S_token_o:
4773                            cur_tok = brackets_match_token;
4774                            break;
4775                        case P_token_l:
4776                        case P_token_o:
4777                            cur_tok = parentheses_match_token;
4778                            break;
4779                        case X_token_l:
4780                        case X_token_o:
4781                            cur_tok = angles_match_token;
4782                            break;
4783                        default:
4784                            if (cur_tok >= one_token && cur_tok <= nine_token) {
4785                                ++*counter;
4786                                if ((cur_tok - other_token - '0') == *counter) {
4787                                    cur_tok += match_token - other_token ;
4788                                    break;
4789                                }
4790                            } else if (cur_tok >= A_token_l && cur_tok <= F_token_l) {
4791                                ++*counter;
4792                                if ((cur_tok - A_token_l + 10) == *counter) {
4793                                    cur_tok += match_token - letter_token;
4794                                    break;
4795                                }
4796                            }
4797                            tex_aux_parameters_order_error();
4798                            cur_tok = match_token; /* zero */
4799                            break;
4800                    }
4801                }
4802                break;
4803            case end_paragraph_cmd:
4804                if (! auto_paragraph_mode(auto_paragraph_macro)) {
4805                   cur_tok = par_command_match_token;
4806                }
4807                break;
4808        }
4809        *p = tex_store_new_token(*p, cur_tok);
4810    }
4811  DONE:
4812    if (h != *p) {
4813        *p = tex_store_new_token(*p, end_match_token);
4814        set_token_preamble(h, macro_with_preamble);
4815        set_token_parameters(h, *counter);
4816    }
4817    if (cur_cmd == right_brace_cmd) {
4818        ++lmt_input_state.align_state;
4819        tex_aux_missing_brace_error();
4820        return 0;
4821    } else {
4822        return 1;
4823    }
4824}
4825
4826halfword tex_scan_macro_normal(void)
4827{
4828    halfword hash_brace = 0;
4829    halfword counter = 0;
4830    halfword result = get_reference_token();
4831    halfword p = result;
4832    lmt_input_state.scanner_status = scanner_is_defining;
4833    lmt_input_state.warning_index = cur_cs;
4834    lmt_input_state.def_ref = result;
4835    if (tex_aux_valid_macro_preamble(&p, &counter, &hash_brace)) {
4836        halfword unbalance = 0;
4837        while (1) {
4838            tex_get_token();
4839            switch (cur_cmd) {
4840                case left_brace_cmd:
4841                    if (! cur_cs) {
4842                        ++unbalance;
4843                    }
4844                    break;
4845                case right_brace_cmd:
4846                    if (! cur_cs) {
4847                        if (unbalance) {
4848                            --unbalance;
4849                        } else {
4850                            goto FINALYDONE;
4851                        }
4852                    }
4853                    break;
4854                case parameter_cmd:
4855                    {
4856                        halfword s = cur_tok;
4857                        tex_get_token();
4858                        if (cur_cmd == parameter_cmd) {
4859                            /*tex Keep the |#|. */
4860                        } else {
4861                            halfword n;
4862                            if (cur_tok >= one_token && cur_tok <= nine_token) {
4863                                n = cur_chr - '0';
4864                            } else if (cur_tok >= A_token_l && cur_tok <= F_token_l) {
4865                                n = cur_chr - '0' - gap_match_count;
4866                            } else {
4867                                n = counter + 1;
4868                            }
4869                            if (n <= counter) {
4870                                cur_tok = token_val(parameter_reference_cmd, n);
4871                            } else {
4872                                halfword v = tex_parameter_escape_mode() ? valid_parameter_reference(cur_tok) : 0;
4873                                if (v) {
4874                                    p = tex_store_new_token(p, token_val(parameter_cmd, match_visualizer));
4875                                } else {
4876                                    tex_aux_illegal_parameter_in_body_error();
4877                                    cur_tok = s;
4878                                }
4879                            }
4880                        }
4881                    }
4882                    break;
4883                case prefix_cmd:
4884                    if (cur_chr == enforced_code && (! overload_mode_par || lmt_main_state.run_state != production_state)) { /* todo cur_tok == let_aliased_token */
4885                        cur_tok = token_val(prefix_cmd, always_code);
4886                    }
4887                    break;
4888                default:
4889                    break;
4890            }
4891            p = tex_store_new_token(p, cur_tok);
4892        }
4893    }
4894  FINALYDONE:
4895    lmt_input_state.scanner_status = scanner_is_normal;
4896    if (hash_brace) {
4897        p = tex_store_new_token(p, hash_brace);
4898    }
4899    return result;
4900}
4901
4902halfword tex_scan_macro_expand(void)
4903{
4904    halfword hash_brace = 0;
4905    halfword counter = 0;
4906    halfword result = get_reference_token();
4907    halfword p = result;
4908    lmt_input_state.scanner_status = scanner_is_defining;
4909    lmt_input_state.warning_index = cur_cs;
4910    lmt_input_state.def_ref = result;
4911    if (tex_aux_valid_macro_preamble(&p, &counter, &hash_brace)) {
4912        halfword unbalance = 0;
4913        while (1) {
4914          PICKUP:
4915            tex_get_next();
4916            switch (cur_cmd) {
4917                case call_cmd:
4918                case tolerant_call_cmd:
4919                    tex_expand_current_token();
4920                    goto PICKUP;
4921                case index_cmd:
4922                    tex_inject_parameter(cur_chr);
4923                    goto PICKUP;
4924                case constant_call_cmd:
4925                    {
4926                        halfword h = token_link(cur_chr);
4927                        while (h) {
4928                            p = tex_store_new_token(p, token_info(h));
4929                            h = token_link(h);
4930                        }
4931                        goto PICKUP;
4932                    }
4933                case protected_call_cmd:
4934                case semi_protected_call_cmd:
4935                case tolerant_protected_call_cmd:
4936                case tolerant_semi_protected_call_cmd:
4937                    cur_tok = cs_token_flag + cur_cs;
4938                    goto APPENDTOKEN;
4939                case the_cmd:
4940                    {
4941                        halfword t = null;
4942                        halfword h = tex_the_toks(cur_chr, &t);
4943                        if (h) {
4944                            set_token_link(p, h);
4945                            p = t;
4946                        }
4947                        goto PICKUP;
4948                    }
4949                case relax_cmd:
4950                    if (cur_chr == no_relax_code) {
4951                        /*tex Think of |\ifdim\dimen0=\dimen2\norelax| inside an |\edef|. */
4952                        goto PICKUP;
4953                    } else {
4954                        goto DONEEXPANDING;
4955                    }
4956                case prefix_cmd:
4957                    if (cur_chr == enforced_code && (! overload_mode_par || lmt_main_state.run_state != production_state)) {
4958                        cur_tok = token_val(prefix_cmd, always_code);
4959                        goto APPENDTOKEN;
4960                    } else {
4961                        goto DONEEXPANDING;
4962                    }
4963                case parameter_cmd:
4964                    {
4965                        /* move into switch ... */
4966                        halfword s = cur_tok;
4967                        tex_get_x_token();
4968                        if (cur_cmd == parameter_cmd) {
4969                            /*tex Keep the |#|. */
4970                        } else {
4971                            halfword n;
4972                            if (cur_tok >= one_token && cur_tok <= nine_token) {
4973                                n = cur_chr - '0';
4974                            } else if (cur_tok >= A_token_l && cur_tok <= F_token_l) {
4975                                n = cur_chr - '0' - gap_match_count;
4976                            } else {
4977                                n = counter + 1;
4978                            }
4979                            if (n <= counter) {
4980                                cur_tok = token_val(parameter_reference_cmd, n);
4981                            } else {
4982                                halfword v = tex_parameter_escape_mode() ? valid_parameter_reference(cur_tok) : 0;
4983                                if (v) {
4984                                    halfword t = null;
4985                                    halfword h = tex_expand_parameter(cur_tok, &t);
4986                                    if (h) {
4987                                        set_token_link(p, h);
4988                                        p = t;
4989                                    }
4990                                    goto PICKUP;
4991                                } else {
4992                                    tex_aux_illegal_parameter_in_body_error();
4993                                    cur_tok = s;
4994                                }
4995                            }
4996                        }
4997                        goto APPENDTOKEN;
4998                    }
4999                case left_brace_cmd:
5000                    if (cur_cs) {
5001                        cur_tok = cs_token_flag + cur_cs;
5002                    } else {
5003                        cur_tok = token_val(cur_cmd, cur_chr);
5004                        ++unbalance;
5005                    }
5006                    goto APPENDTOKEN;
5007                case right_brace_cmd:
5008                    if (cur_cs) {
5009                        cur_tok = cs_token_flag + cur_cs;
5010                        goto APPENDTOKEN;
5011                    } else {
5012                        cur_tok = token_val(cur_cmd, cur_chr);
5013                        if (unbalance) {
5014                            --unbalance;
5015                            goto APPENDTOKEN;
5016                        } else {
5017                            goto FINALYDONE;
5018                        }
5019                    }
5020                default:
5021                    if (cur_cmd > max_command_cmd) {
5022                        tex_expand_current_token();
5023                        goto PICKUP;
5024                    } else {
5025                        goto DONEEXPANDING;
5026                    }
5027            }
5028          DONEEXPANDING:
5029            if (cur_cs) {
5030                cur_tok = cs_token_flag + cur_cs;
5031            } else {
5032                cur_tok = token_val(cur_cmd, cur_chr);
5033            }
5034          APPENDTOKEN:
5035            p = tex_store_new_token(p, cur_tok);
5036        }
5037    }
5038  FINALYDONE:
5039    lmt_input_state.scanner_status = scanner_is_normal;
5040    if (hash_brace) {
5041        p = tex_store_new_token(p, hash_brace);
5042    }
5043    return result;
5044}
5045
5046/*tex
5047
5048    The |scan_expr| procedure scans and evaluates an expression. Evaluating an expression is a
5049    recursive process: When the left parenthesis of a subexpression is scanned we descend to the
5050    next level of recursion; the previous level is resumed with the matching right parenthesis.
5051
5052    The states below are used for |\dimexpr|, |\numexpr| etc. as well as |\dimexpression| and 
5053    |\numexpression|. At some point I might merge them with the later defines states |bit_*| where
5054    we also havea priority vector. 
5055
5056    Maybe we can also add |abs n| but that depends on my needs. 
5057
5058*/
5059
5060typedef enum expression_states {
5061    expression_none,     /*tex |(| or |(expr)| */
5062    expression_not,      /*tex ! ~  not */
5063    expression_add,      /*tex |+| */
5064    expression_subtract, /*tex |-| */
5065    expression_multiply, /*tex |*| */
5066    expression_divide,   /*tex |/| */
5067    expression_scale,    /*tex |* factor| */
5068    expression_idivide,  /*tex |:|, is like |/| but floored */
5069    expression_imodulo,  /*tex |;| */
5070    expression_positive, /*tex |pm| */
5071    expression_negative, /*tex |mp| */
5072
5073    expression_bor,       /* | */
5074    expression_band,      /* & */
5075    expression_bxor,      /* ^ */
5076    expression_bnot,      /* ~ */
5077
5078    expression_bset,      /* bset   */
5079    expression_bunset,    /* bunset */
5080
5081    expression_bleft,     /* << */
5082    expression_bright,    /* >> */
5083
5084    expression_less,      /* <      */
5085    expression_lessequal, /* <=     */
5086    expression_equal,     /* =   == */
5087    expression_moreequal, /* >=     */
5088    expression_more,      /* >      */
5089    expression_unequal,   /* <>  != */
5090
5091//  expression_div,       /*  :       */ /* // */
5092//  expression_mod,       /*  %   mod */
5093
5094    expression_or,        /* or  */
5095    expression_and,       /* and */
5096
5097    expression_cor,       /* cor  */
5098    expression_cand,      /* cand */
5099} expression_states;
5100
5101/*tex
5102
5103    We want to make sure that each term and (intermediate) result is in the proper range. Integer
5104    values must not exceed |infinity| ($2^{31} - 1$) in absolute value, dimensions must not exceed
5105    |max_dimension| ($2^{30} - 1$). We avoid the absolute value of an integer, because this might fail
5106    for the value $-2^{31}$ using 32-bit arithmetic.
5107
5108    Todo: maybe use |long long| here.
5109
5110*/
5111
5112static inline void tex_aux_normalize_glue(halfword g)
5113{
5114    if (! glue_stretch(g)) {
5115        glue_stretch_order(g) = normal_glue_order;
5116    }
5117    if (! glue_shrink(g)) {
5118        glue_shrink_order(g) = normal_glue_order;
5119    }
5120}
5121
5122/*tex
5123
5124    Parenthesized subexpressions can be inside expressions, and this nesting has a stack. Seven
5125    local variables represent the top of the expression stack: |p| points to pushed-down entries,
5126    if any; |l| specifies the type of expression currently beeing evaluated; |e| is the expression
5127    so far and |r| is the state of its evaluation; |t| is the term so far and |s| is the state of
5128    its evaluation; finally |n| is the numerator for a combined multiplication and division, if any.
5129
5130    The function |add_or_sub (x, y, max_answer, negative)| computes the sum (for |negative = false|)
5131    or difference (for |negative = true|) of |x| and |y|, provided the absolute value of the result
5132    does not exceed |max_answer|.
5133
5134*/
5135
5136static inline int tex_aux_add_or_sub(int x, int y, int max_answer, int operation)
5137{
5138    switch (operation) {
5139        case expression_subtract:
5140            y = -y;
5141            // fall-trough
5142        case expression_add:
5143            if (x >= 0) {
5144                if (y <= max_answer - x) {
5145                    return x + y;
5146                } else {
5147                    lmt_scanner_state.arithmic_error = 1;
5148                }
5149            } else if (y >= -max_answer - x) {
5150                return x + y;
5151            } else {
5152                lmt_scanner_state.arithmic_error = 1;
5153            }
5154            break;
5155    }
5156    return 0;
5157}
5158
5159/*tex
5160
5161    The function |quotient (n, d)| computes the rounded quotient $q = \lfloor n / d + {1 \over 2}
5162    \rfloor$, when $n$ and $d$ are positive.
5163
5164*/
5165
5166// static inline int tex_aux_quotient(int n, int d, int round)
5167// {
5168//     /*tex The answer: */
5169//     if (d == 0) {
5170//         lmt_scanner_state.arithmic_error = 1;
5171//         return 0;
5172//     } else {
5173//         /*tex Should the answer be negated? */
5174//         bool negative;
5175//         int a;
5176//         if (d > 0) {
5177//             negative = false;
5178//         } else {
5179//             d = -d;
5180//             negative = true;
5181//         }
5182//         if (n < 0) {
5183//             n = -n;
5184//             negative = ! negative;
5185//         }
5186//         a = n / d;
5187//         if (round) {
5188//             n = n - a * d;
5189//             /*tex Avoid certain compiler optimizations! Really? */
5190//             d = n - d;
5191//             if (d + n >= 0) {
5192//                 ++a;
5193//             }
5194//         }
5195//         if (negative) {
5196//             a = -a;
5197//         }
5198//         return a;
5199//     }
5200// }
5201
5202static inline int tex_aux_quotient(int n, int d, int rounded)
5203{
5204    if (d == 0) {
5205        lmt_scanner_state.arithmic_error = 1;
5206        return 0;
5207    } else if (rounded) {
5208        return lround((double) n / (double) d);
5209    } else {
5210        return n / d;
5211    }
5212}
5213
5214static inline int tex_aux_modulo(int n, int d)
5215{
5216    if (d == 0) {
5217        lmt_scanner_state.arithmic_error = 1;
5218        return 0;
5219    } else {
5220        return n % d;
5221    }
5222}
5223
5224int tex_quotient(int n, int d, int round)
5225{
5226    return tex_aux_quotient(n, d, round);
5227}
5228
5229/*tex
5230
5231    Finally, the function |fract (x, n, d, max_answer)| computes the integer $q = \lfloor x n / d
5232    + {1 \over 2} \rfloor$, when $x$, $n$, and $d$ are positive and the result does not exceed
5233    |max_answer|. We can't use floating point arithmetic since the routine must produce identical
5234    results in all cases; and it would be too dangerous to multiply by~|n| and then divide by~|d|,
5235    in separate operations, since overflow might well occur. Hence this subroutine simulates double
5236    precision arithmetic, somewhat analogous to Metafont's |make_fraction| and |take_fraction|
5237    routines.
5238
5239*/
5240
5241int tex_fract(int x, int n, int d, int max_answer)
5242{
5243    /*tex should the answer be negated? */
5244    bool negative = false;
5245    /*tex the answer */
5246    int a = 0;
5247    /*tex a proper fraction */
5248    int f;
5249    /*tex smallest integer such that |2 * h >= d| */
5250    int h;
5251    /*tex intermediate remainder */
5252    int r;
5253    /*tex temp variable */
5254    int t;
5255    if (d == 0) {
5256        goto TOO_BIG;
5257    }
5258    if (x == 0) {
5259        return 0;
5260    }
5261    if (n == d) {
5262        return x;
5263    }
5264    if (d < 0) {
5265        d = -d;
5266        negative = true;
5267    }
5268    if (x < 0) {
5269        x = -x;
5270        negative = ! negative;
5271    }
5272    if (n < 0) {
5273        n = -n;
5274        negative = ! negative;
5275    }
5276    t = n / d;
5277    if (t > max_answer / x) {
5278        goto TOO_BIG;
5279    }
5280    a = t * x;
5281    n = n - t * d;
5282    if (n == 0) {
5283        goto FOUND;
5284    }
5285    t = x / d;
5286    if (t > (max_answer - a) / n) {
5287        goto TOO_BIG;
5288    }
5289    a = a + t * n;
5290    x = x - t * d;
5291    if (x == 0) {
5292        goto FOUND;
5293    }
5294    if (x < n) {
5295        t = x;
5296        x = n;
5297        n = t;
5298    }
5299    /*tex
5300
5301        Now |0 < n <= x < d| and we compute $f = \lfloor xn/d+{1\over2}\rfloor$. The loop here
5302        preserves the following invariant relations between |f|, |x|, |n|, and~|r|: (i)~$f + \lfloor
5303        (xn + (r + d))/d\rfloor = \lfloor x_0 n_0/d + {1\over2} \rfloor$; (ii)~|-d <= r < 0 < n <= x
5304        < d|, where $x_0$, $n_0$ are the original values of~$x$ and $n$.
5305
5306        Notice that the computation specifies |(x - d) + x| instead of |(x + x) - d|, because the
5307        latter could overflow.
5308
5309    */
5310    f = 0;
5311    r = (d / 2) - d;
5312    h = -r;
5313    while (1) {
5314        if (odd(n)) {
5315            r = r + x;
5316            if (r >= 0) {
5317                r = r - d;
5318                ++f;
5319            }
5320        }
5321        n = n / 2;
5322        if (n == 0) {
5323            break;
5324        } else if (x < h) {
5325            x = x + x;
5326        } else {
5327            t = x - d;
5328            x = t + x;
5329            f = f + n;
5330            if (x < n) {
5331                if (x == 0) {
5332                    break;
5333                } else {
5334                    t = x;
5335                    x = n;
5336                    n = t;
5337                }
5338            }
5339        }
5340    }
5341    if (f > (max_answer - a)) {
5342        goto TOO_BIG;
5343    }
5344    a = a + f;
5345  FOUND:
5346    if (negative) {
5347        a = -a;
5348    }
5349    goto DONE;
5350  TOO_BIG:
5351    lmt_scanner_state.arithmic_error = 1;
5352    a = 0;
5353  DONE:
5354    return a;
5355}
5356
5357/*tex
5358
5359    The main stacking logic approach is kept but I get the impression that the code is still
5360    suboptimal. We also accept braced expressions.
5361
5362*/
5363
5364# define max_expression_depth     1000 
5365# define max_sub_expression_depth 1000 
5366
5367static void tex_aux_scan_missing_error(void)
5368{
5369    tex_handle_error(
5370        back_error_type,
5371        "Missing ) inserted for expression",
5372        "I was expecting to see an operator or ')' but didn't."
5373    );
5374}
5375
5376static void tex_aux_scan_unexpected_relax_error(void)
5377{
5378    tex_handle_error(
5379        back_error_type,
5380        "Unexpected \\relax in expression",
5381        "I was expecting to see an operator or '}' but didn't."
5382    );
5383}
5384
5385static void tex_aux_scan_unexpected_whatever_error(void)
5386{
5387    tex_handle_error(
5388        back_error_type,
5389        "Unexpected token in expression",
5390        "I was expecting to see an operator or '}' but didn't."
5391    );
5392}
5393
5394static void tex_aux_scan_brace_error(void)
5395{
5396    tex_handle_error(
5397        back_error_type,
5398        "I ran into a curly brace mismatch",
5399        "You seem to have a mixup of () and {} or are missing at least one of them."
5400    );
5401}
5402
5403static void tex_aux_scan_parent_error(void)
5404{
5405    tex_handle_error(
5406        back_error_type,
5407        "I ran into a parenthesis mismatch",
5408        "You seem to have a mixup of () and {} or are missing at least one of them."
5409    );
5410}
5411
5412static void tex_aux_scan_zero_divide_error(void)
5413{
5414    tex_handle_error(
5415        back_error_type,
5416        "I can't divide by zero",
5417        "I was expecting to see a nonzero number. Didn't."
5418    );
5419}
5420
5421static void tex_aux_scan_expr(halfword level, int braced)
5422{
5423    /*tex state of expression so far */
5424    int result;
5425    /*tex state of term so far */
5426    int state;
5427    /*tex next operation or type of next factor */
5428    int operation;
5429    /*tex expression so far */
5430    int expression;
5431    /*tex term so far */
5432    int term;
5433    /*tex current factor */
5434    int factor = 0;
5435    /*tex numerator of combined multiplication and division */
5436    int numerator;
5437    /*tex saved values of |arith_error| */
5438    int error_a = lmt_scanner_state.arithmic_error;
5439    int error_b = 0;
5440    /*tex top of expression stack */
5441    halfword top = null;
5442    int nonelevel = level == posit_val_level ? posit_val_level : integer_val_level;
5443    /*tex Scan and evaluate an expression |e| of type |l|. */
5444    cur_val_level = level; /* for now */
5445    lmt_scanner_state.expression_depth++;
5446    if (lmt_scanner_state.expression_depth > max_expression_depth) {
5447        tex_fatal_error("\\*expr can only be nested " LMT_TOSTRING(max_expression_depth) " deep");
5448    }
5449  RESTART:
5450    result = expression_none;
5451    state = expression_none;
5452    expression = 0;
5453    term = 0;
5454    numerator = 0;
5455  CONTINUE:
5456    operation = state == expression_none ? level : nonelevel; 
5457    /*tex
5458        Scan a factor |f| of type |o| or start a subexpression. Get the next non-blank non-call
5459        token. The glue scanner is tricky because it grabs the amount and then later on scans the 
5460        other components. 
5461    */
5462    while (1) {
5463        tex_get_x_token();
5464        if (cur_cmd == spacer_cmd) {
5465            continue;
5466        } else if (cur_tok == left_parent_token) { 
5467            /*tex Push the expression stack and |goto restart|. */
5468            halfword t = tex_get_node(expression_node_size);
5469            node_type(t) = expression_node;
5470            node_subtype(t) = 0;
5471            /* */
5472            node_next(t) = top;
5473            expression_type(t) = (singleword) level;
5474            expression_state(t) = (singleword) state;
5475            expression_result(t) = (singleword) result;
5476            expression_expression(t) = expression;
5477            expression_term(t) = term;
5478            expression_numerator(t) = numerator;
5479            top = t;
5480            level = operation;
5481            goto RESTART;
5482        } else { 
5483            tex_back_input(cur_tok);
5484            break;
5485        }
5486    }
5487    /*tex
5488        In these expressions we use the integer scanner which itself accepts 
5489        |{expressions}| so |-{...}| actally works here. Hoewver, as regular expr 
5490        we don't handle a |-(...)| unless we add that to the integer scanner. 
5491
5492    */
5493    switch (operation) {
5494        case integer_val_level:
5495        case attribute_val_level:
5496            factor = tex_scan_integer(0, NULL, NULL);
5497            break;
5498        case posit_val_level:
5499            factor = tex_scan_posit(0);
5500            break;
5501        case dimension_val_level:
5502            factor = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
5503            break;
5504        case glue_val_level:
5505            factor = tex_scan_glue(glue_val_level, 0, 0);
5506            break;
5507        case muglue_val_level:
5508            factor = tex_scan_glue(muglue_val_level, 0, 0);
5509            break;
5510    }
5511  FOUND:
5512    /*tex
5513        Scan the next operator and set |o| and get the next non-blank non-call token. We 
5514        can also have a |\relax|, |)| or |}| here ending a expression. 
5515    */
5516    do {
5517        tex_get_x_token();
5518    } while (cur_cmd == spacer_cmd);
5519    switch (cur_tok) {
5520        case plus_token:
5521            operation = expression_add;
5522            break;
5523        case minus_token:
5524            operation = expression_subtract;
5525            break;
5526        case asterisk_token:
5527            operation = expression_multiply;
5528            break;
5529        case slash_token:
5530            operation = expression_divide;
5531            break;
5532        case colon_token:
5533            if (etex_expr_mode_par) {
5534                goto MAKESOMEFOLKHAPPY;
5535            } else {
5536                operation = expression_idivide;
5537            }
5538            break;
5539        case semi_colon_token:
5540            if (etex_expr_mode_par) {
5541                goto MAKESOMEFOLKHAPPY;
5542            } else {
5543                operation = expression_imodulo;
5544            }
5545            break;
5546        /*tex
5547            The commented bitwise experiment as of 2020-07-20 has been removed and is now in
5548            |\scanbitexpr|. You can find it in the archive.
5549        */
5550        default:
5551          MAKESOMEFOLKHAPPY:
5552            operation = expression_none;
5553            if (! top) {
5554                if (cur_cmd == relax_cmd) {
5555                    /* we're done */
5556                } else if (braced == 1 && cur_cmd == right_brace_cmd) {
5557                    /* we're done */
5558                } else {
5559                    tex_back_input(cur_tok);
5560                }
5561            } else if (braced == 1 && cur_cmd == right_brace_cmd) {
5562                /* we're done */
5563            } else if (cur_tok == right_parent_token) {
5564                /* we're done */
5565            } else { 
5566                tex_aux_scan_missing_error();
5567            }
5568            break;
5569    }
5570    lmt_scanner_state.arithmic_error = error_b;
5571    /*tex Make sure that |f| is in the proper range. */
5572    switch (level) {
5573        case integer_val_level:
5574        case attribute_val_level:
5575            if ((factor > max_integer) || (factor < min_integer)) {
5576                lmt_scanner_state.arithmic_error = 1;
5577                factor = 0;
5578            }
5579            break;
5580        case posit_val_level:
5581            if ((factor > max_integer) || (factor < min_integer)) {
5582                lmt_scanner_state.arithmic_error = 1;
5583                factor = 0;
5584            }
5585            break;
5586        case dimension_val_level:
5587            if (abs(factor) > max_dimension) {
5588                lmt_scanner_state.arithmic_error = 1;
5589                factor = 0;
5590            }
5591            break;
5592        case glue_val_level:
5593        case muglue_val_level:
5594            if ((abs(glue_amount(factor)) > max_dimension) || (abs(glue_stretch(factor)) > max_dimension) || (abs(glue_shrink(factor)) > max_dimension)) {
5595                lmt_scanner_state.arithmic_error = 1;
5596                tex_reset_glue_to_zero(factor);
5597            }
5598            break;
5599        default:
5600            if ((state > expression_subtract) && ((factor > max_integer) || (factor < min_integer))) {
5601                lmt_scanner_state.arithmic_error = 1;
5602                factor = 0;
5603            }
5604    }
5605    /*tex Cases for evaluation of the current term. */
5606    switch (state) {
5607        case expression_none:
5608            /*tex
5609                Applying the factor |f| to the partial term |t| (with the operator |s|) is delayed
5610                until the next operator |o| has been scanned. Here we handle the first factor of a
5611                partial term. A glue spec has to be copied unless the next operator is a right
5612                parenthesis; this allows us later on to simply modify the glue components.
5613            */
5614            term = factor;
5615            if ((level >= glue_val_level) && (operation != expression_none)) {
5616                /*tex Do we really need to copy here? */
5617                tex_aux_normalize_glue(term);
5618         // } else {
5619         //     term = factor;
5620            }
5621            break;
5622        case expression_multiply:
5623            /*tex
5624                If a multiplication is followed by a division, the two operations are combined into
5625                a 'scaling' operation. Otherwise the term |t| is multiplied by the factor |f|.
5626            */
5627            if (operation == expression_divide) {
5628                numerator = factor;
5629                operation = expression_scale;
5630            } else {
5631                switch (level) {
5632                    case integer_val_level:
5633                    case attribute_val_level:
5634                        term = tex_multiply_integers(term, factor);
5635                        break;
5636                    case posit_val_level:
5637                        term = tex_posit_mul(term, factor);
5638                        break;
5639                    case dimension_val_level:
5640                        term = tex_nx_plus_y(term, factor, 0);
5641                        break;
5642                    default:
5643                        glue_amount(term) = tex_nx_plus_y(glue_amount(term), factor, 0);
5644                        glue_stretch(term) = tex_nx_plus_y(glue_stretch(term), factor, 0);
5645                        glue_shrink(term) = tex_nx_plus_y(glue_shrink(term),  factor, 0);
5646                        break;
5647                }
5648            }
5649            break;
5650        case expression_divide:
5651            /*tex Here we divide the term |t| by the factor |f|. */
5652            switch (level) {
5653                case integer_val_level:
5654                case attribute_val_level:
5655                case dimension_val_level:
5656                    term = tex_aux_quotient(term, factor, 1);
5657                    break;
5658                case posit_val_level:
5659                    if (factor == 0) {
5660                        lmt_scanner_state.arithmic_error = 1;
5661                        term = 0;
5662                    } else {
5663                        term = tex_posit_div(term, factor);
5664                    }
5665                    break;
5666                default:
5667                    glue_amount(term) = tex_aux_quotient(glue_amount(term), factor, 1);
5668                    glue_stretch(term) = tex_aux_quotient(glue_stretch(term), factor, 1);
5669                    glue_shrink(term) = tex_aux_quotient(glue_shrink(term), factor, 1);
5670                    break;
5671            }
5672            break;
5673        case expression_scale:
5674            /*tex Here the term |t| is multiplied by the quotient $n/f$. */
5675            switch (level) {
5676                case integer_val_level:
5677                case attribute_val_level:
5678                    term = tex_fract(term, numerator, factor, max_integer);
5679                    break;
5680                case posit_val_level:
5681                    if (numerator == 0) {
5682                        lmt_scanner_state.arithmic_error = 1;
5683                        term = 0;
5684                    } else {
5685                        term = tex_posit_div(tex_posit_mul(term, factor), numerator);
5686                    }
5687                    break;
5688                case dimension_val_level:
5689                    term = tex_fract(term, numerator, factor, max_dimension);
5690                    break;
5691                default:
5692                    glue_amount(term) = tex_fract(glue_amount(term),   numerator, factor, max_dimension);
5693                    glue_stretch(term) = tex_fract(glue_stretch(term), numerator, factor, max_dimension);
5694                    glue_shrink(term) = tex_fract(glue_shrink(term),  numerator, factor, max_dimension);
5695                    break;
5696            }
5697            break;
5698        case expression_idivide:
5699            /*tex Here we divide the term |t| by the factor |f| but we don't round. */
5700            if (level < glue_val_level) {
5701                term = tex_aux_quotient(term, factor, 0);
5702            } else {
5703                glue_amount(term) = tex_aux_quotient(glue_amount(term),   factor, 0);
5704                glue_stretch(term) = tex_aux_quotient(glue_stretch(term), factor, 0);
5705                glue_shrink(term) = tex_aux_quotient(glue_shrink(term),  factor, 0);
5706            }
5707            break;
5708        case expression_imodulo:
5709            /* \the\numexpr#2-(#2:#1)*#1\relax */
5710            if (level < glue_val_level) {
5711                term = tex_aux_modulo(term, factor);
5712            } else {
5713                glue_amount(term) = tex_aux_modulo(glue_amount(term),   factor);
5714                glue_stretch(term) = tex_aux_modulo(glue_stretch(term), factor);
5715                glue_shrink(term) = tex_aux_modulo(glue_shrink(term),  factor);
5716            }
5717            break;
5718    }
5719    if (operation > expression_subtract) {
5720        state = operation;
5721    } else {
5722        /*tex
5723            Evaluate the current expression. When a term |t| has been completed it is copied to,
5724            added to, or subtracted from the expression |e|.
5725        */
5726        state = expression_none;
5727        if (result == expression_none) {
5728            expression = term;
5729        } else {
5730            switch (level) {
5731                case integer_val_level:
5732                case attribute_val_level:
5733                    expression = tex_aux_add_or_sub(expression, term, max_integer, result);
5734                    break;
5735                case posit_val_level:
5736                    switch (result) {
5737                        case expression_subtract:
5738                            expression = tex_posit_sub(expression, term);
5739                            break;
5740                        case expression_add:
5741                            expression = tex_posit_add(expression, term);
5742                            break;
5743                    }
5744                    break;
5745                case dimension_val_level:
5746                    expression = tex_aux_add_or_sub(expression, term, max_dimension, result);
5747                    break;
5748                default :
5749                    /*tex
5750                        Compute the sum or difference of two glue specs. We know that |stretch_order
5751                        (e) > normal| implies |stretch (e) <> 0| and |shrink_order (e)  > normal|
5752                        implies |shrink (e) <> 0|.
5753                    */
5754                    glue_amount(expression) = tex_aux_add_or_sub(glue_amount(expression), glue_amount(term), max_dimension, result);
5755                    if (glue_stretch_order(expression) == glue_stretch_order(term)) {
5756                        glue_stretch(expression) = tex_aux_add_or_sub(glue_stretch(expression), glue_stretch(term), max_dimension, result);
5757                    } else if ((glue_stretch_order(expression) < glue_stretch_order(term)) && (glue_stretch(term) != 0)) {
5758                        glue_stretch(expression) = glue_stretch(term);
5759                        glue_stretch_order(expression) = glue_stretch_order(term);
5760                    }
5761                    if (glue_shrink_order(expression) == glue_shrink_order(term)) {
5762                        glue_shrink(expression) = tex_aux_add_or_sub(glue_shrink(expression), glue_shrink(term), max_dimension, result);
5763                    } else if ((glue_shrink_order(expression) < glue_shrink_order(term)) && (glue_shrink(term) != 0)) {
5764                        glue_shrink(expression) = glue_shrink(term);
5765                        glue_shrink_order(expression) = glue_shrink_order(term);
5766                    }
5767                    tex_flush_node(term);
5768                    tex_aux_normalize_glue(expression);
5769                    break;
5770            }
5771        }
5772        result = operation;
5773    }
5774    error_b = lmt_scanner_state.arithmic_error;
5775    if (operation != expression_none) {
5776        goto CONTINUE;
5777    } else if (top) {
5778        /*tex Pop the expression stack and |goto found|. */
5779        halfword newtop = node_next(top);
5780        factor = expression;
5781        expression = expression_expression(top);
5782        term = expression_term(top);
5783        numerator = expression_numerator(top);
5784        state = (singleword) expression_state(top);
5785        result = (singleword) expression_result(top);
5786        level = (singleword) expression_type(top);
5787        tex_free_node(top, expression_node_size);
5788        top = newtop;
5789        goto FOUND;
5790    } else if (error_b) {
5791        switch (level) {
5792            case posit_val_level:
5793            case integer_val_level:
5794            case attribute_val_level:
5795                tex_aux_scan_integer_out_of_range_error(4);
5796                expression = 0;
5797                break;
5798            case glue_val_level:
5799            case muglue_val_level:
5800                tex_reset_glue_to_zero(expression);
5801            case dimension_val_level:
5802                tex_aux_scan_dimension_out_of_range_error(2);
5803                break;
5804        }
5805    }
5806    lmt_scanner_state.arithmic_error = error_a;
5807    lmt_scanner_state.expression_depth--;
5808    cur_val_level = level;
5809    cur_val = expression;
5810}
5811
5812halfword tex_scan_expr(halfword level) /* maybe avoid indirectness */
5813{
5814    tex_aux_scan_expr(level, 0);
5815    return cur_val;
5816}
5817
5818static halfword tex_aux_scan_unit_applied(halfword value, halfword fraction, int *has_unit, int has_fraction) 
5819{
5820    halfword num = 0;
5821    halfword denom = 0;
5822    scaled unit = 0;
5823    *has_unit = 1;
5824    switch (tex_aux_scan_unit(&num, &denom, &unit, NULL)) {
5825        case normal_unit_scanned:
5826            if (num) {
5827                int remainder = 0;
5828                value = tex_xn_over_d_r(value, num, denom, &remainder);
5829                fraction = (num * fraction + unity * remainder) / denom;
5830                value += fraction / unity;
5831                fraction = fraction % unity;
5832            }
5833            *has_unit = 1;
5834            if (value >= 040000) { // 0x4000
5835                lmt_scanner_state.arithmic_error = 1;
5836                value = 040000; 
5837            }
5838            return value * unity + fraction;
5839        case scaled_point_scanned:
5840            *has_unit = 0;
5841            return value;
5842        case relative_unit_scanned:
5843            return tex_nx_plus_y(value, unit, tex_xn_over_d(unit, fraction, unity));
5844        case quantitity_unit_scanned:
5845            cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, dimension_val_level, 0, 0);
5846            value = tex_nx_plus_y(value, cur_val, tex_xn_over_d(cur_val, fraction, unity));
5847            return value;
5848        case no_unit_scanned:      
5849            if (has_fraction) {
5850                *has_unit = 0;
5851                return value * unity + fraction;
5852            } else { 
5853               /* what */
5854            }
5855        default: 
5856            break;
5857     /* case math_unit_scanned:     */ /* ignored */
5858     /* case flexible_unit_scanned: */ /* ignored */
5859    }
5860    *has_unit = 0;
5861    return value;
5862}
5863
5864static halfword tex_scan_bit_integer(int *radix)
5865{
5866    bool negative = false;
5867    long long result = 0;
5868    while (1) {
5869        if (cur_tok == minus_token) {
5870            negative = ! negative;
5871        } else if (cur_tok == plus_token) {
5872            /* ignore */
5873        } else if (cur_cmd == spacer_cmd) { 
5874            /* ignore */
5875        } else { 
5876            break; 
5877        }
5878        tex_get_token();
5879    }
5880    if (cur_tok == alpha_token) {
5881        tex_get_token();
5882        if (cur_tok < cs_token_flag) {
5883            result = cur_chr;
5884        } else {
5885            strnumber txt = cs_text(cur_tok - cs_token_flag);
5886            if (tex_single_letter(txt)) {
5887                result = aux_str2uni(str_string(txt));
5888            } else if (tex_is_active_cs(txt)) {
5889                result = active_cs_value(txt);
5890            } else {
5891                result = max_character_code + 1;
5892            }
5893        }
5894        if (result > max_character_code) {
5895            tex_aux_improper_constant_error();
5896            return 0;
5897        }
5898    } else if ((cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) || cur_cmd == parameter_cmd) {
5899        result = tex_aux_scan_something_internal(cur_cmd, cur_chr, integer_val_level, 0, 0);
5900        if (cur_val_level != integer_val_level) {
5901            tex_aux_missing_number_error(2);
5902            return 0;
5903        }
5904    } else {
5905        bool vacuous = true;
5906        bool ok_so_far = true;
5907        switch (cur_tok) {
5908            case octal_token:
5909                {
5910                    if (radix) {
5911                        *radix = 8;
5912                    }
5913                    while (1) {
5914                        unsigned d = 0;
5915                        tex_get_x_token();
5916                        if ((cur_tok >= zero_token) && (cur_tok <= seven_token)) {
5917                            d = cur_tok - zero_token;
5918                        } else {
5919                            goto DONE;
5920                        }
5921                        vacuous = false;
5922                        if (ok_so_far) {
5923                            result = result * 8 + d;
5924                            if (result > max_integer) {
5925                                result = max_integer;
5926                                tex_aux_scan_integer_out_of_range_error(5);
5927                                ok_so_far = false;
5928                            }
5929                        }
5930                    }
5931                 // break;
5932                }
5933            case hex_token:
5934                {
5935                    if (radix) {
5936                        *radix = 16;
5937                    }
5938                    while (1) {
5939                        unsigned d = 0;
5940                        tex_get_x_token();
5941                        if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
5942                            d = cur_tok - zero_token;
5943                        } else if ((cur_tok >= A_token_l) && (cur_tok <= F_token_l)) {
5944                            d = cur_tok - A_token_l + 10;
5945                        } else if ((cur_tok >= A_token_o) && (cur_tok <= F_token_o)) {
5946                            d = cur_tok - A_token_o + 10;
5947                        } else {
5948                            goto DONE;
5949                        }
5950                        vacuous = false;
5951                        if (ok_so_far) {
5952                            result = result * 16 + d;
5953                            if (result > max_integer) {
5954                                result = max_integer;
5955                                tex_aux_scan_integer_out_of_range_error(6);
5956                                ok_so_far = false;
5957                            }
5958                        }
5959                    }
5960                 // break;
5961                }
5962            default:
5963                {
5964                    if (radix) {
5965                        *radix = 10;
5966                    }
5967                    while (1) {
5968                        unsigned d = 0;
5969                        if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
5970                            d = cur_tok - zero_token;
5971                        } else {
5972                            goto DONE;
5973                        }
5974                        vacuous = false;
5975                        if (ok_so_far) {
5976                            result = result * 10 + d;
5977                            if (result > max_integer) {
5978                                result = max_integer;
5979                                tex_aux_scan_integer_out_of_range_error(7);
5980                                ok_so_far = false;
5981                            }
5982                        }
5983                        tex_get_x_token();
5984                    }
5985                 // break;
5986                }
5987        }
5988      DONE:
5989        if (vacuous) {
5990            tex_aux_missing_number_error(3);
5991        } else {
5992            tex_push_back(cur_tok, cur_cmd, cur_chr);
5993        }
5994    }
5995    cur_val = (halfword) (negative ? - result : result);
5996    return cur_val;
5997}
5998
5999static halfword tex_scan_bit_dimension(int *has_fraction, int *has_unit)
6000{
6001    bool negative = false;
6002    int fraction = 0;
6003    *has_fraction = 0;
6004    *has_unit = 0;
6005    lmt_scanner_state.arithmic_error = 0;
6006    while (1) {
6007        if (cur_tok == minus_token) {
6008            negative = ! negative;
6009        } else if (cur_tok == plus_token) {
6010            /* ignore */
6011        } else if (cur_cmd == spacer_cmd) { 
6012            /* ignore */
6013        } else { 
6014            break; 
6015        }
6016        tex_get_token();
6017    }
6018    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
6019        cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, dimension_val_level, 0, 0);
6020        if (cur_val_level == dimension_val_level) {
6021            *has_unit = 1;
6022            goto ATTACH_SIGN;
6023        }
6024    } else if (tex_token_is_unit(cur_tok) && tex_aux_scan_unit_only(&cur_val) != no_unit_scanned) {
6025        *has_unit = 1;
6026        goto NEARLY_DONE;
6027    } else {
6028        *has_fraction = tex_token_is_separator(cur_tok);
6029        if (*has_fraction) {
6030            /*tex We started with a |.| or |,|. */
6031            cur_val = 0;
6032        } else {
6033            int cur_radix = 10;
6034            cur_val = tex_scan_bit_integer(&cur_radix);
6035            if (cur_radix == 10 && tex_token_is_separator(cur_tok)) {
6036                *has_fraction = 1;
6037                tex_get_token(); /* why not x */
6038            }
6039        }
6040        if (*has_fraction) {
6041            unsigned k = 0;
6042            unsigned char digits[18];
6043            while (1) {
6044                tex_get_x_token();
6045                if (cur_tok > nine_token || cur_tok < zero_token) {
6046                    break;
6047                } else if (k < 17) {
6048                    digits[k] = (unsigned char) (cur_tok - zero_token);
6049                    ++k;
6050                }
6051            }
6052            fraction = tex_round_decimals_digits(digits, k);
6053            if (cur_cmd != spacer_cmd) {
6054                /* we can avoid this when parsing a unit but not now */
6055                tex_back_input(cur_tok);
6056            }
6057        }
6058    }
6059    cur_val = tex_aux_scan_unit_applied(cur_val, fraction, has_unit, *has_fraction);
6060  ATTACH_SIGN:
6061    if (lmt_scanner_state.arithmic_error || (abs(cur_val) >= 010000000000)) { // 0x40000000
6062        tex_aux_scan_dimension_out_of_range_error(3);
6063        cur_val = max_dimension;
6064        lmt_scanner_state.arithmic_error = 0;
6065    }
6066  NEARLY_DONE:
6067    if (negative) {
6068        cur_val = -cur_val;
6069    }
6070    return cur_val;
6071}
6072
6073/*tex
6074
6075    Already early in \LUAMETATEX\ I wondered about adding support for boolean expressions but at
6076    that time (2019) I still wanted it as part of \type |\numexpr|. I added some code that actually
6077    worked okay, but kept it commented. After all, we don't need it that often and \CONTEXT\ has
6078    helpers for it so it's best to avoid the extra overhead in other expressions.
6079
6080    However, occasionally, when I check the manual I came back to this. I wondered about some more
6081    that just extra bitwise operators. However, prcedence makes it a bit tricky. Also, we can't use
6082    some characters because they can be letter, other, active or have special meaning in math or
6083    alignments. Then I played with verbose operators: mod (instead of a percent sign), and
6084    |and|, |or|, |band|, |bor| and |bxor| (cf the \LUA\ bit32 library).
6085
6086    In the end I decided not to integrate it but make a dedicated |\bitexpr| instead. I played with
6087    some variants but the approach in the normal expression scanned is not really suitable for it.
6088
6089    After the initial |\bitexpr| I eventually ended up with an integer and dimension scanner and
6090    it became more complex that originally intended, but the current implementation is flexible
6091    enough to extend. I can probably squeeze out some more performance.
6092
6093    Beware: details can change, for instance handling some (math) \UNICODE\ characters has been
6094    dropped because it's an inconsistent bunch and incomplete anyway.
6095
6096    In the end we have a set of dedicated scanners. We could use the existing ones but for instance
6097    units are optional here. We also have a bit more predictable sentinel, so we can optimize some
6098    push back. We don't handle mu units nor fillers. It was also kind of fun to explore that.
6099
6100*/
6101
6102int tex_scanned_expression(int level)
6103{
6104    tex_aux_scan_expression(level, 0);
6105    return cur_val;
6106}
6107
6108/*tex
6109    We used to only scale by 1000 when we had a fraction but that is kind of fuzzy so now we always
6110    assume a fraction.
6111*/
6112
6113halfword tex_scan_scale(int optional_equal)
6114{
6115    bool negative = false;
6116    lmt_scanner_state.arithmic_error = 0;
6117    do {
6118        while (1) {
6119            tex_get_x_token();
6120            if (cur_cmd != spacer_cmd) {
6121                if (optional_equal && (cur_tok == equal_token)) {
6122                    optional_equal = 0;
6123                } else {
6124                    break;
6125                }
6126            }
6127        }
6128        if (cur_tok == minus_token) {
6129            negative = ! negative;
6130            cur_tok = plus_token;
6131        }
6132    } while (cur_tok == plus_token);
6133    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
6134        cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, integer_val_level, 0, 0);
6135    } else {
6136        int has_fraction = tex_token_is_separator(cur_tok);
6137        if (has_fraction) {
6138            cur_val = 0;
6139        } else {
6140            int cur_radix;
6141            tex_back_input(cur_tok);
6142            cur_val = tex_scan_integer(0, &cur_radix, NULL);
6143            tex_get_token();
6144            if (cur_radix == 10 && tex_token_is_separator(cur_tok)) {
6145                has_fraction = 1;
6146            }
6147        }
6148        cur_val = cur_val * 1000;
6149        if (has_fraction) {
6150            unsigned k = 4;
6151            while (1) {
6152                tex_get_x_token();
6153                if (cur_tok < zero_token || cur_tok > nine_token) {
6154                    break;
6155                } else if (k == 1) {
6156                    /* rounding */
6157                    if (cur_tok >= five_token && cur_tok <= nine_token) {
6158                        cur_val += 1;
6159                    }
6160                    --k;
6161                } else if (k) {
6162                    cur_val = cur_val + (k == 4 ? 100 : (k == 3 ? 10 : 1)) * (cur_tok - zero_token);
6163                    --k;
6164                }
6165            }
6166        }
6167        tex_push_back(cur_tok, cur_cmd, cur_chr);
6168    }
6169    if (negative) {
6170        cur_val = -cur_val;
6171    }
6172    if (lmt_scanner_state.arithmic_error || (abs(cur_val) >= 0x40000000)) {
6173     // scan_dimension_out_of_range_error();
6174        cur_val = max_dimension;
6175        lmt_scanner_state.arithmic_error = 0;
6176    }
6177    return cur_val;
6178}
6179
6180/* todo: share with lmttokenlib.scan_float */
6181
6182# define max_posit_size 60
6183
6184halfword tex_scan_posit(int optional_equal)
6185{
6186    int hexadecimal = 1;
6187    int exponent = 1;
6188    bool negative = false;
6189    int b = 0;
6190    char buffer[max_posit_size+4] = { 0 };
6191    do {
6192        while (1) {
6193            tex_get_x_token();
6194            if (cur_cmd != spacer_cmd) {
6195                if (optional_equal && (cur_tok == equal_token)) {
6196                    optional_equal = 0;
6197                } else {
6198                    break;
6199                }
6200            }
6201        }
6202        if (cur_tok == minus_token) {
6203            negative = ! negative;
6204            cur_tok = plus_token;
6205        }
6206    } while (cur_tok == plus_token);
6207    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
6208        cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, posit_val_level, 0, 0);
6209    } else {
6210        if (negative) {
6211            buffer[b++] = '-';
6212        }
6213        /*tex we accept |[.,]digits| */
6214        if (hexadecimal && (cur_tok == zero_token)) {
6215            buffer[b++] = '0';
6216            tex_get_x_token();
6217            if (tex_token_is_hexadecimal(cur_tok)) {
6218                buffer[b++] = 'x';
6219                goto SCANHEXADECIMAL;
6220            } else {
6221                goto PICKUPDECIMAL;
6222            }
6223        } else {
6224            goto SCANDECIMAL;
6225        }
6226      SCANDECIMAL:
6227        if (tex_token_is_separator(cur_tok)) {
6228            buffer[b++] = '.';
6229            while (1) {
6230                tex_get_x_token();
6231                if (tex_token_is_digit(cur_tok)) {
6232                    buffer[b++] = (unsigned char) cur_chr;
6233                } else if (exponent) {
6234                    goto DECIMALEXPONENT;
6235                } else {
6236                    tex_back_input(cur_tok);
6237                    goto DONE;
6238                }
6239                if (b >= 60) {
6240                    goto TOOBIG;
6241                }
6242            }
6243        } else {
6244            goto PICKUPDECIMAL;
6245        }
6246        while (1) {
6247            tex_get_x_token();
6248          PICKUPDECIMAL:
6249            if (tex_token_is_digit(cur_tok)) {
6250                buffer[b++] = (unsigned char) cur_chr;
6251            } else if (tex_token_is_separator(cur_tok)) {
6252                buffer[b++] = '.';
6253                while (1) {
6254                    tex_get_x_token();
6255                    if (tex_token_is_digit(cur_tok)) {
6256                        buffer[b++] = (unsigned char) cur_chr;
6257                    } else {
6258                        tex_back_input(cur_tok);
6259                        break;
6260                    }
6261                }
6262            } else if (exponent) {
6263                goto DECIMALEXPONENT;
6264            } else {
6265                tex_back_input(cur_tok);
6266                goto DONE;
6267            }
6268            if (b >= max_posit_size) {
6269                goto TOOBIG;
6270            }
6271        }
6272      DECIMALEXPONENT:
6273        if (tex_token_is_exponent(cur_tok)) {
6274            buffer[b++] = (unsigned char) cur_chr;
6275            tex_get_x_token();
6276            if (tex_token_is_sign(cur_tok)) {
6277                buffer[b++] = (unsigned char) cur_chr;
6278            } else if (tex_token_is_digit(cur_tok)) {
6279                buffer[b++] = (unsigned char) cur_chr;
6280            }
6281            while (1) {
6282                tex_get_x_token();
6283                if (tex_token_is_digit(cur_tok)) {
6284                    buffer[b++] = (unsigned char) cur_chr;
6285                } else {
6286                    break;
6287                }
6288                if (b >= max_posit_size) {
6289                    goto TOOBIG;
6290                }
6291            }
6292        }
6293        tex_back_input(cur_tok);
6294        goto DONE;
6295      SCANHEXADECIMAL:
6296        tex_get_x_token();
6297        if (tex_token_is_separator(cur_tok)) {
6298            buffer[b++] = '.';
6299            while (1) {
6300                tex_get_x_token();
6301                if (tex_token_is_xdigit(cur_tok)) {
6302                    buffer[b++] = (unsigned char) cur_chr;
6303                } else if (exponent) {
6304                    goto HEXADECIMALEXPONENT;
6305                } else {
6306                    tex_back_input(cur_tok);
6307                    goto DONE;
6308                }
6309                if (b >= max_posit_size) {
6310                    goto TOOBIG;
6311                }
6312            }
6313        } else {
6314            /* hm, we could avoid this pushback */
6315            tex_back_input(cur_tok);
6316            while (1) {
6317                tex_get_x_token();
6318                if (tex_token_is_xdigit(cur_tok)) {
6319                    buffer[b++] = (unsigned char) cur_chr;
6320                } else if (tex_token_is_separator(cur_tok)) {
6321                    buffer[b++] = '.';
6322                    while (1) {
6323                        tex_get_x_token();
6324                        if (tex_token_is_xdigit(cur_tok)) {
6325                            buffer[b++] = (unsigned char) cur_chr;
6326                        } else {
6327                            tex_back_input(cur_tok);
6328                            break;
6329                        }
6330                    }
6331                } else if (exponent) {
6332                    goto HEXADECIMALEXPONENT;
6333                } else {
6334                    tex_back_input(cur_tok);
6335                    goto DONE;
6336                }
6337                if (b >= max_posit_size) {
6338                    goto TOOBIG;
6339                }
6340            }
6341        }
6342      HEXADECIMALEXPONENT:
6343        if (tex_token_is_xexponent(cur_tok)) {
6344            buffer[b++] = (unsigned char) cur_chr;
6345            tex_get_x_token();
6346            if (tex_token_is_sign(cur_tok)) {
6347                buffer[b++] = (unsigned char) cur_chr;
6348            } else if (tex_token_is_xdigit(cur_tok)) {
6349                buffer[b++] = (unsigned char) cur_chr;
6350            }
6351            while (1) {
6352                tex_get_x_token();
6353                if (tex_token_is_xdigit(cur_tok)) {
6354                    buffer[b++] = (unsigned char) cur_chr;
6355                } else {
6356                    break;
6357                }
6358                if (b >= max_posit_size) {
6359                    goto TOOBIG;
6360                }
6361            }
6362        }
6363        tex_back_input(cur_tok);
6364      DONE:
6365        if (b) {
6366            double d = strtod(buffer, NULL);
6367            cur_val = tex_double_to_posit(d).v;
6368            return cur_val;
6369        } else {
6370            tex_aux_missing_number_error(4);
6371        }
6372      TOOBIG:
6373        cur_val = tex_integer_to_posit(0).v;
6374    }
6375    return cur_val;
6376}
6377
6378int tex_scan_tex_value(halfword level, halfword *value)
6379{
6380    tex_aux_scan_expr(level, 0);
6381    *value = cur_val;
6382    return 1;
6383}
6384
6385quarterword tex_scan_direction(int optional_equal)
6386{
6387    int i = tex_scan_integer(optional_equal, NULL, NULL);
6388    return (quarterword) checked_direction_value(i);
6389}
6390
6391halfword tex_scan_geometry(int optional_equal)
6392{
6393    int i = tex_scan_integer(optional_equal, NULL, NULL);
6394    return checked_geometry_value(i);
6395}
6396
6397halfword tex_scan_orientation(int optional_equal)
6398{
6399    halfword i = tex_scan_integer(optional_equal, NULL, NULL);
6400    return checked_orientation_value(i);
6401}
6402
6403halfword tex_scan_anchor(int optional_equal)
6404{
6405    halfword a = tex_scan_integer(optional_equal, NULL, NULL);
6406    halfword l = (a >> 16) & 0xFFFF;
6407    halfword r =  a        & 0xFFFF;
6408    return (checked_anchor_value(l) << 16) + checked_anchor_value(r);
6409}
6410
6411halfword tex_scan_anchors(int optional_equal)
6412{
6413    halfword l = tex_scan_integer(optional_equal, NULL, NULL) & 0xFFFF;
6414    halfword r = tex_scan_integer(0, NULL, NULL)              & 0xFFFF;
6415    return (checked_anchor_value(l) << 16) + checked_anchor_value(r);
6416}
6417
6418halfword tex_scan_attribute(halfword attrlist)
6419{
6420    halfword i = tex_scan_attribute_register_number();
6421    halfword v = tex_scan_integer(1, NULL, NULL);
6422    if (eq_value(register_attribute_location(i)) != v) {
6423        if (attrlist) {
6424            attrlist = tex_patch_attribute_list(attrlist, i, v);
6425        } else {
6426            attrlist = tex_copy_attribute_list_set(tex_current_attribute_list(), i, v);
6427        }
6428    }
6429    return attrlist;
6430}
6431
6432halfword tex_scan_extra_attribute(halfword attrlist)
6433{
6434    halfword i = tex_scan_attribute_register_number();
6435    halfword v = tex_scan_integer(1, NULL, NULL);
6436    if (attrlist) {
6437        attrlist = tex_patch_attribute_list(attrlist, i, v);
6438    } else {
6439        attrlist = tex_copy_attribute_list_set(null, i, v);
6440    }
6441    return attrlist;
6442}
6443
6444# undef factor 
6445
6446static inline halfword tex_scan_aux_o(void)
6447{
6448    tex_get_x_token();
6449    switch (cur_tok) {
6450        case r_token_l: case r_token_o:
6451            return expression_or;
6452        default: 
6453            tex_aux_show_keyword_error("or");
6454            return expression_none;
6455    }
6456}
6457
6458static inline halfword tex_scan_aux_a(void)
6459{
6460    tex_get_x_token();
6461    switch (cur_tok) {
6462        case n_token_l: case n_token_o:
6463            tex_get_x_token();
6464            switch (cur_tok) { 
6465                case d_token_l: case d_token_o:
6466                    return expression_and;
6467                default:
6468                    goto A_ERROR;
6469            }
6470        default:
6471          A_ERROR:
6472            tex_aux_show_keyword_error("and");
6473            return expression_none;
6474    }
6475}
6476
6477static inline halfword tex_scan_aux_n(void)
6478{
6479    tex_get_x_token();
6480    switch (cur_tok) {
6481        case o_token_l: case o_token_o:
6482            tex_get_x_token();
6483            switch (cur_tok) {
6484                case t_token_l: case t_token_o:
6485                    return expression_not;
6486                default:
6487                    goto N_ERROR;
6488            }
6489        case p_token_l: case p_token_o:
6490            tex_get_x_token();
6491            switch (cur_tok) {
6492                case m_token_l: case m_token_o:
6493                    return expression_positive;
6494                default:
6495                    goto N_ERROR;
6496            }
6497        case m_token_l: case m_token_o:
6498            tex_get_x_token();
6499            switch (cur_tok) {
6500                case p_token_l: case p_token_o:
6501                    return expression_negative;
6502                default:
6503                    goto N_ERROR;
6504            }
6505        default:
6506          N_ERROR:
6507            tex_aux_show_keyword_error("not|nmp|npm");
6508            return expression_none;
6509    }
6510}
6511
6512static inline halfword tex_scan_aux_m(void)
6513{
6514    tex_get_x_token();
6515    switch (cur_tok) {
6516        case o_token_l: case o_token_o:
6517            tex_get_x_token();
6518            switch (cur_tok) { 
6519                case d_token_l: case d_token_o:
6520                    return expression_imodulo;
6521                default:
6522                    goto M_ERROR;
6523            }
6524        default:
6525          M_ERROR:
6526            tex_aux_show_keyword_error("mod");
6527            return expression_none;
6528    }
6529}
6530
6531static inline halfword tex_scan_aux_d(void)
6532{
6533    tex_get_x_token();
6534    switch (cur_tok) {
6535        case i_token_l: case i_token_o:
6536            tex_get_x_token();
6537            switch (cur_tok) { 
6538                case v_token_l: case v_token_o:
6539                    return expression_idivide;
6540                default:
6541                    goto M_ERROR;
6542            }
6543        default:
6544          M_ERROR:
6545            tex_aux_show_keyword_error("div");
6546            return expression_none;
6547    }
6548}
6549
6550static inline halfword tex_scan_aux_b(void)
6551{
6552    tex_get_x_token();
6553    switch (cur_tok) {
6554        case a_token_l: case a_token_o:
6555            tex_get_x_token();
6556            switch (cur_tok) {
6557                case n_token_l: case n_token_o:
6558                    tex_get_x_token();
6559                    switch (cur_tok) { 
6560                        case d_token_l: case d_token_o:
6561                            return expression_band;
6562                        default:
6563                            goto B_ERROR;
6564                    }
6565                default:
6566                    goto B_ERROR;
6567            }
6568            break;
6569        case o_token_l: case o_token_o:
6570            tex_get_x_token();
6571            switch (cur_tok) {
6572                case r_token_l: case r_token_o:
6573                    return expression_bor;
6574                default:
6575                    goto B_ERROR;
6576            }
6577            break;
6578        case x_token_l: case x_token_o:
6579            tex_get_x_token();
6580            switch (cur_tok) {
6581                case o_token_l: case o_token_o:
6582                    tex_get_x_token();
6583                    switch (cur_tok) { 
6584                        case r_token_l: case r_token_o:
6585                            return expression_bxor;
6586                        default:
6587                            goto B_ERROR;
6588                    }
6589                default:
6590                    goto B_ERROR;
6591            }
6592            break;
6593        case s_token_l: case s_token_o:
6594            tex_get_x_token();
6595            switch (cur_tok) { 
6596                case e_token_l: case e_token_o:
6597                    tex_get_x_token();
6598                    switch (cur_tok) { 
6599                        case t_token_l: case t_token_o:
6600                            return expression_bset;
6601                        default:
6602                            goto B_ERROR;
6603                    }
6604                default:
6605                    goto B_ERROR;
6606            }
6607            break;
6608        case u_token_l: case u_token_o:
6609            tex_get_x_token();
6610            switch (cur_tok) {
6611                case n_token_l: case n_token_o:
6612                    tex_get_x_token();
6613                    switch (cur_tok) { 
6614                        case s_token_l: case s_token_o:
6615                            tex_get_x_token();
6616                            switch (cur_tok) { 
6617                                case e_token_l: case e_token_o:
6618                                    tex_get_x_token();
6619                                    switch (cur_tok) { 
6620                                        case t_token_l: case t_token_o:
6621                                            return expression_bset;
6622                                        default:
6623                                            goto B_ERROR;
6624                                    }
6625                                default:
6626                                    goto B_ERROR;
6627                            }
6628                        default:
6629                            goto B_ERROR;
6630                    }
6631                default:
6632                    goto B_ERROR;
6633            }
6634            break;
6635        default:
6636          B_ERROR:
6637            tex_aux_show_keyword_error("band|bor|bxor|bset|bunset");
6638            return expression_none;
6639    }
6640}
6641
6642static inline halfword tex_scan_aux_c(void)
6643{
6644    tex_get_x_token();
6645    switch (cur_tok) {
6646        case a_token_l: case a_token_o:
6647            tex_get_x_token();
6648            switch (cur_tok) {
6649                case n_token_l: case n_token_o:
6650                    tex_get_x_token();
6651                    switch (cur_tok) { 
6652                        case d_token_l: case d_token_o:
6653                            return expression_cand;
6654                        default:
6655                            goto C_ERROR;
6656                    }
6657                default:
6658                    goto C_ERROR;
6659            }
6660            break;
6661        case o_token_l: case o_token_o:
6662            tex_get_x_token();
6663            switch (cur_tok) {
6664                case r_token_l: case r_token_o:
6665                    return expression_cor;
6666                default:
6667                    goto C_ERROR;
6668            }
6669        default:
6670          C_ERROR:
6671            tex_aux_show_keyword_error("cand|cor");
6672            return expression_none;
6673    }
6674}
6675
6676static inline halfword tex_scan_aux_equal(int *alreadygotten)
6677{
6678    tex_get_x_token();
6679    switch (cur_tok) {
6680        case equal_token:
6681            return expression_equal;
6682        default:
6683            *alreadygotten = 1;
6684            return expression_equal;
6685    }
6686}
6687
6688static inline halfword tex_scan_aux_less(int *alreadygotten)
6689{
6690    tex_get_x_token();
6691    switch (cur_tok) {
6692        case less_token:
6693            return expression_bleft;
6694        case equal_token:
6695            return expression_lessequal;
6696        case more_token:
6697            return expression_unequal;
6698        default:
6699            *alreadygotten = 1;
6700            return expression_less;
6701    }
6702}
6703
6704static inline halfword tex_scan_aux_more(int *alreadygotten)
6705{
6706    tex_get_x_token();
6707    switch (cur_tok) {
6708        case more_token:
6709            return expression_bright;
6710        case equal_token:
6711            return expression_moreequal;
6712        default:
6713            *alreadygotten = 1;
6714            return expression_more;
6715    }
6716}
6717
6718static inline halfword tex_scan_aux_tilde(int *alreadygotten)
6719{
6720    tex_get_x_token();
6721    switch (cur_tok) {
6722        case equal_token:
6723            return expression_unequal;
6724        default:
6725            *alreadygotten = 1;
6726            return expression_bnot;
6727    }
6728}
6729
6730static inline halfword tex_scan_aux_exclamation(int *alreadygotten)
6731{
6732    tex_get_x_token();
6733    switch (cur_tok) {
6734        case equal_token:
6735            return expression_unequal;
6736        default:
6737            *alreadygotten = 1;
6738            return expression_not;
6739    }
6740}
6741
6742static inline halfword tex_scan_aux_ampersand(int *alreadygotten)
6743{
6744    tex_get_x_token();
6745    switch (cur_tok) {
6746        case ampersand_token_l: case ampersand_token_o: case ampersand_token_t:
6747            return expression_and;
6748        default: 
6749            *alreadygotten = 1;
6750            return expression_band;
6751    }
6752}
6753
6754static inline halfword tex_scan_aux_bar(int *alreadygotten)
6755{
6756    tex_get_x_token();
6757    switch (cur_tok) {
6758        case bar_token_l: case bar_token_o:
6759            return expression_or;
6760        default:
6761            *alreadygotten = 1;
6762            return expression_bor;
6763    }
6764}
6765
6766static inline long long tex_aux_double_rounded_long_long(double d)
6767{
6768    if (d < min_doubleinteger) { 
6769        tex_aux_scan_integer_out_of_range_error(10);
6770        return min_longinteger;
6771    } else if (d > max_doubleinteger) { 
6772        tex_aux_scan_integer_out_of_range_error(10);
6773        return max_longinteger;
6774    } else {
6775        return llround(d);
6776    }
6777}
6778
6779/*
6780static inline long long tex_aux_double_floored_long_long(double d)
6781{
6782    long long l = (long long) d;
6783    if (l < min_longinteger) { 
6784        tex_aux_scan_integer_out_of_range_error(10);
6785        return min_longinteger;
6786    } else if (l > max_longinteger) { 
6787        tex_aux_scan_integer_out_of_range_error(10);
6788        return max_longinteger;
6789    } else {    
6790       return llround(d);
6791    }
6792}
6793*/
6794
6795static inline long long tex_aux_long_long(long long l)
6796{
6797    if (l < min_longinteger) { 
6798        tex_aux_scan_integer_out_of_range_error(10);
6799        return min_longinteger;
6800    } else if (l > max_longinteger) { 
6801        tex_aux_scan_integer_out_of_range_error(10);
6802        return max_longinteger;
6803    } else {    
6804       return l;
6805    }
6806}
6807
6808/* Todo: depth () {} check */
6809
6810static void tex_aux_scan_integer_expression(int braced)
6811{
6812    int result;
6813    int state;
6814    int operation;
6815    int negate;
6816    int nothing;
6817    int positive;
6818    int negative;
6819    int bnothing;
6820    long long expression;
6821    long long term;
6822    long long factor = 0;
6823    long long numerator;
6824    int error_a = lmt_scanner_state.arithmic_error;
6825    int error_b = 0;
6826    halfword top = null;
6827    int alreadygotten = 0;
6828    int depth = 0;
6829    cur_val_level = integer_val_level;
6830    lmt_scanner_state.expression_depth++;
6831    if (lmt_scanner_state.expression_depth > max_expression_depth) {
6832        tex_fatal_error("\\numexpression can only be nested " LMT_TOSTRING(max_expression_depth) " deep");
6833    }
6834  RESTART:
6835    result = expression_none;
6836    state = expression_none;
6837    expression = 0;
6838    term = 0;
6839    numerator = 0;
6840    negate = 0;
6841    nothing = 0;
6842    positive = 0;
6843    negative = 0;
6844    bnothing = 0;
6845  CONTINUE:
6846 // operation = integer_val_level; 
6847  AGAIN:
6848    if (alreadygotten) {
6849        alreadygotten = 0;
6850        goto ALREADYGOTTEN;
6851    }
6852    while (1) {
6853        tex_get_x_token();
6854      ALREADYGOTTEN:
6855        switch (cur_cmd) { 
6856            case spacer_cmd:
6857                continue;
6858            case left_brace_cmd:
6859                if ((negate || nothing || positive || negative) && braced == 0) { 
6860                    tex_aux_scan_expression_unexpected_negation_error();
6861                    break;
6862                } else { 
6863                    ++braced;
6864                    if (braced > 1) {
6865                        goto PUSH;
6866                    } else { 
6867                        goto AGAIN;
6868                    }
6869                }
6870            case right_brace_cmd:
6871                if (braced > 1) {
6872                    --braced;
6873                    goto POP;
6874                } else { 
6875                    break;
6876                }
6877            case end_paragraph_cmd:
6878                if (braced) { 
6879                    continue;
6880                } else {
6881                    break;
6882                }
6883            default:
6884                switch (cur_tok) { 
6885                    case minus_token:
6886                        negate = ! negate;
6887                        continue;
6888                    case plus_token:
6889                        continue;
6890                    case n_token_l:
6891                    case n_token_o:
6892                        switch (tex_scan_aux_n()) { 
6893                            case expression_not: nothing = ! nothing;
6894                                continue;
6895                            case expression_positive:
6896                                positive = 1;
6897                                continue;
6898                            case expression_negative:
6899                                negative = 1;
6900                                continue;
6901                            default:
6902                                break;
6903                        }
6904                    case tilde_token_l:
6905                    case tilde_token_o:
6906                        if (tex_scan_aux_tilde(&alreadygotten) == expression_bnot) { 
6907                            bnothing = ! bnothing;
6908                            continue;
6909                        } else { 
6910                            alreadygotten = 0;
6911                            goto ALREADYGOTTEN;
6912                        }
6913                    case exclamation_token_l: 
6914                    case exclamation_token_o:
6915                        if (tex_scan_aux_exclamation(&alreadygotten) == expression_not) { 
6916                            nothing = ! nothing;
6917                            continue;
6918                        } else { 
6919                            alreadygotten = 0;
6920                            goto ALREADYGOTTEN;
6921                        }
6922                    case plus_minus_token:
6923                        positive = 1;
6924                        continue;
6925                    case minus_plus_token:
6926                        negative = 1;
6927                        continue;
6928                    case left_parent_token:
6929                      PUSH:
6930                        ++depth;
6931                        if (depth > max_sub_expression_depth) {
6932                            tex_fatal_error("sub expressions can only be nested " LMT_TOSTRING(max_sub_expression_depth) " deep");
6933                        }
6934                        {
6935                            halfword newtop = tex_get_node(lmtx_expression_node_size);
6936                            node_type(newtop) = lmtx_expression_node;
6937                            node_subtype(newtop) = cur_tok == left_parent_token ? 0x1 : 0x2;
6938                            node_next(newtop) = top;
6939                            lmtx_expression_type(newtop) = (singleword) integer_val_level;
6940                            lmtx_expression_state(newtop) = (singleword) state;
6941                            lmtx_expression_result(newtop) = (singleword) result;
6942                            lmtx_expression_negate(newtop) = (singleword) (negate | (nothing << 1) | (bnothing << 2) | (positive << 3) | (negative << 4)); 
6943                            lmtx_expression_expression(newtop) = expression;
6944                            lmtx_expression_term(newtop) = term;
6945                            lmtx_expression_numerator(newtop) = numerator;
6946                            top = newtop;
6947                            goto RESTART;
6948                        }
6949                    default:
6950                        goto ACTION;
6951                }
6952        }
6953    }
6954  ACTION:
6955    factor = tex_scan_bit_integer(NULL);
6956    if (negate) { 
6957        factor = - factor;
6958        negate = 0;
6959    } 
6960    if (nothing) {
6961        factor = factor ? 0 : 1;
6962        nothing = 0;
6963    }
6964    if (bnothing) {
6965        factor = ~ factor;
6966        bnothing = 0;
6967    }
6968    if (positive) {
6969        if (factor < 0) {    
6970            factor = - factor;
6971        }
6972        positive = 0;
6973    }
6974    if (negative) {
6975        if (factor > 0) {
6976            factor = - factor;
6977        }
6978        negative = 0;
6979    }
6980  FOUND:
6981    do {
6982        tex_get_x_token();
6983    } while (cur_cmd == spacer_cmd || (braced && cur_cmd == end_paragraph_cmd));
6984    switch (cur_tok) {
6985        case plus_token      : operation = expression_add; break;
6986        case minus_token     : operation = expression_subtract; break;
6987        case asterisk_token  : operation = expression_multiply; break;
6988        case slash_token     : operation = expression_divide; break;
6989        case colon_token     : operation = expression_idivide; break;
6990        case semi_colon_token:
6991        case percentage_token: operation = expression_imodulo; break;
6992        /* */
6993        case equal_token: operation = tex_scan_aux_equal(&alreadygotten); break;
6994        case less_token : operation = tex_scan_aux_less(&alreadygotten); break;
6995        case more_token : operation = tex_scan_aux_more(&alreadygotten); break;
6996        /* */
6997        case not_equal_token    : operation = expression_unequal; break;
6998        case less_or_equal_token: operation = expression_lessequal; break;
6999        case more_or_equal_token: operation = expression_moreequal; break;
7000        /* */
7001        case conditional_and_token: operation = expression_cand; break;
7002        case conditional_or_token : operation = expression_cor; break;
7003        /* */
7004        case a_token_l: case a_token_o: operation = tex_scan_aux_a(); break;
7005        case b_token_l: case b_token_o: operation = tex_scan_aux_b(); break;
7006        case c_token_l: case c_token_o: operation = tex_scan_aux_c(); break;
7007        case d_token_l: case d_token_o: operation = tex_scan_aux_d(); break;
7008        case m_token_l: case m_token_o: operation = tex_scan_aux_m(); break;
7009        case o_token_l: case o_token_o: operation = tex_scan_aux_o(); break;
7010        case v_token_l: case v_token_o: operation = expression_bor; break;
7011        /* */
7012        case ampersand_token_l : 
7013        case ampersand_token_o : 
7014        case ampersand_token_t : operation = tex_scan_aux_ampersand(&alreadygotten); break;
7015        case circumflex_token_l: 
7016        case circumflex_token_o: 
7017        case circumflex_token_s: operation = expression_bxor; break;
7018        case bar_token_l       : 
7019        case bar_token_o       : operation = tex_scan_aux_bar(&alreadygotten); break;
7020        /* */
7021        default:
7022            operation = expression_none;
7023            if (! top) {
7024                if (cur_cmd == relax_cmd) {
7025                    if (braced > 0) { 
7026                        tex_aux_scan_unexpected_relax_error();
7027                    } else { 
7028                        /* we're done */
7029                    }
7030                } else if (braced == 1 && cur_cmd == right_brace_cmd) {
7031                    /* we're done */
7032                } else {
7033                    if (braced > 0) { 
7034                        tex_aux_scan_unexpected_whatever_error();
7035                    }
7036                    tex_back_input(cur_tok);
7037                }
7038            } else if (cur_cmd == right_brace_cmd) {
7039                if (braced) { 
7040                    --braced;
7041                } else { 
7042                    tex_back_input(cur_tok);
7043                }
7044            } else if (cur_tok != right_parent_token) {
7045                tex_aux_scan_missing_error();
7046            }
7047            break;
7048    }
7049    lmt_scanner_state.arithmic_error = error_b;
7050    if (state > expression_subtract) { 
7051        if (factor > max_integer || factor < min_integer) {
7052            lmt_scanner_state.arithmic_error = 1;
7053            factor = 0;
7054        }
7055    }
7056    switch (state) {
7057        case expression_none:
7058            term = factor;
7059            break;
7060        case expression_multiply:
7061            if (operation == expression_divide) {
7062                numerator = factor;
7063                operation = expression_scale;
7064            } else {
7065                term = tex_aux_long_long((double) term * (double) factor);
7066            }
7067            break;
7068        case expression_divide:
7069            /* round */
7070            term = tex_aux_double_rounded_long_long((double) term / (double) factor);
7071            break;
7072        case expression_scale:
7073            term = tex_aux_double_rounded_long_long((double) term * (double) numerator / (double) factor);
7074            break;
7075        case expression_idivide:
7076            term = term / factor; 
7077            break;
7078        case expression_imodulo:
7079            term = tex_aux_double_rounded_long_long(fmod((double) term, (double) factor));
7080            break;
7081        /* */
7082        case expression_equal    : term = term == factor; break;
7083        case expression_less     : term = term <  factor; break;
7084        case expression_more     : term = term >  factor; break;
7085        case expression_lessequal: term = term <= factor; break;
7086        case expression_moreequal: term = term >- factor; break;
7087        case expression_unequal  : term = term != factor; break;
7088        case expression_bleft    : term = term << factor; break;
7089        case expression_bright   : term = term >> factor; break;
7090        /* */
7091        case expression_or       : term = (term || factor) ? 1 : 0; break;
7092        case expression_and      : term = (term && factor) ? 1 : 0; break;
7093        /* */
7094        case expression_cor      : term = term             ? term   : (factor ? factor : 0); break;
7095        case expression_cand     : term = (term && factor) ? factor : 0;                     break;
7096        /* */                    
7097        case expression_bor      : term |= factor; break;               
7098        case expression_band     : term &= factor; break;               
7099        case expression_bxor     : term ^= factor; break;               
7100        /* */
7101        case expression_bset     : term = term |   ((long long) 1 << (factor - 1)); break;               
7102        case expression_bunset   : term = term & ~ ((long long) 1 << (factor - 1)); break;
7103        /* */
7104        case expression_not:
7105            /* already handled, like add and subtract */
7106            term = term ? 0 : 1;
7107            break;
7108    }
7109    if (operation > expression_subtract) {
7110        state = operation;
7111    } else {
7112        state = expression_none;
7113        switch (result) { 
7114            case expression_add     : expression = tex_aux_long_long(expression + term); break;
7115            case expression_subtract: expression = tex_aux_long_long(expression - term); break;
7116            /* */
7117            case expression_not     : expression = term ? 0 : 1; break;
7118            case expression_bnot    : expression = ~ term; break;
7119            /* */
7120            default: 
7121                expression = term;
7122                break;
7123        }
7124        result = operation;
7125    }
7126    error_b = lmt_scanner_state.arithmic_error;
7127    if (operation != expression_none) {
7128        goto CONTINUE;
7129    } else if (top) {
7130      POP:
7131        --depth;
7132        {
7133            halfword newtop = node_next(top);
7134            factor = expression;
7135            expression = lmtx_expression_expression(top);
7136            term = lmtx_expression_term(top);
7137            numerator = lmtx_expression_numerator(top);
7138            state = lmtx_expression_state(top);
7139            result = lmtx_expression_result(top);
7140            switch (node_subtype(top)) { 
7141                case 0x1: /* ( ) */
7142                    if (cur_tok != right_parent_token) {
7143                        expression = 0;
7144                        tex_aux_scan_parent_error();
7145                        tex_flush_node_list(top);
7146                        top = null;
7147                        goto DONE;
7148                    }
7149                    break;
7150                case 0x2: /* { } */
7151                    if (cur_cmd != right_brace_cmd) {
7152                        expression = 0;
7153                        tex_aux_scan_brace_error();
7154                        tex_flush_node_list(top);
7155                        top = null;
7156                        goto DONE;
7157                    }
7158                    break;
7159            }
7160            if (lmtx_expression_negate(top) & 0x01) { /* negate << 0 */
7161                factor = - factor;
7162            } 
7163            if (lmtx_expression_negate(top) & 0x02) { /* nothing << 1 */
7164                factor = factor ? 0 : 1;
7165            }          
7166            if (lmtx_expression_negate(top) & 0x04) { /* positive << 2 */
7167                factor = ~ factor;
7168            }
7169            if ((lmtx_expression_negate(top) & 0x08)  && factor < 0) { /* positive << 3 */
7170                factor = - factor;
7171            }
7172            if ((lmtx_expression_negate(top) & 0x10) && factor > 0) { /* negative << 4 */
7173                factor = - factor;
7174            }
7175            tex_free_node(top, lmtx_expression_node_size);
7176            top = newtop;
7177            goto FOUND;
7178        }
7179    } else if (error_b) {
7180        tex_aux_scan_integer_out_of_range_error(8);
7181        expression = 0;
7182    }
7183  DONE:
7184    lmt_scanner_state.arithmic_error = error_a;
7185    lmt_scanner_state.expression_depth--;
7186 // cur_val_level = level;
7187    cur_val_level = integer_val_level;
7188    if (expression < min_integer) {
7189        tex_aux_scan_integer_out_of_range_error(9);
7190        cur_val = min_integer;
7191    } else if (expression > max_integer) {
7192        tex_aux_scan_integer_out_of_range_error(9);
7193        cur_val = max_integer;
7194    } else { 
7195        cur_val = (halfword) expression;
7196    }
7197}
7198
7199typedef enum expression_type { 
7200    expression_type_integer,
7201    expression_type_dimension,
7202    expression_type_float,
7203} expression_type;
7204
7205static long long tex_aux_expression_d_round(double d, halfword *termtype, halfword factortype)
7206{
7207    switch (*termtype) { 
7208        case expression_type_integer:
7209            *termtype = factortype;
7210            break;
7211        case expression_type_dimension:
7212            switch (factortype) { 
7213                case expression_type_integer:
7214                    *termtype = expression_type_dimension;
7215                    break;
7216                case expression_type_dimension:
7217                    d /= 65536;
7218                    *termtype = expression_type_dimension;
7219                    break;
7220                case expression_type_float:
7221                    d /= 65536;
7222                    *termtype = expression_type_dimension;
7223                    break;
7224            }
7225            break;
7226        case expression_type_float:
7227            switch (factortype) { 
7228                case expression_type_integer:
7229                    *termtype = expression_type_float;
7230                    break;
7231                case expression_type_dimension:
7232                    d /= 65536;
7233                    *termtype = expression_type_dimension;
7234                    break;
7235                case expression_type_float:
7236                    d /= 65536;
7237                    *termtype = expression_type_float;
7238                    break;
7239            }
7240            break;
7241    }
7242    if (d < min_doubleinteger) { 
7243        return min_longinteger;
7244    } else if (d > max_doubleinteger) { 
7245        return max_longinteger;
7246    } else {
7247        return longlonground(d);
7248    }
7249}
7250
7251static long long tex_aux_expression_d_divide(double d, halfword *termtype, halfword factortype)
7252{
7253    switch (*termtype) { 
7254        case expression_type_integer:
7255            *termtype = factortype;
7256            break;
7257        case expression_type_dimension:
7258            switch (factortype) { 
7259                case expression_type_integer:
7260                    *termtype = expression_type_dimension;
7261                    break;
7262                case expression_type_dimension:
7263                    d *= 65536;
7264                    *termtype = expression_type_dimension;
7265                    break;
7266                case expression_type_float:
7267                    d *= 65536;
7268                    *termtype = expression_type_dimension;
7269                    break;
7270            }
7271            break;
7272        case expression_type_float:
7273            switch (factortype) { 
7274                case expression_type_integer:
7275                    *termtype = expression_type_float;
7276                    break;
7277                case expression_type_dimension:
7278                    d *= 65536;
7279                    *termtype = expression_type_dimension;
7280                    break;
7281                case expression_type_float:
7282                    d *= 65536;
7283                    *termtype = expression_type_float;
7284                    break;
7285            }
7286            break;
7287    }
7288    if (d < min_doubleinteger) { 
7289        return min_longinteger;
7290    } else if (d > max_doubleinteger) { 
7291        return max_longinteger;
7292    } else {
7293        return longlonground(d);
7294    }
7295}
7296
7297static long long tex_aux_expression_d_round_divide(double d, halfword *termtype, halfword factortype, halfword divtype)
7298{
7299    switch (*termtype) { 
7300        case expression_type_integer:
7301            *termtype = factortype;
7302            break;
7303        case expression_type_dimension:
7304            switch (factortype) { 
7305                case expression_type_integer:
7306                    *termtype = expression_type_dimension;
7307                    break;
7308                case expression_type_dimension:
7309                    d /= 65536;
7310                    *termtype = expression_type_dimension;
7311                    break;
7312                case expression_type_float:
7313                    d /= 65536;
7314                    *termtype = expression_type_dimension;
7315                    break;
7316            }
7317            break;
7318        case expression_type_float:
7319            switch (factortype) { 
7320                case expression_type_integer:
7321                    *termtype = expression_type_float;
7322                    break;
7323                case expression_type_dimension:
7324                    d /= 65536;
7325                    *termtype = expression_type_dimension;
7326                    break;
7327                case expression_type_float:
7328                    d /= 65536;
7329                    *termtype = expression_type_float;
7330                    break;
7331            }
7332            break;
7333    }
7334    /* */
7335    switch (*termtype) { 
7336        case expression_type_integer:
7337            *termtype = divtype;
7338            break;
7339        case expression_type_dimension:
7340            switch (divtype) { 
7341                case expression_type_integer:
7342                    *termtype = expression_type_dimension;
7343                    break;
7344                case expression_type_dimension:
7345                    d *= 65536;
7346                    *termtype = expression_type_dimension;
7347                    break;
7348                case expression_type_float:
7349                    d *= 65536;
7350                    *termtype = expression_type_dimension;
7351                    break;
7352            }
7353            break;
7354        case expression_type_float:
7355            switch (divtype) { 
7356                case expression_type_integer:
7357                    *termtype = expression_type_float;
7358                    break;
7359                case expression_type_dimension:
7360                    d *= 65536;
7361                    *termtype = expression_type_dimension;
7362                    break;
7363                case expression_type_float:
7364                    d *= 65536;
7365                    *termtype = expression_type_float;
7366                    break;
7367            }
7368            break;
7369    }
7370    if (d < min_doubleinteger) { 
7371        return min_longinteger;
7372    } else if (d > max_doubleinteger) { 
7373        return max_longinteger;
7374    } else {
7375        return longlonground(d);
7376    }
7377}
7378
7379static long long tex_aux_expression_d_floor(double d, halfword *termtype, halfword factortype)
7380{
7381    switch (*termtype) { 
7382        case expression_type_integer:
7383            *termtype = factortype;
7384            break;
7385        case expression_type_dimension:
7386            switch (factortype) { 
7387                case expression_type_integer:
7388                    *termtype = expression_type_dimension;
7389                    break;
7390                case expression_type_dimension:
7391                    d /= 65536;
7392                    *termtype = expression_type_dimension;
7393                    break;
7394                case expression_type_float:
7395                    d /= 65536;
7396                    *termtype = expression_type_dimension;
7397                    break;
7398            }
7399            break;
7400        case expression_type_float:
7401            switch (factortype) { 
7402                case expression_type_integer:
7403                    *termtype = expression_type_float;
7404                    break;
7405                case expression_type_dimension:
7406                    d /= 65536;
7407                    *termtype = expression_type_dimension;
7408                    break;
7409                case expression_type_float:
7410                    d /= 65536;
7411                    *termtype = expression_type_float;
7412                    break;
7413            }
7414            break;
7415    }
7416    if (d < min_doubleinteger) { 
7417        return min_longinteger;
7418    } else if (d > max_doubleinteger) { 
7419        return max_longinteger;
7420    } else {
7421        return (long long) d;
7422    }
7423}
7424
7425/* Todo: depth () {} check */
7426
7427static void tex_aux_scan_dimension_expression(int braced)
7428{
7429    int result;
7430    int state;
7431    int operation;
7432    int negate;
7433    int nothing;
7434    int positive;
7435    int negative;
7436    int expressiontype;
7437    int termtype; 
7438    int factortype = expression_type_integer; 
7439    int numeratortype;
7440    long long expression;
7441    long long term; 
7442    long long factor = 0;
7443    long long numerator;
7444    int error_a = lmt_scanner_state.arithmic_error;
7445    int error_b = 0;
7446    halfword top = null;
7447    int alreadygotten = 0;
7448    int depth = 0;
7449    cur_val_level = dimension_val_level;
7450    lmt_scanner_state.expression_depth++;
7451    if (lmt_scanner_state.expression_depth > max_expression_depth) {
7452        tex_fatal_error("\\dimexpression can only be nested " LMT_TOSTRING(max_expression_depth) " deep");
7453    }
7454  RESTART:
7455    result = expression_none;
7456    state = expression_none;
7457    expressiontype = expression_type_dimension;
7458    termtype = expression_type_dimension;
7459    numeratortype = expression_type_integer;
7460    expression = 0;
7461    term = 0;
7462    numerator = 0;
7463    negate = 0;
7464    nothing = 0;
7465    positive = 0;
7466    negative = 0;
7467  CONTINUE:
7468    operation = dimension_val_level;  /* not needed, abused */
7469  AGAIN:
7470    if (alreadygotten) {
7471        alreadygotten = 0;
7472        goto ALREADYGOTTEN;
7473    }
7474    while (1) {
7475        tex_get_x_token();
7476      ALREADYGOTTEN:
7477        switch (cur_cmd ){ 
7478            case spacer_cmd:
7479                continue;
7480            case left_brace_cmd:
7481                if ((negate || nothing || positive || negative) && braced == 0) { 
7482                    tex_aux_scan_expression_unexpected_negation_error();
7483                    break;
7484                } else { 
7485                    ++braced;
7486                    if (braced > 1) {
7487                        goto PUSH;
7488                    } else { 
7489                        goto AGAIN;
7490                    }
7491                }
7492            case right_brace_cmd:
7493                if (braced > 1) {
7494                    --braced;
7495                    goto POP;
7496                } else { 
7497                    break;
7498                }
7499            case end_paragraph_cmd:
7500                if (braced) { 
7501                    continue;
7502                } else {
7503                    break;
7504                }
7505            default:
7506                switch (cur_tok) { 
7507                    case minus_token:
7508                        negate = ! negate;
7509                        continue;
7510                    case plus_token:
7511                        continue;
7512                    case n_token_l:
7513                    case n_token_o:
7514                        switch (tex_scan_aux_n()) { 
7515                            case expression_not:
7516                                nothing = ! nothing;
7517                                continue;
7518                            case expression_positive:
7519                                positive = 1;
7520                                continue;
7521                            case expression_negative:
7522                                negative = 1;
7523                                continue;
7524                            default:
7525                                break;
7526                        }
7527                    case exclamation_token_l: 
7528                    case exclamation_token_o:
7529                        if (tex_scan_aux_exclamation(&alreadygotten) == expression_not) { 
7530                            nothing = ! nothing;
7531                            continue;
7532                        } else { 
7533                            alreadygotten = 0;
7534                            goto ALREADYGOTTEN;
7535                        }
7536                    case plus_minus_token:
7537                        positive = 1;
7538                        continue;
7539                    case minus_plus_token:
7540                        negative = 1;
7541                        continue;
7542                    case left_parent_token:
7543                       PUSH:
7544                        ++depth;
7545                        if (depth > max_sub_expression_depth) {
7546                            tex_fatal_error("sub expressions can only be nested " LMT_TOSTRING(max_sub_expression_depth) " deep");
7547                        }
7548                        { 
7549                            halfword newtop = tex_get_node(lmtx_expression_node_size);
7550                            node_type(newtop) = lmtx_expression_node;
7551                            node_subtype(newtop) = cur_tok == left_parent_token ? 0x1 : 0x2;
7552                            node_next(newtop) = top;
7553                            lmtx_expression_type(newtop) = (singleword) dimension_val_level;
7554                            lmtx_expression_state(newtop) = (singleword) state;
7555                            lmtx_expression_result(newtop) = (singleword) result;               /* no (bnothing << 2) here */
7556                            lmtx_expression_negate(newtop) = (singleword) (negate | (nothing << 1) | (positive << 3) | (negative << 4)); 
7557                            lmtx_expression_expression(newtop) = expression;
7558                            lmtx_expression_term(newtop) = term;
7559                            lmtx_expression_numerator(newtop) = numerator;
7560                            lmtx_expression_type_expression(newtop) = expressiontype;
7561                            lmtx_expression_type_term(newtop) = termtype;
7562                            lmtx_expression_type_numerator(newtop) = numeratortype;
7563                            top = newtop;
7564                            goto RESTART;
7565                        }
7566                    default:
7567                        goto ACTION;
7568                }
7569        }
7570    }
7571  ACTION:
7572    {
7573        int has_fraction = 0;
7574        int has_unit = 1;
7575        factor = tex_scan_bit_dimension(&has_fraction, &has_unit);
7576        if (has_unit) { 
7577            factortype = expression_type_dimension;
7578        } else if (has_fraction) { 
7579            factortype = expression_type_float;
7580        } else {  
7581            factortype = expression_type_integer;
7582        }
7583    }
7584    if (negate) { 
7585        factor = - factor;
7586        negate = 0;
7587    }
7588    if (nothing) {
7589        factor = factor ? 0 : 1;
7590        nothing = 0;
7591        factortype = expression_type_integer;
7592    }
7593    if (positive) {
7594        if (factor < 0) { 
7595            factor = - factor;
7596        }
7597        positive = 0;
7598    }
7599    if (negative) {
7600        if (factor > 0) {
7601            factor = - factor;
7602        }
7603        negative = 0;
7604    }
7605  FOUND:
7606    do {
7607        tex_get_x_token();
7608    } while (cur_cmd == spacer_cmd || (braced && cur_cmd == end_paragraph_cmd));
7609    switch (cur_tok) {
7610        case plus_token      : operation = expression_add; break;
7611        case minus_token     : operation = expression_subtract; break;
7612        case asterisk_token  : operation = expression_multiply; break;
7613        case slash_token     : operation = expression_divide; break;
7614        case colon_token     : operation = expression_idivide; break;
7615        case semi_colon_token:
7616        case percentage_token: operation = expression_imodulo; break;
7617        /* */
7618        case equal_token: operation = tex_scan_aux_equal(&alreadygotten); break;
7619        case less_token : operation = tex_scan_aux_less(&alreadygotten); break;
7620        case more_token : operation = tex_scan_aux_more(&alreadygotten); break;
7621        /* */
7622        case not_equal_token    : operation = expression_unequal; break;
7623        case less_or_equal_token: operation = expression_lessequal; break;
7624        case more_or_equal_token: operation = expression_moreequal; break;
7625        /* */
7626        case conditional_and_token: operation = expression_cand; break;
7627        case conditional_or_token : operation = expression_cor; break;
7628       /* */
7629        case a_token_l: case a_token_o: operation = tex_scan_aux_a(); break;
7630        case c_token_l: case c_token_o: operation = tex_scan_aux_c(); break;
7631        case d_token_l: case d_token_o: operation = tex_scan_aux_d(); break;
7632        case m_token_l: case m_token_o: operation = tex_scan_aux_m(); break;
7633        case o_token_l: case o_token_o: operation = tex_scan_aux_o(); break;
7634        /* */
7635        default:
7636            operation = expression_none;
7637            if (! top) {
7638                if (cur_cmd == relax_cmd) {
7639                    if (braced > 0) { 
7640                        tex_aux_scan_unexpected_relax_error();
7641                    } else { 
7642                        /* we're done */
7643                    }
7644                } else if (braced == 1 && cur_cmd == right_brace_cmd) {
7645                    /* we're done */
7646                } else {
7647                    if (braced > 0) { 
7648                        tex_aux_scan_unexpected_whatever_error();
7649                    }
7650                    tex_back_input(cur_tok);
7651                }
7652            } else if (cur_cmd == right_brace_cmd) {
7653                if (braced) { 
7654                    --braced;
7655                } else { 
7656                    tex_back_input(cur_tok);
7657                }
7658            } else if (cur_tok != right_parent_token) {
7659                tex_aux_scan_missing_error();
7660            }
7661            break;
7662    }
7663    lmt_scanner_state.arithmic_error = error_b;
7664    if (llabs(factor) > max_dimension) {
7665        lmt_scanner_state.arithmic_error = 1;
7666        factor = 0;
7667    }
7668    switch (state) {
7669        case expression_none:
7670            term = factor;
7671            termtype = factortype;
7672            break;
7673        case expression_multiply:
7674            if (operation == expression_divide) {
7675                numerator = factor;
7676                numeratortype = factortype;
7677                operation = expression_scale;
7678            } else {
7679                term = tex_aux_expression_d_round((double) term * (double) factor, &termtype, factortype);
7680            }
7681            break;
7682        case expression_divide:
7683            if (factor == 0) {
7684                lmt_scanner_state.arithmic_error = 1;
7685                term = 0;
7686                termtype = expression_type_integer;
7687            } else {
7688                term = tex_aux_expression_d_divide((double) term / (double) factor, &termtype, factortype);
7689            }
7690            break;
7691        case expression_scale:
7692            if (factor == 0) {
7693                lmt_scanner_state.arithmic_error = 1;
7694                term = 0;
7695                termtype = expression_type_integer;
7696            } else { 
7697             // term = tex_aux_expression_d_round((double) term * (double) numerator, &termtype, numeratortype);
7698             // term = tex_aux_expression_d_divide((double) term / (double) factor, &termtype, factortype);
7699                term = tex_aux_expression_d_round_divide((double) term * (double) numerator / (double) factor, &termtype, numeratortype, factortype);
7700            }
7701            break;
7702        case expression_idivide:
7703            if (factor == 0) {
7704                lmt_scanner_state.arithmic_error = 1;
7705                term = 0;
7706                termtype = expression_type_integer;
7707            } else {
7708                term = tex_aux_expression_d_floor((double) term / (double) factor, &termtype, factortype);
7709            }
7710            break;
7711        case expression_imodulo:
7712            if (factor == 0) {
7713                lmt_scanner_state.arithmic_error = 1;
7714                term = 0;
7715                termtype = expression_type_integer;
7716            } else {
7717                term = tex_aux_expression_d_divide(fmod((double) term, (double) factor), &termtype, factortype);
7718            }
7719            break;
7720        /* */
7721        case expression_equal:
7722            /* todo: check types */
7723            term = term == factor;
7724            termtype = expression_type_integer;
7725            break;
7726        case expression_less:
7727            /* todo: check types */
7728            term = term < factor;
7729            termtype = expression_type_integer;
7730            break;
7731        case expression_more:
7732            /* todo: check types */
7733            term = term > factor;
7734            termtype = expression_type_integer;
7735            break;
7736        case expression_lessequal:
7737            /* todo: check types */
7738            term = term <= factor;
7739            termtype = expression_type_integer;
7740            break;
7741        case expression_moreequal:
7742            /* todo: check types */
7743            term = term >- factor;
7744            termtype = expression_type_integer;
7745            break;
7746        case expression_unequal:
7747            /* todo: check types */
7748            term = term != factor;
7749            termtype = expression_type_integer;
7750            break;
7751        case expression_or:
7752            /* todo: check types */
7753            term = (term || factor) ? 1 : 0;
7754            termtype = expression_type_integer;
7755            break;
7756        case expression_and:
7757            /* todo: check types */
7758            term = (term && factor) ? 1 : 0;
7759            termtype = expression_type_integer;
7760            break;
7761        case expression_cor:
7762            if (term) { 
7763                /* ok */
7764            } else if (factor) {
7765                term = factor; 
7766                termtype = factortype;
7767            } else {
7768                term = 0; 
7769                termtype = expression_type_integer;
7770            }
7771            break;
7772        case expression_cand:
7773            if (term && factor) { 
7774                term = factor;
7775                termtype = factortype;
7776            } else { 
7777                term = 0; 
7778                termtype = expression_type_integer;
7779            }
7780            break;
7781        case expression_not:
7782            /* todo: check types */
7783            /* already handled, like add and subtract */
7784            term = ! term;
7785            termtype = expression_type_integer;
7786            break;
7787        /* */
7788    }
7789    if (operation > expression_subtract) {
7790        state = operation;
7791    } else {
7792        state = expression_none;
7793        switch (result) { 
7794            case expression_add:
7795                /* depends on term type: float / dimen ok, int is sp */
7796                expression = expression + term;
7797                expressiontype = expression_type_dimension;
7798                break;
7799            case expression_subtract:
7800                /* depends on term type: float / dimen ok, int is sp */
7801                expression = expression - term;
7802                expressiontype = expression_type_dimension;
7803                break;
7804            case expression_not:
7805                expression = term ? 0 : 1;
7806                expressiontype = expression_type_dimension;
7807                break;
7808         // case expression_none:
7809            default:
7810                expression = term;
7811                expressiontype = termtype;
7812                break;
7813        }
7814        result = operation;
7815    }
7816    error_b = lmt_scanner_state.arithmic_error;
7817    if (operation != expression_none) {
7818        goto CONTINUE;
7819    } else if (top) {
7820      POP:
7821        --depth;
7822        { 
7823            halfword newtop = node_next(top);
7824            factor = expression;
7825            expression = lmtx_expression_expression(top);
7826            term = lmtx_expression_term(top);
7827            numerator = lmtx_expression_numerator(top);
7828            state = lmtx_expression_state(top);
7829            result = lmtx_expression_result(top);
7830            expressiontype = lmtx_expression_type_expression(top);
7831            termtype = lmtx_expression_type_term(top);
7832            numeratortype = lmtx_expression_type_numerator(top);
7833            switch (node_subtype(top)) { 
7834                case 0x1: /* ( ) */
7835                    if (cur_tok != right_parent_token) {
7836                        expression = 0;
7837                        tex_aux_scan_parent_error();
7838                        tex_flush_node_list(top);
7839                        top = null;
7840                        goto DONE;
7841                    }
7842                    break;
7843                case 0x2: /* { } */
7844                    if (cur_cmd != right_brace_cmd) {
7845                        expression = 0;
7846                        tex_aux_scan_brace_error();
7847                        tex_flush_node_list(top);
7848                        top = null;
7849                        goto DONE;
7850                    }
7851                    break;
7852            }
7853            if (lmtx_expression_negate(top) & 0x01) { /* negate << 0 */
7854                factor = - factor;
7855            } 
7856            if (lmtx_expression_negate(top) & 0x02) { /* nothing << 1 */
7857                factor = factor ? 0 : 1;
7858                factortype = expression_type_integer;
7859            }
7860            if ((lmtx_expression_negate(top) & 0x08) && factor < 0) { /* positive << 3 */
7861                factor = - factor;
7862            }
7863            if ((lmtx_expression_negate(top) & 0x10) && factor > 0) { /* negative << 4 */
7864                factor = - factor;
7865            }
7866            tex_free_node(top, lmtx_expression_node_size);
7867            top = newtop;
7868            goto FOUND;
7869        }
7870    } else if (error_b) {
7871        tex_aux_scan_dimension_out_of_range_error(4);
7872        expression = 0;
7873    }
7874  DONE:
7875    lmt_scanner_state.arithmic_error = error_a;
7876    lmt_scanner_state.expression_depth--;
7877    cur_val_level = dimension_val_level;
7878    if (expression < min_dimension) {
7879        tex_aux_scan_dimension_out_of_range_error(5);
7880        cur_val = min_dimension;
7881    } else if (expression > max_dimension) {
7882        tex_aux_scan_dimension_out_of_range_error(5);
7883        cur_val = max_dimension;
7884    } else { 
7885        cur_val = (halfword) expression;
7886    }
7887}
7888
7889/* 
7890    We have the somewhat weird |\dimexpr| and friends from \ETEX, but now prefer to use the more 
7891    flexible |\dimexpression| instead. However that one still has only divide and multiply win 
7892    over plus and minus. The next scanner is bit more advanced. It used to be the one used for 
7893    |\dimexpression| etc.\ but now is just an extra one, currently bound to |\dimexperimental| 
7894    and |\numexperimental|. 
7895
7896    I need to check cases where we can overflow and we might at some point delay clipping to the 
7897    maxima till we return. There might also be cases where operations on mixed number types give 
7898    unexpected results.
7899
7900    In the end, after some variations, I decided that some reverse polish notation approach made
7901    more sense and when considering an infix to rpn translation and searching the web a bit I ran
7902    into nice example:
7903
7904        https://github.com/chidiwilliams/expression-evaluator/blob/main/simple.js
7905
7906    It shows how to handled the nested expressions. I made a comparable variant in \LUA, extended
7907    it for more than the usual four operators, condensed it a bit and then went on to write the code
7908    below. Of course we have a completely different token parser and we use \TEX\ (temp) nodes for
7909    a few stacks. I know that we can combine the loops but that becomes messy and performance is
7910    quite okay, also because we move items from one to another stack with little overhead. Although
7911    stacks are not that large, using static sized stacks (\CCODE\ arrays) makes no sense here.
7912
7913    Unary minus was sort of a challenge as was the fact that we also need to handle multiple 
7914    (redundant) + and a possibly mixed + and - sequence. So in the end the code became a bit more
7915    complex than the example. 
7916
7917*/
7918
7919/* 
7920    todo: use subtype for all 
7921*/
7922
7923typedef enum bit_expression_states {
7924    bit_expression_none,
7925
7926    bit_expression_bor,       /*  |   bor  v */
7927    bit_expression_band,      /*  &   band   */
7928    bit_expression_bxor,      /*  ^   bxor   */
7929    bit_expression_bnot,      /*  ~   bnot   */
7930
7931    bit_expression_bset,      /*      bset   */
7932    bit_expression_bunset,    /*      bunset */
7933
7934    bit_expression_bleft,     /*  <<         */
7935    bit_expression_bright,    /*  >>         */
7936
7937    bit_expression_less,      /*  <          */
7938    bit_expression_lessequal, /*  <=         */
7939    bit_expression_equal,     /*  =   ==     */
7940    bit_expression_moreequal, /*  >=         */
7941    bit_expression_more,      /*  >          */
7942    bit_expression_unequal,   /*  <>  !=     */
7943
7944    bit_expression_add,       /*  +          */
7945    bit_expression_subtract,  /*  -          */
7946
7947    bit_expression_multiply,  /*  *          */
7948    bit_expression_divide,    /*  /          */
7949
7950    bit_expression_div,       /*  :   div    */
7951    bit_expression_mod,       /*  ; % mod    */
7952
7953 // bit_expression_power,     /*             */
7954
7955    bit_expression_not,       /* ! not       */
7956
7957    bit_expression_pm,        /* pm          */
7958    bit_expression_mp,        /* mp          */
7959
7960    bit_expression_or,        /* or          */
7961    bit_expression_and,       /* and         */
7962
7963    bit_expression_cor,       /* cor         */
7964    bit_expression_cand,      /* cand        */
7965
7966    bit_expression_open_parent,
7967    bit_expression_close_parent,
7968    bit_expression_open_brace,
7969    bit_expression_close_brace,
7970
7971    bit_expression_number,
7972    bit_expression_float,
7973    bit_expression_dimension,
7974
7975    bit_expression_plus,
7976    bit_expression_minus,
7977    bit_expression_flip,
7978    bit_expression_positive,
7979    bit_expression_negative,
7980    
7981    /* applied to top */
7982
7983    bit_expression_sin,
7984    bit_expression_cos,
7985    bit_expression_tan,
7986    bit_expression_asin,
7987    bit_expression_acos,
7988    bit_expression_atan,
7989
7990    bit_expression_sinh,
7991    bit_expression_cosh,
7992    bit_expression_tanh,
7993    bit_expression_asinh,
7994    bit_expression_acosh,
7995    bit_expression_atanh,
7996
7997    bit_expression_ceil,
7998    bit_expression_floor,
7999    bit_expression_round,
8000    bit_expression_abs,
8001
8002    bit_expression_sqrt,
8003    bit_expression_log,
8004    bit_expression_ln, 
8005    bit_expression_exp,
8006
8007    /* applied to two top */
8008
8009    bit_expression_last,
8010} bit_expression_states;
8011
8012static int bit_operator_precedence[bit_expression_last+1] = {  /* like in lua */
8013    0, // bit_expression_none
8014
8015    4, // bit_expression_bor
8016    6, // bit_expression_band
8017    5, // bit_expression_bxor
8018   10, // bit_expression_bnot
8019
8020    7, // bit_expression_bset   // like shifts
8021    7, // bit_expression_bunset // like shifts
8022
8023    7, // bit_expression_bleft
8024    7, // bit_expression_bright
8025
8026    3, // bit_expression_less
8027    3, // bit_expression_lessequal
8028    3, // bit_expression_equal
8029    3, // bit_expression_more
8030    3, // bit_expression_moreequal
8031    3, // bit_expression_unequal
8032
8033    8, // bit_expression_add
8034    8, // bit_expression_subtract
8035
8036    9, // bit_expression_multiply
8037    9, // bit_expression_divide
8038
8039    9, // bit_expression_div
8040    9, // bit_expression_mod
8041
8042// 10, // bit_expression_power
8043
8044   10, // bit_expression_not
8045
8046   10, // bit_expression_pm
8047   10, // bit_expression_mp
8048
8049    1, // bit_expression_or
8050    2, // bit_expression_and
8051
8052    1, // bit_expression_cor
8053    2, // bit_expression_cand
8054
8055    0, // bit_expression_open_parent
8056    0, // bit_expression_close_parent
8057    0, // bit_expression_open_brace
8058    0, // bit_expression_close_brace
8059
8060    0, // bit_expression_number
8061    0, // bit_expression_float
8062    0, // bit_expression_dimension
8063
8064    0, 0, 0, 0, 0,    // plus minus flip positive negative 
8065
8066    0, 0, 0, 0, 0, 0, // sin cos tan asin acos atan
8067    0, 0, 0, 0, 0, 0, // sinh cosh tanh asinh acosh atanh
8068    0, 0, 0, 0,       // ceil floor round abs 
8069    0, 0, 0, 0,       // sqrt log ln exp 
8070
8071    0                 // null 
8072};
8073
8074static const char *bit_expression_names[bit_expression_last+1] = {
8075    "none", 
8076    "bor", "band", "bxor", "bnot", 
8077    "bset", "bunset",
8078    "<<", ">>", 
8079    "<", "<=", "==", ">=", ">", "<>",
8080    "+", "-", 
8081    "*", "/", 
8082    "div", "mod", 
8083    "not",
8084    "±", "∓"
8085    "or", "and", 
8086    "cor", "cand",
8087    "open (", "close )", "open {", "close }", 
8088    "number", "float", "dimension",
8089    "plus","minus","flip","positive", "negative",
8090    /* */
8091    "sin", "cos", "tan", "asin", "acos", "atan",
8092    "sinh", "cosh", "tanh", "asinh", "acosh", "atanh",    
8093    "ceil", "floor", "round", "abs",   
8094    "sqrt", "log", "ln", "exp",
8095    /* */
8096    "null"
8097};
8098
8099/*tex
8100    This way we stay within the regular tex accuracy with 1000 scales. But I will play with a
8101    variant that only uses doubles: |dimenexpression| and |numberexpression|.
8102*/
8103
8104# define factor 1 // 256, 1000 : wrong results so needs a fix
8105
8106typedef struct stack_info {
8107    halfword head;
8108    halfword tail;
8109} stack_info;
8110
8111static stack_info tex_aux_new_stack(void)
8112{
8113    return (stack_info) {
8114        .head = null,
8115        .tail = null,
8116    };
8117}
8118
8119static void tex_aux_dispose_stack(stack_info *stack)
8120{
8121    /*tex Unless we have a problem we have stacks with zero or one slot. */
8122    halfword current = stack->head;
8123    while (current) {
8124        halfword next = node_next(current);
8125        tex_free_node(current, rpn_expression_node_size);
8126        current = next;
8127    }
8128}
8129
8130static void tex_push_stack_entry(stack_info *stack, long long value)
8131{
8132    halfword n = tex_get_node(rpn_expression_node_size);
8133    node_type(n) = rpn_expression_node;
8134    node_subtype(n) = 0;
8135    rpn_expression_entry(n) = value;
8136    if (! stack->head) {
8137        stack->head = n;
8138    } else if (stack->head == stack->tail)  {
8139        node_next(stack->head) = n;
8140        node_prev(n) = stack->head;
8141    } else {
8142        node_prev(n) = stack->tail;
8143        node_next(stack->tail) = n;
8144    }
8145    stack->tail = n;
8146}
8147
8148static long long tex_pop_stack_entry(stack_info *stack)
8149{
8150    halfword t = stack->tail;
8151    if (t) {
8152        long long v = rpn_expression_entry(t);
8153        if (t == stack->head) {
8154            stack->head = null;
8155            stack->tail = null;
8156        } else {
8157            stack->tail = node_prev(t);
8158            node_next(stack->tail) = null;
8159        }
8160        tex_free_node(t, rpn_expression_node_size);
8161        return v;
8162    } else {
8163        return 0;
8164    }
8165}
8166
8167static void tex_move_stack_entry(stack_info *target, stack_info *source)
8168{
8169    halfword n = source->tail;
8170    if (n == source->head) {
8171        source->head = null;
8172        source->tail = null;
8173    } else {
8174        source->tail = node_prev(n);
8175    }
8176    if (! target->head) {
8177        target->head = n;
8178        node_prev(n) = null;
8179    } else if (target->head == target->tail)  {
8180        node_next(target->head) = n;
8181        node_prev(n) = target->head;
8182    } else {
8183        node_prev(n) = target->tail;
8184        node_next(target->tail) = n;
8185    }
8186    node_next(n) = null;
8187    target->tail = n;
8188}
8189
8190static void tex_take_stack_entry(stack_info *target, stack_info *source, halfword current)
8191{
8192    while (source->head != current) {
8193        halfword next = node_next(source->head);
8194        tex_free_node(source->head, rpn_expression_node_size);
8195        source->head = next;
8196    }
8197    if (current == source->tail) {
8198        source->head = null;
8199        source->tail = null;
8200    } else {
8201        source->head = node_next(current);
8202    }
8203    if (! target->head) {
8204        target->head = current;
8205        node_prev(current) = null;
8206    } else if (target->head == target->tail)  {
8207        node_next(target->head) = current;
8208        node_prev(current) = target->head;
8209    } else {
8210        node_prev(current) = target->tail;
8211        node_next(target->tail) = current;
8212    }
8213    node_next(current) = null;
8214    target->tail = current;
8215}
8216
8217# define e_v(value) ((value > max_integer) ? max_integer : ((value < min_integer) ? min_integer : (halfword) value))
8218
8219static void tex_aux_print_expression_entry(halfword type, long long value)
8220{
8221    switch (type) {
8222        case bit_expression_number:
8223            tex_print_int(e_v(value));
8224            break;
8225        case bit_expression_float:
8226            tex_print_dimension(e_v(value), no_unit);
8227            break;
8228        case bit_expression_dimension:
8229            tex_print_dimension(e_v(value), pt_unit);
8230            break;
8231        default:
8232            if (value >= 0 && value <= 38) {
8233                tex_print_str(bit_expression_names[value]);
8234            }
8235            break;
8236    }
8237}
8238
8239// static void tex_aux_trace_expression_entry(const char *what, halfword type, long long value)
8240// {
8241//     tex_begin_diagnostic();
8242//     tex_print_format("[expression entry %s: ", what);
8243//     tex_aux_print_expression_entry(type, value);
8244//     tex_print_char(']');
8245//     tex_end_diagnostic();
8246// }
8247
8248static void tex_aux_trace_expression(stack_info stack, halfword level, halfword n, int what)
8249{
8250    tex_begin_diagnostic();
8251    if (n > 0) {
8252        tex_print_format(level == dimension_val_level ? "[dimexpression rpn %i %s:" : "[numexpression rpn %i %s:", n, what ? "r" :"s");
8253        if (! stack.head) {
8254            tex_print_char(' ');
8255        }
8256    } else {
8257        tex_print_str(level == dimension_val_level ? "[dimexpression rpn:" : "[numexpression rpn:");
8258    }
8259    for (halfword current = stack.head; current; current = node_next(current)) {
8260        tex_print_char(' ');
8261        tex_aux_print_expression_entry(node_subtype(current), rpn_expression_entry(current));
8262    }
8263    tex_print_char(']');
8264    tex_end_diagnostic();
8265}
8266
8267static inline halfword tex_scan_aux_function(int *alreadygotten)
8268{
8269    tex_get_x_token();
8270    switch (cur_tok) {
8271        case a_token_l: case a_token_o:
8272            tex_get_x_token();
8273            switch (cur_tok) { 
8274                case b_token_l: case b_token_o:
8275                    tex_get_x_token();
8276                    switch (cur_tok) { 
8277                        case s_token_l: case s_token_o:
8278                            return bit_expression_abs;
8279                    }
8280                    break;
8281                case c_token_l: case c_token_o:
8282                    tex_get_x_token();
8283                    switch (cur_tok) { 
8284                        case o_token_l: case o_token_o:
8285                            tex_get_x_token();
8286                            switch (cur_tok) { 
8287                                case s_token_l: case s_token_o:
8288                                    tex_get_x_token();
8289                                    switch (cur_tok) { 
8290                                        case h_token_l: case h_token_o:
8291                                            return bit_expression_acosh;
8292                                        default:
8293                                            *alreadygotten = 1;
8294                                            return bit_expression_acos;
8295                                    }
8296                            }
8297                            break;
8298                    }
8299                case s_token_l: case s_token_o:
8300                    tex_get_x_token();
8301                    switch (cur_tok) { 
8302                        case i_token_l: case i_token_o:
8303                            tex_get_x_token();
8304                            switch (cur_tok) { 
8305                                case n_token_l: case n_token_o:
8306                                    tex_get_x_token();
8307                                    switch (cur_tok) { 
8308                                        case h_token_l: case h_token_o:
8309                                            return bit_expression_asinh;
8310                                        default:
8311                                            *alreadygotten = 1;
8312                                            return bit_expression_asin;
8313                                    }
8314                            }
8315                            break;
8316                    }
8317                case t_token_l: case t_token_o:
8318                    tex_get_x_token();
8319                    switch (cur_tok) { 
8320                        case a_token_l: case a_token_o:
8321                            tex_get_x_token();
8322                            switch (cur_tok) { 
8323                                case n_token_l: case n_token_o:
8324                                    tex_get_x_token();
8325                                    switch (cur_tok) { 
8326                                        case h_token_l: case h_token_o:
8327                                            return bit_expression_atanh;
8328                                        default:
8329                                            *alreadygotten = 1;
8330                                            return bit_expression_atan;
8331                                    }
8332                            }
8333                            break;
8334                    }
8335            }
8336            break;
8337        case c_token_l: case c_token_o:
8338            tex_get_x_token();
8339            switch (cur_tok) { 
8340                case e_token_l: case e_token_o:
8341                    tex_get_x_token();
8342                    switch (cur_tok) { 
8343                        case e_token_l: case e_token_o:
8344                            tex_get_x_token();
8345                            switch (cur_tok) { 
8346                                case i_token_l: case i_token_o:
8347                                    tex_get_x_token();
8348                                    switch (cur_tok) { 
8349                                        case l_token_l: case l_token_o:
8350                                            return bit_expression_ceil;
8351                                    }
8352                                    break;
8353                            }
8354                            break;
8355                    }
8356                    break;
8357                case o_token_l: case o_token_o:
8358                    tex_get_x_token();
8359                    switch (cur_tok) { 
8360                        case s_token_l: case s_token_o:
8361                            tex_get_x_token();
8362                            switch (cur_tok) { 
8363                                case h_token_l: case h_token_o:
8364                                    return bit_expression_cosh;
8365                                default:
8366                                    *alreadygotten = 1;
8367                                    return bit_expression_cos;
8368                            }
8369                    }
8370                    break;
8371            }
8372            break;
8373        case e_token_l: case e_token_o:
8374            tex_get_x_token();
8375            switch (cur_tok) { 
8376                case x_token_l: case x_token_o:
8377                    tex_get_x_token();
8378                    switch (cur_tok) { 
8379                        case p_token_l: case p_token_o:
8380                            return bit_expression_exp;
8381                    }
8382                    break;
8383            }
8384            break;
8385        case f_token_l: case f_token_o:
8386            tex_get_x_token();
8387            switch (cur_tok) { 
8388                case l_token_l: case l_token_o:
8389                    tex_get_x_token();
8390                    switch (cur_tok) { 
8391                        case o_token_l: case o_token_o:
8392                            tex_get_x_token();
8393                            switch (cur_tok) { 
8394                                case o_token_l: case o_token_o:
8395                                    tex_get_x_token();
8396                                    switch (cur_tok) { 
8397                                        case r_token_l: case r_token_o:
8398                                            return bit_expression_floor;
8399                                    }
8400                                    break;
8401                            }
8402                            break;
8403                    }
8404                    break;
8405            }
8406            break;
8407        case l_token_l: case l_token_o:
8408            tex_get_x_token();
8409            switch (cur_tok) { 
8410                case n_token_l: case n_token_o:
8411                    return bit_expression_ln; 
8412                case o_token_l: case o_token_o:
8413                    tex_get_x_token();
8414                    switch (cur_tok) { 
8415                        case g_token_l: case g_token_o:
8416                            return bit_expression_log;
8417                    }
8418                    break;
8419            }
8420            break;
8421        case r_token_l: case r_token_o:
8422            tex_get_x_token();
8423            switch (cur_tok) { 
8424                case o_token_l: case o_token_o:
8425                    tex_get_x_token();
8426                    switch (cur_tok) { 
8427                        case u_token_l: case u_token_o:
8428                            tex_get_x_token();
8429                            switch (cur_tok) { 
8430                                case n_token_l: case n_token_o:
8431                                    tex_get_x_token();
8432                                    switch (cur_tok) { 
8433                                        case d_token_l: case d_token_o:
8434                                            return bit_expression_round;
8435                                    }
8436                                    break;
8437                            }
8438                            break;
8439                    }
8440                    break;
8441            }
8442            break;
8443        case s_token_l: case s_token_o:
8444            tex_get_x_token();
8445            switch (cur_tok) { 
8446                case i_token_l: case i_token_o:
8447                    tex_get_x_token();
8448                    switch (cur_tok) { 
8449                        case n_token_l: case n_token_o:
8450                            tex_get_x_token();
8451                            switch (cur_tok) { 
8452                                case h_token_l: case h_token_o:
8453                                    return bit_expression_sinh;
8454                                default:
8455                                    *alreadygotten = 1;
8456                                    return bit_expression_sin;
8457                            }
8458                    }
8459                    break;
8460                case q_token_l: case q_token_o:
8461                    tex_get_x_token();
8462                    switch (cur_tok) { 
8463                        case q_token_l: case q_token_o:
8464                            tex_get_x_token();
8465                            switch (cur_tok) { 
8466                                case r_token_l: case r_token_o:
8467                                    tex_get_x_token();
8468                                    switch (cur_tok) { 
8469                                        case t_token_l: case t_token_o:
8470                                            return bit_expression_sqrt;
8471                                    }
8472                                    break;
8473                            }
8474                            break;
8475                    }
8476                    break;
8477            }
8478            break;
8479        case t_token_l: case t_token_o:
8480            tex_get_x_token();
8481            switch (cur_tok) { 
8482                case a_token_l: case a_token_o:
8483                    tex_get_x_token();
8484                    switch (cur_tok) { 
8485                        case n_token_l: case n_token_o:
8486                            tex_get_x_token();
8487                            switch (cur_tok) { 
8488                                case h_token_l: case h_token_o:
8489                                    return bit_expression_tanh;
8490                                default:
8491                                    *alreadygotten = 1;
8492                                    return bit_expression_tan;
8493                            }
8494                    }
8495                    break;
8496            }
8497            break;
8498    }
8499    tex_aux_show_keyword_error("function");
8500    return expression_none;
8501
8502}
8503
8504static double tex_aux_function_argument(stack_info *s)
8505{
8506    switch (node_subtype(s->tail)) {
8507        case bit_expression_float:
8508        case bit_expression_dimension:
8509            return (double) rpn_expression_entry(s->tail) / 65536.0;
8510        default: 
8511            return (double) rpn_expression_entry(s->tail);
8512    }
8513}
8514
8515static long long tex_aux_function_result(stack_info *s, double v)
8516{
8517    switch (node_subtype(s->tail)) {
8518        case bit_expression_float:
8519        case bit_expression_dimension:
8520            break;
8521        default: 
8522            node_subtype(s->tail) = bit_expression_float;
8523            break;
8524    }
8525    return longlonground(v * 65536);
8526}
8527
8528static void tex_aux_scan_expression(int level, int braced)
8529{
8530    stack_info operators = tex_aux_new_stack();
8531    stack_info reverse = tex_aux_new_stack();
8532    stack_info stack = tex_aux_new_stack();
8533    halfword operation = bit_expression_none;
8534    int alreadygotten = 0;
8535    int trace = tracing_expressions_par;
8536    halfword lastoperation = bit_expression_none;
8537    int negate = 0; 
8538    int nothing = 0; 
8539    int positive = 0; 
8540    int negative = 0; 
8541    int initial = 1;
8542    int depth = 0;
8543    lmt_scanner_state.expression_depth++;
8544    if (lmt_scanner_state.expression_depth > max_expression_depth) {
8545        tex_fatal_error("\\*expression can only be nested " LMT_TOSTRING(max_expression_depth) " deep");
8546    }
8547    while (1) {
8548      HERE:
8549        if (alreadygotten) {
8550            alreadygotten = 0;
8551        } else {
8552            tex_get_x_token();
8553        }
8554        operation = bit_expression_none;
8555        switch (cur_cmd) {
8556            case relax_cmd:
8557                goto COLLECTED;
8558            case left_brace_cmd:
8559                if (! braced) {
8560                    braced = 1;
8561                    continue;
8562                } else {
8563                    ++braced;
8564                    tex_push_stack_entry(&operators, bit_expression_open_brace); 
8565                    goto PUSH;
8566                }
8567            case right_brace_cmd:
8568                --braced;
8569                if (! braced) {
8570                    goto COLLECTED;
8571                } else {
8572                    while (operators.tail && rpn_expression_entry(operators.tail) != bit_expression_open_brace) {
8573                        tex_move_stack_entry(&reverse, &operators);
8574                    }
8575                    if (! operators.tail) { 
8576                        goto BRACEERROR;
8577                    }
8578                    goto POP;
8579                }
8580            case spacer_cmd:
8581            case end_paragraph_cmd:
8582                continue;
8583            default:
8584                switch (cur_tok) {
8585                    case left_parent_token:
8586                        tex_push_stack_entry(&operators, bit_expression_open_parent); 
8587                      PUSH:
8588                        ++depth;
8589                        if (depth > max_sub_expression_depth) {
8590                            tex_fatal_error("sub expressions can only be nested " LMT_TOSTRING(max_sub_expression_depth) " deep");
8591                        }
8592                        rpn_expression_negate(operators.tail) = (singleword) (negate | (nothing << 1) | (positive << 2) | (negative << 3));
8593                        negate = 0;
8594                        nothing = 0;
8595                        positive = 0;
8596                        negative = 0;
8597                        lastoperation = bit_expression_none;
8598                        continue;
8599                    case right_parent_token:
8600                        while (operators.tail && rpn_expression_entry(operators.tail) != bit_expression_open_parent) {
8601                            tex_move_stack_entry(&reverse, &operators);
8602                        }
8603                        if (! operators.tail) { 
8604                            goto PARENTERROR;
8605                        }
8606                      POP:
8607                        --depth; /* can't become negative */
8608                        if (rpn_expression_negate(operators.tail) & 0x01) { /* (negate << 0) */ 
8609                            tex_push_stack_entry(&reverse, bit_expression_minus);
8610                        }
8611                        if (rpn_expression_negate(operators.tail) & 0x02) { /* (nothing << 1) */
8612                            tex_push_stack_entry(&reverse, bit_expression_flip);
8613                        }
8614                        if (rpn_expression_negate(operators.tail) & 0x04) { /* (positive < 2) */
8615                            tex_push_stack_entry(&reverse, bit_expression_positive);
8616                        }
8617                        if (rpn_expression_negate(operators.tail) & 0x08) { /* (negative << 3) */
8618                            tex_push_stack_entry(&reverse, bit_expression_negative);
8619                        }
8620                        tex_pop_stack_entry(&operators); /* bit_expression_open */
8621                        continue;
8622                    case plus_token:
8623                        if (lastoperation !=  bit_expression_none || initial) {
8624                            operation = bit_expression_none;
8625                            goto HERE;
8626                        } else { 
8627                            operation = bit_expression_add;
8628                            break;
8629                        }
8630                    case minus_token:
8631                        if (lastoperation !=  bit_expression_none || initial) {
8632                            operation = bit_expression_none;
8633                            negate = ! negate;
8634                            goto HERE;
8635                        } else { 
8636                            operation = bit_expression_subtract;
8637                            break;
8638                        }
8639                    /* */
8640                    case asterisk_token  : operation = bit_expression_multiply; break;
8641                    case slash_token     : operation = bit_expression_divide  ; break;
8642                    case colon_token     : operation = bit_expression_div     ; break;
8643                    case percentage_token:
8644                    case semi_colon_token: operation = bit_expression_mod     ; break;
8645                    /* */
8646                 // case underscore_token_l: case underscore_token_o: case underscore_token_s:
8647                    case at_sign_token_l: case at_sign_token_o:
8648                        if (level == dimension_val_level) {
8649                            operation = tex_scan_aux_function(&alreadygotten); 
8650                        } else { 
8651                            goto UNEXPECTED;
8652                        }
8653                        break;
8654                    /* */
8655                    case ampersand_token_l: case ampersand_token_o: case ampersand_token_t:
8656                        tex_get_x_token();
8657                        switch (cur_tok) {
8658                            case ampersand_token_l: case ampersand_token_o: case ampersand_token_t:
8659                                operation = bit_expression_and;
8660                                goto OKAY;
8661                        }
8662                        operation = bit_expression_band;
8663                        alreadygotten = 1;
8664                        break;
8665                    /* */
8666                    case not_equal_token      : operation = bit_expression_unequal  ; break;
8667                    case less_or_equal_token  : operation = bit_expression_lessequal; break;
8668                    case more_or_equal_token  : operation = bit_expression_moreequal; break;
8669                    case conditional_and_token: operation = bit_expression_cand     ; break;
8670                    case conditional_or_token : operation = bit_expression_cor      ; break;
8671                    /* */
8672                    case circumflex_token_l: case circumflex_token_o: case circumflex_token_s:
8673                        operation = bit_expression_bxor;
8674                        break;
8675                    case bar_token_l: case bar_token_o:
8676                        tex_get_x_token();
8677                        switch (cur_tok) {
8678                            case bar_token_l: case bar_token_o:
8679                                operation = bit_expression_or;
8680                                goto OKAY;
8681                        }
8682                        operation = bit_expression_bor;
8683                        alreadygotten = 1;
8684                        break;
8685                    case less_token:
8686                        tex_get_x_token();
8687                        switch (cur_tok) {
8688                            case less_token : operation = bit_expression_bleft    ; goto OKAY;
8689                            case equal_token: operation = bit_expression_lessequal; goto OKAY;
8690                            case more_token : operation = bit_expression_unequal  ; goto OKAY;
8691                        }
8692                        operation = bit_expression_less;
8693                        alreadygotten = 1;
8694                        break;
8695                    case more_token:
8696                        tex_get_x_token();
8697                        switch (cur_tok) {
8698                            case more_token : operation = bit_expression_bright   ; goto OKAY;
8699                            case equal_token: operation = bit_expression_moreequal; goto OKAY;
8700                        }
8701                        operation = bit_expression_more;
8702                        alreadygotten = 1;
8703                        break;
8704                    case equal_token:
8705                        tex_get_x_token();
8706                        switch (cur_tok) {
8707                            case equal_token:
8708                                break;
8709                            default:
8710                                alreadygotten = 1;
8711                                break;
8712                        }
8713                        operation = bit_expression_equal;
8714                        break;
8715                    /* */
8716                    case plus_minus_token: positive = 1; operation = bit_expression_none; goto HERE;
8717                    case minus_plus_token: negative = 1; operation = bit_expression_none; goto HERE;
8718                    /* */
8719                    case tilde_token_l: case tilde_token_o:
8720                        tex_get_x_token();
8721                        switch (cur_tok) {
8722                            case equal_token:
8723                                operation = bit_expression_unequal;
8724                                goto OKAY;
8725                        }
8726                        operation = bit_expression_bnot;
8727                        alreadygotten = 1;
8728                        break;
8729                    case exclamation_token_l: case exclamation_token_o:
8730                        tex_get_x_token();
8731                        switch (cur_tok) {
8732                            case equal_token:
8733                                operation = bit_expression_unequal;
8734                                goto OKAY;
8735                        }
8736                        operation = bit_expression_not; /* better bitwise ~ */
8737                        alreadygotten = 1;
8738                        break;
8739                    case m_token_l: case m_token_o:
8740                        tex_get_x_token();
8741                        switch (cur_tok) {
8742                            case o_token_l: case o_token_o:
8743                               tex_get_x_token();
8744                               switch (cur_tok) {
8745                                   case d_token_l: case d_token_o:
8746                                       operation = bit_expression_mod;
8747                                       goto OKAY;
8748                               }
8749                        }
8750                        goto UNEXPECTED;
8751                    case d_token_l: case d_token_o:
8752                        tex_get_x_token();
8753                        switch (cur_tok) {
8754                            case i_token_l: case i_token_o:
8755                               tex_get_x_token();
8756                               switch (cur_tok) {
8757                                   case v_token_l: case v_token_o:
8758                                       operation = bit_expression_div;
8759                                       goto OKAY;
8760                               }
8761                        }
8762                        goto UNEXPECTED;
8763                    case n_token_l: case n_token_o:
8764                        tex_get_x_token();
8765                        switch (cur_tok) {
8766                            case o_token_l: case o_token_o:
8767                               tex_get_x_token();
8768                               switch (cur_tok) {
8769                                    case t_token_l: case t_token_o:
8770                                       if (lastoperation != bit_expression_none || initial) {
8771                                           operation = bit_expression_none;
8772                                           nothing = ! nothing;
8773                                           goto HERE;
8774                                       } else { 
8775                                           operation = bit_expression_not;
8776                                           goto OKAY;
8777                                       }
8778                               }
8779                            case p_token_l: case p_token_o:
8780                               tex_get_x_token();
8781                               switch (cur_tok) {
8782                                    case m_token_l: case m_token_o:
8783                                       if (lastoperation != bit_expression_none || initial) {
8784                                           operation = bit_expression_none;
8785                                           positive = 1;
8786                                           goto HERE;
8787                                       } else { 
8788                                           operation = bit_expression_pm;
8789                                           goto OKAY;
8790                                       }
8791                               }
8792                            case m_token_l: case m_token_o:
8793                               tex_get_x_token();
8794                               switch (cur_tok) {
8795                                    case p_token_l: case p_token_o:
8796                                       if (lastoperation !=  bit_expression_none || initial) {
8797                                           operation = bit_expression_none;
8798                                           negative = 1;
8799                                           goto HERE;
8800                                       } else { 
8801                                           operation = bit_expression_mp;
8802                                           goto OKAY;
8803                                       }
8804                               }
8805                        }
8806                        goto UNEXPECTED;
8807                    case a_token_l: case a_token_o:
8808                        tex_get_x_token();
8809                        switch (cur_tok) { 
8810                            case n_token_l: case n_token_o:
8811                                tex_get_x_token();
8812                                switch (cur_tok) { 
8813                                    case d_token_l: case d_token_o:
8814                                        operation = bit_expression_and;
8815                                        goto OKAY;
8816                                }
8817                                break;
8818                        }
8819                        goto UNEXPECTED;
8820                    case b_token_l: case b_token_o:
8821                        tex_get_x_token();
8822                        switch (cur_tok) {
8823                            case a_token_l: case a_token_o:
8824                                tex_get_x_token();
8825                                switch (cur_tok) { 
8826                                    case n_token_l: case n_token_o:
8827                                        tex_get_x_token();
8828                                        switch (cur_tok) { 
8829                                            case d_token_l: case d_token_o:
8830                                                operation = bit_expression_band;
8831                                                goto OKAY;
8832                                        }
8833                                }
8834                                break;
8835                            case o_token_l: case o_token_o:
8836                                tex_get_x_token();
8837                                switch (cur_tok) {
8838                                    case r_token_l: case r_token_o:
8839                                        operation = bit_expression_bor;
8840                                        goto OKAY;
8841                                }
8842                                break;
8843                            case x_token_l: case x_token_o:
8844                                tex_get_x_token();
8845                                switch (cur_tok) {
8846                                    case o_token_l: case o_token_o:
8847                                        tex_get_x_token();
8848                                        switch (cur_tok) {
8849                                            case r_token_l: case r_token_o:
8850                                                operation = bit_expression_bxor;
8851                                                goto OKAY;
8852                                        }
8853                                }
8854                                break;
8855                            case s_token_l: case s_token_o:
8856                                tex_get_x_token();
8857                                switch (cur_tok) {
8858                                    case e_token_l: case e_token_o:
8859                                        tex_get_x_token();
8860                                        switch (cur_tok) {
8861                                            case t_token_l: case t_token_o:
8862                                                operation = bit_expression_bset;
8863                                                goto OKAY;
8864                                        }
8865                                }
8866                                break;
8867                            case u_token_l: case u_token_o:
8868                                tex_get_x_token();
8869                                switch (cur_tok) {
8870                                    case n_token_l: case n_token_o:
8871                                        tex_get_x_token();
8872                                        switch (cur_tok) {
8873                                            case s_token_l: case s_token_o:
8874                                                tex_get_x_token();
8875                                                switch (cur_tok) {
8876                                                    case e_token_l: case e_token_o:
8877                                                        tex_get_x_token();
8878                                                        switch (cur_tok) {
8879                                                            case t_token_l: case t_token_o:
8880                                                                operation = bit_expression_bset;
8881                                                                goto OKAY;
8882                                                        }
8883                                                }
8884                                        }
8885                                }
8886                                break;
8887                        }
8888                        goto UNEXPECTED;
8889                    case c_token_l: case c_token_o:
8890                        tex_get_x_token();
8891                        switch (cur_tok) {
8892                            case a_token_l: case a_token_o:
8893                                tex_get_x_token();
8894                                switch (cur_tok) { 
8895                                    case n_token_l: case n_token_o:
8896                                        tex_get_x_token();
8897                                        switch (cur_tok) { 
8898                                            case d_token_l: case d_token_o:
8899                                                operation = bit_expression_cand;
8900                                                goto OKAY;
8901                                        }
8902                                }
8903                                break;
8904                            case o_token_l: case o_token_o:
8905                                tex_get_x_token();
8906                                switch (cur_tok) {
8907                                    case r_token_l: case r_token_o:
8908                                        operation = bit_expression_cor;
8909                                        goto OKAY;
8910                                }
8911                                break;
8912                        }
8913                        goto UNEXPECTED;
8914                    case o_token_l: case o_token_o:
8915                        tex_get_x_token();
8916                        switch (cur_tok) {
8917                            case r_token_l: case r_token_o:
8918                                operation = bit_expression_or;
8919                                goto OKAY;
8920                        }
8921                        goto UNEXPECTED;
8922                    case v_token_l: case v_token_o:
8923                        operation = bit_expression_bor;
8924                        break;
8925                    default:
8926                        goto NUMBER;
8927                }
8928              OKAY:
8929                while (operators.tail && bit_operator_precedence[rpn_expression_entry(operators.tail)] >= bit_operator_precedence[operation]) {
8930                 // tex_push_stack_entry(&reverse, tex_pop_stack_entry(&operators));
8931                    tex_move_stack_entry(&reverse, &operators);
8932                }
8933                tex_push_stack_entry(&operators, operation);
8934                lastoperation = operation;
8935                break;
8936              NUMBER:
8937                lastoperation = bit_expression_none;
8938                initial = 0;
8939                /*tex These use |cur_tok|: */
8940                {
8941                    int has_fraction = 0;
8942                    int has_unit = 0;
8943                    operation = level == dimension_val_level ? tex_scan_bit_dimension(&has_fraction, &has_unit) : tex_scan_bit_integer(NULL);
8944                    if (negate) { 
8945                        operation = - operation;
8946                        negate = 0;
8947                    }
8948                    if (nothing) { 
8949                        operation = operation ? 0 : 1;
8950                        nothing = 0;
8951                    }
8952                    if (positive) {
8953                        if (operation < 0) {
8954                            operation = - operation;
8955                        }    
8956                        positive = 0;
8957                    }
8958                    if (negative) {
8959                        if (operation > 0) {
8960                            operation = - operation;
8961                        }    
8962                        negative = 0;
8963                    }
8964                    tex_push_stack_entry(&reverse, operation * factor);
8965                    if (level == dimension_val_level && has_unit) {
8966                        node_subtype(reverse.tail) = bit_expression_dimension;
8967                    } else if (has_fraction) {
8968                        node_subtype(reverse.tail) = bit_expression_float; /* maybe posit */
8969                    } else {
8970                        node_subtype(reverse.tail) = bit_expression_number;
8971                    }
8972                    continue;
8973                }
8974        }
8975    }
8976  COLLECTED:
8977    while (operators.tail) {
8978        tex_move_stack_entry(&reverse, &operators);
8979    }
8980    /*tex This is the reference: */
8981    /*
8982    {
8983        halfword current = reverse.head;
8984        while (current) {
8985            if (node_subtype(current) == bit_expression_number) {
8986                tex_push_stack_entry(&stack, rpn_expression_entry(current));
8987            } else {
8988                halfword token = rpn_expression_entry(current);
8989                long long v;
8990                if (token == bit_expression_not) {
8991                    v = ~ (long long) tex_pop_stack_entry(&stack);
8992                } else {
8993                    long long b = (long long) tex_pop_stack_entry(&stack);
8994                    long long a = (long long) tex_pop_stack_entry(&stack);
8995                    switch (token) {
8996                       // calculations, see below
8997                    }
8998                }
8999                // checks, see below
9000                tex_push_stack_entry(&stack, (halfword) v);
9001            }
9002            current = node_next(current);
9003        }
9004    }
9005    */
9006    if (trace == 1) {
9007        tex_aux_trace_expression(reverse, level, 0, 0);
9008    }
9009    {
9010        halfword current = reverse.head;
9011        int step = 0;
9012        while (current) {
9013            halfword next = node_next(current);
9014            halfword subtype = node_subtype(current);
9015            if (trace > 1) {
9016                step = step + 1;
9017                tex_aux_trace_expression(reverse, level, step, 0);
9018                tex_aux_trace_expression(stack, level, step, 1);
9019            }
9020            switch (subtype) {
9021                case bit_expression_number:
9022                case bit_expression_float:
9023                case bit_expression_dimension:
9024                    tex_take_stack_entry(&stack, &reverse, current);
9025                    break;
9026                default:
9027                    {
9028                        halfword token = (halfword) rpn_expression_entry(current);
9029                        long long v = 0;
9030                        switch (token) {
9031                            /* */
9032                            case bit_expression_bnot : v = stack.tail ? ~ rpn_expression_entry(stack.tail) : 0; break;
9033                            case bit_expression_plus : v = stack.tail ?   rpn_expression_entry(stack.tail) : 0; break;
9034                            case bit_expression_minus: v = stack.tail ? - rpn_expression_entry(stack.tail) : 0; break;
9035                            /* */
9036                            case bit_expression_not : v = stack.tail ? (rpn_expression_entry(stack.tail) ? 0 : 1) : 0; break;
9037                            case bit_expression_flip: v = stack.tail ? (rpn_expression_entry(stack.tail) ? 0 : 1) : 1; break;
9038                            /* */
9039                            case bit_expression_positive: v = stack.tail ? rpn_expression_entry(stack.tail) : 0; if (v < 0) { v = - v; } break;
9040                            case bit_expression_negative: v = stack.tail ? rpn_expression_entry(stack.tail) : 0; if (v > 0) { v = - v; } break;
9041                            /* */
9042                            case bit_expression_sin  : { v = stack.tail ? tex_aux_function_result(&stack, sin  (tex_aux_function_argument(&stack))) : 0; } break;
9043                            case bit_expression_cos  : { v = stack.tail ? tex_aux_function_result(&stack, cos  (tex_aux_function_argument(&stack))) : 0; } break;
9044                            case bit_expression_tan  : { v = stack.tail ? tex_aux_function_result(&stack, tan  (tex_aux_function_argument(&stack))) : 0; } break; // error 
9045                            case bit_expression_asin : { v = stack.tail ? tex_aux_function_result(&stack, asin (tex_aux_function_argument(&stack))) : 0; } break;
9046                            case bit_expression_acos : { v = stack.tail ? tex_aux_function_result(&stack, acos (tex_aux_function_argument(&stack))) : 0; } break;
9047                            case bit_expression_atan : { v = stack.tail ? tex_aux_function_result(&stack, atan (tex_aux_function_argument(&stack))) : 0; } break; // error  
9048                            case bit_expression_sinh : { v = stack.tail ? tex_aux_function_result(&stack, sinh (tex_aux_function_argument(&stack))) : 0; } break;
9049                            case bit_expression_cosh : { v = stack.tail ? tex_aux_function_result(&stack, cosh (tex_aux_function_argument(&stack))) : 0; } break;
9050                            case bit_expression_tanh : { v = stack.tail ? tex_aux_function_result(&stack, tanh (tex_aux_function_argument(&stack))) : 0; } break; // error 
9051                            case bit_expression_asinh: { v = stack.tail ? tex_aux_function_result(&stack, asinh(tex_aux_function_argument(&stack))) : 0; } break;
9052                            case bit_expression_acosh: { v = stack.tail ? tex_aux_function_result(&stack, acosh(tex_aux_function_argument(&stack))) : 0; } break;
9053                            case bit_expression_atanh: { v = stack.tail ? tex_aux_function_result(&stack, atanh(tex_aux_function_argument(&stack))) : 0; } break; // error 
9054                            case bit_expression_ceil : { v = stack.tail ? tex_aux_function_result(&stack, ceil (tex_aux_function_argument(&stack))) : 0; } break;
9055                            case bit_expression_floor: { v = stack.tail ? tex_aux_function_result(&stack, floor(tex_aux_function_argument(&stack))) : 0; } break;
9056                            case bit_expression_round: { v = stack.tail ? tex_aux_function_result(&stack, round(tex_aux_function_argument(&stack))) : 0; } break;
9057                            case bit_expression_abs  : { v = stack.tail ? tex_aux_function_result(&stack, abs  (tex_aux_function_argument(&stack))) : 0; } break;
9058                            case bit_expression_sqrt : { v = stack.tail ? tex_aux_function_result(&stack, sqrt (tex_aux_function_argument(&stack))) : 0; } break;
9059                            case bit_expression_log  : { v = stack.tail ? tex_aux_function_result(&stack, log10(tex_aux_function_argument(&stack))) : 0; } break;
9060                            case bit_expression_ln   : { v = stack.tail ? tex_aux_function_result(&stack, log  (tex_aux_function_argument(&stack))) : 0; } break;
9061                            case bit_expression_exp  : { v = stack.tail ? tex_aux_function_result(&stack, exp  (tex_aux_function_argument(&stack))) : 0; } break;
9062                            /* */
9063                            default: 
9064                                {
9065                                    quarterword sa, sb;
9066                                    long long va, vb;
9067                                    sb = node_subtype(stack.tail);
9068                                    vb = tex_pop_stack_entry(&stack);
9069                                    if (stack.tail) {
9070                                        sa = node_subtype(stack.tail);
9071                                        va = rpn_expression_entry(stack.tail);
9072                                        /* */
9073                                        if (sa == sb) {
9074                                            /* okay */
9075                                        } else if (sa == bit_expression_number) {
9076                                            va = va * 65536;
9077                                            sa = bit_expression_float;
9078                                            node_subtype(stack.tail) = sb; /* float or dimension */
9079                                        } else if (sb == bit_expression_number) {
9080                                            vb = vb * 65536;
9081                                            sb = bit_expression_float;
9082                                        } 
9083                                        /* */ 
9084                                    } else {
9085                                        sa = bit_expression_number;
9086                                        va = 0; 
9087                                    }
9088                                    switch (token) {
9089                                        case bit_expression_bor      : v = va |  vb; break;
9090                                        case bit_expression_band     : v = va &  vb; break;
9091                                        case bit_expression_bxor     : v = va ^  vb; break;
9092                                        /* */
9093                                        case bit_expression_bset     : v = va |   ((long long) 1 << (vb - 1)); break;
9094                                        case bit_expression_bunset   : v = va & ~ ((long long) 1 << (vb - 1)); break;
9095                                        /* */
9096                                        case bit_expression_bleft    : v = va << vb; break;
9097                                        case bit_expression_bright   : v = va >> vb; break;
9098                                        case bit_expression_less     : v = va <  vb; break;
9099                                        case bit_expression_lessequal: v = va <= vb; break;
9100                                        case bit_expression_equal    : v = va == vb; break;
9101                                        case bit_expression_moreequal: v = va >= vb; break;
9102                                        case bit_expression_more     : v = va >  vb; break;
9103                                        case bit_expression_unequal  : v = va != vb; break;
9104                                        /* */
9105                                        case bit_expression_add      : v = va +  vb; break;
9106                                        case bit_expression_subtract : v = va -  vb; break;
9107                                        /* */
9108                                        case bit_expression_multiply:
9109                                            /* needs checking */
9110                                            {
9111                                                double d = (double) va * (double) vb;
9112                                                if (sa == bit_expression_float) {
9113                                                    d = d / (65536 * factor);
9114                                                } else if (sb == bit_expression_float) {
9115                                                    d = d / (65536 * factor);
9116                                                } else {
9117                                                    d = d / factor;
9118                                                }
9119                                                if (sa == bit_expression_dimension || sb == bit_expression_dimension) {
9120                                                    node_subtype(stack.tail) = bit_expression_dimension;
9121                                                }
9122                                                v = longlonground(d);
9123                                            }
9124                                            break;
9125                                        case bit_expression_divide:
9126                                            /* needs checking */
9127                                            if (vb) {
9128                                                double d = (double) va / (double) vb;
9129                                                if (sa == bit_expression_float) {
9130                                                // d = d / (65536 * factor);
9131                                                   d = d * (65536 * factor);
9132                                                } else if (sb == bit_expression_float) {
9133                                                 // d = d / (65536 * factor);
9134                                                    d = d * (65536 * factor);
9135                                                } else {
9136                                                    d = d * factor;
9137                                                }
9138                                                if (sa == bit_expression_dimension || sb == bit_expression_dimension) {
9139                                                    node_subtype(stack.tail) = bit_expression_dimension;
9140                                                }
9141                                                v = longlonground(d);
9142                                            } else {
9143                                                goto ZERO;
9144                                            }
9145                                            break;
9146                                        /* */
9147                                        case bit_expression_mod : v =  va % vb; break;
9148                                        case bit_expression_div : v =  va / vb; break;
9149                                        /* */
9150                                        case bit_expression_or  : v = (va || vb) ? 1 : 0 ; break;
9151                                        case bit_expression_and : v = (va && vb) ? 1 : 0 ; break;
9152                                        /* */
9153                                        case bit_expression_cor : v = va         ? va : (vb ? vb : 0); break;
9154                                        case bit_expression_cand: v = (va && vb) ? vb            : 0 ; break;
9155                                        /* */
9156                                        default:
9157                                            v = 0;
9158                                            break;
9159                                    }
9160                            }
9161                        }
9162                        /* todo */
9163                        if (node_subtype(stack.tail) == bit_expression_number) {
9164                            if (v < min_integer) {
9165                                v = min_integer;
9166                            } else if (v > max_integer) {
9167                                v = max_integer;
9168                            }
9169                        } else {
9170                            if (v < min_longinteger) {
9171                                v = min_longinteger;
9172                            } else if (v > max_longinteger) {
9173                                v = max_longinteger;
9174                            }
9175                        }
9176                        /* */
9177                        rpn_expression_entry(stack.tail) = v;
9178                        break;
9179                    }
9180            }
9181            current = next;
9182        }
9183    }
9184    goto DONE;
9185  BRACEERROR:
9186    tex_aux_scan_brace_error();
9187    goto DONE;
9188  PARENTERROR:
9189    tex_aux_scan_parent_error();
9190    goto DONE;
9191  ZERO:
9192    tex_aux_scan_zero_divide_error();
9193    goto DONE;
9194  UNEXPECTED:
9195    tex_handle_error(
9196        back_error_type,
9197        "Premature end of expression",
9198        "I was expecting to see an integer or bitwise operator. Didn't."
9199    );
9200  DONE:
9201    lmt_scanner_state.expression_depth--;
9202    cur_val = scaledround(((double) rpn_expression_entry(stack.tail)) / factor);
9203    cur_val_level = level;
9204    tex_aux_dispose_stack(&stack);
9205    tex_aux_dispose_stack(&reverse);
9206    tex_aux_dispose_stack(&operators);
9207}
9208